kovakovi2000

fission_controller

Oct 31st, 2025 (edited)
400
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.68 KB | None | 0 0
  1. -- controller.lua
  2. -- shows reactors, lets you start/stop, and has a yellow "RESTART ALL" button
  3. -- clicks work on:
  4. --   - ADVANCED monitor (monitor_touch)
  5. --   - controller PC screen (mouse_click)
  6.  
  7. os.loadAPI("rnc3")
  8.  
  9. local CTRL_PROG  = "fission-controller"
  10. local AGENT_PROG = "fission-agent"
  11.  
  12. local CTRL_ID    = rnc3.getId(CTRL_PROG)
  13. local CTRL_PORT  = rnc3.getPort(CTRL_PROG)
  14. local AGENT_PORT = rnc3.getPort(AGENT_PROG)
  15.  
  16. local DISCOVER_EVERY = 3
  17. local REDRAW_EVERY   = 0.4
  18. local OFFLINE_AFTER  = 10
  19.  
  20. -- logging ---------------------------------------------------
  21. local function log(msg)
  22.   local h = fs.open("debug.log", "a")
  23.   h.writeLine(("[%d] %s"):format(os.epoch("utc"), msg))
  24.   h.close()
  25. end
  26.  
  27. log("=== controller start ===")
  28. log(("CTRL_ID=%s CTRL_PORT=%d AGENT_PORT=%d"):format(CTRL_ID, CTRL_PORT, AGENT_PORT))
  29.  
  30. -- open both ports on all modems
  31. do
  32.   local ms = { peripheral.find("modem") }
  33.   for _, m in ipairs(ms) do
  34.     if not m.isOpen(CTRL_PORT) then m.open(CTRL_PORT) end
  35.     if not m.isOpen(AGENT_PORT) then m.open(AGENT_PORT) end
  36.   end
  37. end
  38.  
  39. -- monitor ---------------------------------------------------
  40. local mon = peripheral.find("monitor")
  41. if not mon then error("no monitor") end
  42.  
  43. -- pick small-ish scale
  44. for _, s in ipairs({1, 0.75, 0.5, 0.25}) do
  45.   mon.setTextScale(s)
  46.   local w,h = mon.getSize()
  47.   if h >= 4 then break end
  48. end
  49.  
  50. mon.setBackgroundColor(colors.black)
  51. mon.setTextColor(colors.white)
  52. mon.clear()
  53.  
  54. -- state -----------------------------------------------------
  55. local reactors = {}   -- [id] = {id=..., telem=..., lastSeen=..., row=...}
  56. local lastDiscover = 0
  57. local lastDraw = 0
  58.  
  59. -- UI coords -------------------------------------------------
  60. local function shortId(id, max)
  61.   max = max or 12
  62.   if #id <= max then return id end
  63.   return id:sub(1, max-3) .. "..."
  64. end
  65.  
  66. -- will update every draw
  67. local restartBtn = {x1=1, x2=1, y=1}
  68.  
  69. local function draw()
  70.   local w,h = mon.getSize()
  71.   mon.setBackgroundColor(colors.black)
  72.   mon.setTextColor(colors.white)
  73.   mon.clear()
  74.  
  75.   -- top bar: center "RESTART ALL" in yellow
  76.   local label = "RESTART ALL"
  77.   local x1 = math.floor((w - #label)/2) + 1
  78.   local x2 = x1 + #label - 1
  79.   restartBtn.x1 = x1
  80.   restartBtn.x2 = x2
  81.   restartBtn.y  = 1
  82.  
  83.   mon.setCursorPos(x1, 1)
  84.   mon.setTextColor(colors.yellow)
  85.   mon.write(label)
  86.  
  87.   -- list reactors
  88.   local list = {}
  89.   for _, r in pairs(reactors) do table.insert(list, r) end
  90.   table.sort(list, function(a,b) return a.id < b.id end)
  91.  
  92.   local row = 2
  93.   local drawn = 0
  94.   for _, r in ipairs(list) do
  95.     if row > h then break end
  96.     local online = (os.clock() - r.lastSeen) < OFFLINE_AFTER
  97.     local t      = r.telem or {}
  98.     local temp   = t.temp or 0
  99.     local burn   = t.burn or 0
  100.  
  101.     -- ✅ running = burn > 0
  102.     local running = (burn and burn > 0)
  103.  
  104.     r.row = row
  105.  
  106.     mon.setCursorPos(1, row)
  107.     if online then mon.setTextColor(colors.white) else mon.setTextColor(colors.gray) end
  108.  
  109.     local btnText = running and "[OFF]" or "[ON]"
  110.  
  111.     local idPart   = shortId(r.id, math.floor(w*0.35))
  112.     local tempPart = (" T:%d"):format(temp)
  113.     local burnPart = (" B:%d"):format(burn)
  114.  
  115.     local text = idPart .. tempPart .. burnPart
  116.     local maxText = w - #btnText
  117.     if #text > maxText then text = text:sub(1, maxText) end
  118.     mon.write(text)
  119.  
  120.     local btnX = w - #btnText + 1
  121.     if btnX < 1 then btnX = 1 end
  122.     mon.setCursorPos(btnX, row)
  123.     if running then mon.setTextColor(colors.red) else mon.setTextColor(colors.lime) end
  124.     mon.write(btnText)
  125.  
  126.     drawn = drawn + 1
  127.     row = row + 1
  128.   end
  129.  
  130.   log(("draw: %d reactors, %d shown, mon=%dx%d"):format(#list, drawn, w, h))
  131. end
  132.  
  133. local function reactorAtY(y)
  134.   for _, r in pairs(reactors) do
  135.     if r.row == y then return r end
  136.   end
  137.   return nil
  138. end
  139.  
  140. local function sendDiscover()
  141.   rnc3.send(CTRL_PROG, "*", {type="discover", disableSoftScram=true})
  142.   log("sent discover")
  143. end
  144.  
  145. local function sendControllerHB()
  146.   for id,_ in pairs(reactors) do
  147.     rnc3.send(CTRL_PROG, id, {type="controller-hb", disableSoftScram=true})
  148.   end
  149. end
  150.  
  151. local function handlePacket(pkt, viaPort)
  152.   local b = pkt.body or {}
  153.   local t = b.type
  154.  
  155.   if t == "reactor-hello" or t == "hb" then
  156.     local rid = pkt.from
  157.     if not reactors[rid] then
  158.       reactors[rid] = { id = rid }
  159.       log("new reactor: " .. rid)
  160.     end
  161.     reactors[rid].lastSeen = os.clock()
  162.     reactors[rid].telem    = b.telem or reactors[rid].telem
  163.  
  164.     -- keep them alive
  165.     rnc3.send(CTRL_PROG, rid, {type="controller-hb", disableSoftScram=true})
  166.   end
  167. end
  168.  
  169. -- action ----------------------------------------------------
  170. local function toggleReactor(r)
  171.   local t = r.telem or {}
  172.   local burn = t.burn or 0
  173.   local running = burn > 0
  174.   if running then
  175.     log("send OFF -> " .. r.id)
  176.     rnc3.send(CTRL_PROG, r.id, {type="cmd", cmd="off"})
  177.   else
  178.     log("send ON -> " .. r.id)
  179.     rnc3.send(CTRL_PROG, r.id, {type="cmd", cmd="on"})
  180.   end
  181. end
  182.  
  183. local function restartAll()
  184.   log("RESTART ALL pressed")
  185.   for id,_ in pairs(reactors) do
  186.     rnc3.send(CTRL_PROG, id, {type="cmd", cmd="restart"})
  187.   end
  188. end
  189.  
  190. -- main loop ------------------------------------------------
  191. while true do
  192.   local now = os.clock()
  193.  
  194.   if now - lastDiscover >= DISCOVER_EVERY then
  195.     sendDiscover()
  196.     lastDiscover = now
  197.   end
  198.  
  199.   if now - lastDraw >= REDRAW_EVERY then
  200.     draw()
  201.     lastDraw = now
  202.   end
  203.  
  204.   local ev,p1,p2,p3,p4,p5 = os.pullEvent()
  205.  
  206.   if ev == "modem_message" then
  207.     local channel = p2
  208.     local data    = p4
  209.     if channel == CTRL_PORT or channel == AGENT_PORT then
  210.       local ok,pkt = pcall(textutils.unserialize, data)
  211.       if ok and type(pkt) == "table" then
  212.         if (not pkt.to) or pkt.to == "*" or pkt.to == CTRL_ID then
  213.           handlePacket(pkt, channel)
  214.           lastDraw = 0
  215.         end
  216.       end
  217.     end
  218.  
  219.   elseif ev == "monitor_touch" then
  220.     local side,x,y = p1,p2,p3
  221.     log(("monitor_touch x=%d y=%d"):format(x,y))
  222.     -- top button?
  223.     if y == restartBtn.y and x >= restartBtn.x1 and x <= restartBtn.x2 then
  224.       restartAll()
  225.     else
  226.       local r = reactorAtY(y)
  227.       if r then toggleReactor(r) end
  228.     end
  229.     lastDraw = 0
  230.  
  231.   elseif ev == "mouse_click" then
  232.     -- clicking in controller PC terminal
  233.     local button,x,y = p1,p2,p3
  234.     log(("mouse_click x=%d y=%d"):format(x,y))
  235.     if y == restartBtn.y and x >= restartBtn.x1 and x <= restartBtn.x2 then
  236.       restartAll()
  237.     else
  238.       local r = reactorAtY(y)
  239.       if r then toggleReactor(r) end
  240.     end
  241.     lastDraw = 0
  242.  
  243.   elseif ev == "terminate" then
  244.     log("terminated")
  245.     mon.clear()
  246.     mon.setCursorPos(1,1)
  247.     mon.write("CTRL stopped")
  248.     break
  249.   end
  250.  
  251.   sendControllerHB()
  252. end
  253.  
Advertisement
Add Comment
Please, Sign In to add comment