-- local_agent.lua -- Run this on a computer adjacent to a reactor or brine tower. -- Listens on a modem (wired/wireless/ender) for simple commands and performs local peripheral or redstone control. local config = { auth_token = nil, -- optional token required as first word of incoming messages modem_side = nil, -- optional side/name to open rednet on; nil = auto-detect log_file = "agent_log.txt", } local function log(msg) local line = string.format("[%s] %s", os.date("%Y-%m-%d %H:%M:%S"), tostring(msg)) print(line) local ok, f = pcall(fs.open, config.log_file, "a") if ok and f then f.writeLine(line); f.close() end end -- Return the peripheral name and type for the first modem-like peripheral found local function find_modem_peripheral() for _,n in ipairs(peripheral.getNames()) do local ok, t = pcall(peripheral.getType, n) if ok and t then local tl = tostring(t):lower() if tl == "modem" or tl == "wireless_modem" or tl:find("ender") or tl:find("ender_modem") then return n, t end end end return nil, nil end local function scan_peripherals() local list = {} for _,n in ipairs(peripheral.getNames()) do table.insert(list, n) end return list end local function find_target(name) -- exact name first for _,n in ipairs(peripheral.getNames()) do if n == name then return n end end -- substring match for _,n in ipairs(peripheral.getNames()) do if n:lower():find(name:lower(),1,true) then return n end end return nil end local function try_call(side, method, ...) local ok, res = pcall(peripheral.call, side, method, ...) return ok, res end -- open rednet local modem = config.modem_side if not modem then local mside, mtype = find_modem_peripheral() if mside then modem = mside log("Detected modem peripheral: '" .. tostring(mside) .. "' (type: " .. tostring(mtype) .. ")") log("You can open rednet manually with: rednet.open(\"" .. tostring(mside) .. "\")") else log("No modem peripheral detected automatically.") log("If you have a wireless modem, attach it to this computer and set config.modem_side or run: rednet.open('')") end else log("Using configured modem side/name: " .. tostring(modem)) log("To open manually: rednet.open(\"" .. tostring(modem) .. "\")") end if modem then local ok, err = pcall(rednet.open, modem) if not ok then log("Failed to open rednet on " .. tostring(modem) .. " -> " .. tostring(err)) log("If automatic open fails, try running: rednet.open('') in the REPL where is the modem name shown by peripheral.getNames()") else log("Rednet opened on " .. tostring(modem)) end else log("Rednet not opened; agent will still run but won't receive network commands.") end log("Agent online") local function handle_message(id, msg) if not id or not msg then return end local raw = tostring(msg) local parts = {} for w in raw:gmatch("%S+") do table.insert(parts, w) end local idx = 1 if config.auth_token then if #parts == 0 or parts[1] ~= config.auth_token then if rednet then rednet.send(id, "AUTH_FAIL") end log("Rejected message from " .. tostring(id) .. " (bad token)") return end idx = 2 end local cmd = parts[idx] and parts[idx]:upper() or "" local target = parts[idx+1] if cmd == "STATUS" then -- reply with list of peripherals local per = table.concat(scan_peripherals(), ",") if rednet then rednet.send(id, "OK PERIPHERALS " .. per) end log("STATUS -> replied to " .. tostring(id)) return end if (cmd == "START" or cmd == "STOP" or cmd == "TOGGLE") and target then local tname = find_target(target) if not tname then if rednet then rednet.send(id, "NOTFOUND") end log("Target not found: " .. tostring(target)) return end local desired if cmd == "START" then desired = true elseif cmd == "STOP" then desired = false end if cmd == "TOGGLE" then local active for _,m in ipairs({"isActive","getActive","isRunning","getPumpState","getValveState"}) do local ok, res = try_call(tname, m) if ok then active = res; break end end if active == nil then if rednet then rednet.send(id, "CANNOT_READ") end log("Cannot read state of " .. tname) return end desired = not active end local set_ok = false local methods = peripheral.getMethods(tname) or {} for _,m in ipairs({"setActive","setRunning","setPump","setValve","activate","deactivate","enable","disable"}) do for _,mm in ipairs(methods) do if mm == m then local ok, res = try_call(tname, m, desired) if ok then set_ok = true; break end end end if set_ok then break end end if not set_ok then for _,side in ipairs({"left","right","front","back","top","bottom"}) do pcall(redstone.setOutput, side, desired) end set_ok = true end if rednet then rednet.send(id, set_ok and "OK" or "FAIL") end log(string.format("%s %s -> %s", cmd, tostring(tname), tostring(set_ok))) return end if rednet then rednet.send(id, "UNKNOWN_CMD") end log("Unknown command: " .. tostring(raw)) end while true do local id, msg = nil, nil if rednet then id, msg = rednet.receive(1) end if id and msg then handle_message(id, msg) end os.sleep(0.1) end