Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- fission_agent.lua
- -- Mekanism fission agent
- -- HARD: SCRAM >= 1400 C
- -- HARD: do NOT start if temp >= 1000 C
- -- persists desired run/burn in ldb3
- -- comms: rnc3
- -- remote cmd: on/off/setBurn/setSoftScram/restart
- local HAVE_LDB = pcall(os.loadAPI, "ldb3")
- os.loadAPI("rnc3")
- local PROG = "fission-agent"
- local MY_ID = rnc3.getId(PROG)
- print("agent id:", MY_ID)
- -- constants -------------------------------------------------
- local HEARTBEAT_EVERY = 1.0
- local CONTROLLER_TIMEOUT = 5.0
- local HARD_SCRAM_TEMP = 1400
- local SAFE_START_TEMP = 1000
- local SOFT_MIN_COOLANT = 5
- local SOFT_MAX_WASTE = 95
- local SOFT_MAX_HOT = 95
- local DB_NAME = "fission-agent" -- for ldb3
- -- reactor ---------------------------------------------------
- local reactor = peripheral.find("fissionReactorLogicAdapter")
- if not reactor then
- error("fission_agent: no fissionReactorLogicAdapter found")
- end
- -- state -----------------------------------------------------
- local controllerId = nil
- local controllerLastSeen = 0
- local softAutoScram = true
- local lastHeartbeat = 0
- -- this is what we SAVE
- -- desired.run = true/false
- -- desired.burn = number
- local desired = {
- run = false,
- burn = 0,
- }
- -- load persisted desired
- if HAVE_LDB then
- local d = ldb3.get(DB_NAME, "desired")
- if type(d) == "table" then
- desired = d
- end
- end
- local function saveDesired()
- if HAVE_LDB then
- ldb3.set(DB_NAME, "desired", desired)
- end
- end
- -- helpers ---------------------------------------------------
- local function safe(fn, ...)
- if not fn then return nil end
- local ok, r = pcall(fn, ...)
- if ok then return r end
- return nil
- end
- local function scram()
- if reactor.scram then reactor.scram() end
- if reactor.setBurnRate then reactor.setBurnRate(0) end
- end
- local function canStartNow()
- local t = safe(reactor.getTemperature) or 0
- return t < SAFE_START_TEMP
- end
- local function startFromDesired()
- if desired.run and canStartNow() then
- if reactor.activate then reactor.activate() end
- if reactor.setBurnRate then
- local maxB = safe(reactor.getMaxBurnRate) or desired.burn or 0
- local b = desired.burn or 0
- if b > maxB then b = maxB end
- reactor.setBurnRate(b)
- end
- return true
- end
- return false
- end
- local function setBurn(b)
- b = tonumber(b) or 0
- local t = safe(reactor.getTemperature) or 0
- if t >= SAFE_START_TEMP and b > 0 then
- return false, "temp>=1000"
- end
- local maxB = safe(reactor.getMaxBurnRate) or b
- if b > maxB then b = maxB end
- if b < 0 then b = 0 end
- if reactor.setBurnRate then reactor.setBurnRate(b) end
- desired.burn = b
- if b > 0 then
- desired.run = true
- end
- saveDesired()
- return true, b
- end
- -- telemetry: ONLY your methods --------------------------------
- local function readTelemetry()
- local temp = safe(reactor.getTemperature) or 0
- local burn = safe(reactor.getBurnRate) or 0
- local actualBurn = safe(reactor.getActualBurnRate) or burn
- local coolPct = safe(reactor.getCoolantFilledPercentage) or 0
- local hotPct = safe(reactor.getHeatedCoolantFilledPercentage) or 0
- local fuelPct = safe(reactor.getFuelFilledPercentage) or 0
- local wastePct = safe(reactor.getWasteFilledPercentage) or 0
- local heating = safe(reactor.getHeatingRate) or 0
- local rawStatus = safe(reactor.getStatus)
- -- normalize: your getStatus was weird, so we use burn as truth
- local status
- if burn and burn > 0 then
- status = "active"
- else
- status = "stopped"
- end
- -- if rawStatus was a nice string, keep it in case you want to log
- return {
- id = MY_ID,
- temp = temp,
- burn = burn,
- actualBurn = actualBurn,
- coolantPct = coolPct,
- heatedCoolantPct = hotPct,
- fuelPct = fuelPct,
- wastePct = wastePct,
- heatingRate = heating,
- status = status,
- rawStatus = rawStatus,
- softAutoScram = softAutoScram,
- desiredRun = desired.run,
- desiredBurn = desired.burn,
- time = os.epoch("utc"),
- label = os.getComputerLabel(),
- }
- end
- local function isSoftUnsafe(t)
- if not softAutoScram then return false end
- if t.coolantPct and t.coolantPct > 0 and t.coolantPct < SOFT_MIN_COOLANT then return true end
- if t.wastePct and t.wastePct >= SOFT_MAX_WASTE then return true end
- if t.heatedCoolantPct and t.heatedCoolantPct >= SOFT_MAX_HOT then return true end
- return false
- end
- -- handle incoming pkt -------------------------------------------
- local function handlePacket(pkt)
- if rnc3.autoPong(PROG, pkt) then return end
- local b = pkt.body or {}
- local t = b.type
- if t == "discover" or t == "controller-hello" then
- controllerId = pkt.from
- controllerLastSeen = os.clock()
- if b.disableSoftScram ~= nil then
- softAutoScram = not b.disableSoftScram
- end
- rnc3.send(PROG, pkt.from, {
- type = "reactor-hello",
- id = MY_ID,
- role = "fission-reactor",
- telem = readTelemetry(),
- ver = "fission_agent/1.4"
- })
- return
- end
- if t == "controller-hb" then
- controllerId = pkt.from
- controllerLastSeen = os.clock()
- if b.disableSoftScram ~= nil then
- softAutoScram = not b.disableSoftScram
- end
- return
- end
- if t == "cmd" then
- controllerId = pkt.from
- controllerLastSeen = os.clock()
- local cmd = b.cmd
- if cmd == "on" then
- desired.run = true
- -- don't force start if too hot; main loop will do it
- saveDesired()
- startFromDesired()
- rnc3.send(PROG, pkt.from, {type="ack", cmd="on", ok=true})
- elseif cmd == "off" then
- desired.run = false
- saveDesired()
- scram()
- rnc3.send(PROG, pkt.from, {type="ack", cmd="off", ok=true})
- elseif cmd == "setBurn" and b.value then
- local ok, valOrReason = setBurn(b.value)
- rnc3.send(PROG, pkt.from, {
- type="ack", cmd="setBurn", ok=ok,
- burn = ok and valOrReason or nil,
- reason = ok and nil or valOrReason
- })
- elseif cmd == "setSoftScram" and b.value ~= nil then
- softAutoScram = b.value and true or false
- rnc3.send(PROG, pkt.from, {type="ack", cmd="setSoftScram", ok=true})
- elseif cmd == "restart" or cmd == "reboot" then
- -- SAVE CURRENT DESIRED, then reboot
- saveDesired()
- scram()
- rnc3.send(PROG, pkt.from, {type="ack", cmd=cmd, ok=true})
- sleep(0.1)
- os.reboot()
- end
- end
- end
- -- try to honor desired right after boot ---------------------------
- startFromDesired()
- -- main loop -------------------------------------------------------
- while true do
- local now = os.clock()
- -- controller timeout -> re-enable soft scram
- if controllerId and (now - controllerLastSeen) > CONTROLLER_TIMEOUT then
- controllerId = nil
- softAutoScram = true
- end
- local telem = readTelemetry()
- -- HARD safety
- if telem.temp >= HARD_SCRAM_TEMP then
- if telem.status ~= "stopped" then
- scram()
- -- once HARD fired, we should not auto-start -> desired.run = false
- desired.run = false
- saveDesired()
- end
- else
- -- SOFT safety
- if isSoftUnsafe(telem) and telem.status ~= "stopped" then
- scram()
- -- also clear desired.run so we don't flap
- desired.run = false
- saveDesired()
- end
- end
- -- try to start later if desired.run is true but we couldn't start earlier
- if desired.run then
- if telem.status == "stopped" and canStartNow() then
- startFromDesired()
- end
- end
- -- heartbeat
- if (now - lastHeartbeat) >= HEARTBEAT_EVERY then
- lastHeartbeat = now
- local hb = {
- type = "hb",
- id = MY_ID,
- role = "fission-reactor",
- telem = telem
- }
- if controllerId then
- rnc3.send(PROG, controllerId, hb)
- else
- rnc3.broadcast(PROG, hb)
- end
- end
- local pkt = rnc3.recv(PROG, 0.2)
- if pkt then handlePacket(pkt) end
- end
Advertisement
Add Comment
Please, Sign In to add comment