-- 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