Skip to content

Instantly share code, notes, and snippets.

@nitehawk
Last active August 6, 2017 22:31
Show Gist options
  • Save nitehawk/9753864 to your computer and use it in GitHub Desktop.
Save nitehawk/9753864 to your computer and use it in GitHub Desktop.
Big Reactors reactor control
--[[
Big Reactors controller script
Uses wired modems to connect to computer control port on reactor
Prefers monitor connection (also via wired modem) for local display and potentially control
Uses wireless modem channel to send status and accept control commands from central system
If present, wireless modem must be on top of the computer
Note: Do not connect more than one reactor to the same controlling computer
]]--
-- Global settings
-- This reactor ID
reactorID = 2
-- Wireless channels
wcStatus = 100
wcCtl = 110
-- Minimum control rod setting - This is to prevent longer 'hunt' times to optimize temperature
-- Note: This setting will be adjusted automatically as the reactor runs, but is not saved through restarts
crmin = 0
-- Start pushing in control rods when temperature climbs above this temp
tempmax = 995
tempmin = 940
-- Start pushing in control rods when stored energy climbs above this percent
-- Control rods will scale from crmin to 100%, starting from this %
storpct = 30
-- There should be no need to edit code below this line
-- Utility function - return a wrapped variable for a specific peripheral
function wrapThis(thing)
local wrapped, i = nil, 0
while wrapped == nil and i <= 100 do
wrapped = peripheral.wrap(thing.."_"..i)
i = i + 1
end
if wrapped == nil then
return nil
else
return wrapped
end
end
-- Collect statistics for Local display and remote send
function getStats()
local curTemp = r.getFuelTemperature()
local curFuel = r.getFuelAmount()
local maxFuel = r.getFuelAmountMax()
local curRF = r.getEnergyStored()
local ctlRod = r.getControlRodLevel(0)
local curFuelPct = curFuel/maxFuel*100
local curRFPct = curRF/100000
local curRFGen = r.getEnergyProducedLastTick()
local active = r.getActive()
-- Round values
curRFPct = math.floor((curRFPct*10))/10
curFuelPct = math.floor((curFuelPct*10))/10
curRFGen = math.floor(curRFGen)
return curTemp, curFuelPct, curRFPct, curRFGen, ctlRod, active
end
-- Local status display
function displayStatus()
if mon == nil then
return
end
local curTemp, curFuelPct, curRFPct, curRFGen, ctlRod, active = getStats()
mon.clear()
mon.setCursorPos(1,1)
mon.write("Temp: "..curTemp)
mon.setCursorPos(1,2)
mon.write("Fuel: "..curFuelPct.."%")
mon.setCursorPos(1,3)
mon.write("Energy: "..curRFPct.."%")
mon.setCursorPos(1,4)
mon.write("RF Gen: "..curRFGen)
mon.setCursorPos(1,5)
mon.write("Control Rod: "..ctlRod.."%")
mon.setCursorPos(1,6)
mon.write("Min Control Rod: "..crmin.."%")
mon.setCursorPos(1,7)
if active then
mon.write("Active")
else
mon.write("Shutdown")
end
end
-- Transmit current system stats on wireless channel
function txStatus()
if modem == nil then
return
end
local curTemp, curFuelPct, curRFPct, curRFGen, ctlRod, active = getStats()
local updateStr = "R:"..reactorID.." T:"..curTemp.." F:"..curFuelPct.." E:"..curRFPct.." G:"..curRFGen.." C:"..ctlRod.." S:"..crmin
if active then
updateStr = updateStr.." A:true"
else
updateStr = updateStr.." A:false"
end
modem.transmit(wcStatus, wcCtl, updateStr)
end
function main()
if tonumber(args[1]) ~= nil then
reactorID = tonumber(args[1])
end
print("Initializing reactor "..reactorID.." control...")
print("Max Temp: "..tempmax)
print("Control rod minimum: "..crmin.."%")
print("Target Energy level: "..storpct.."%")
-- Initialize connections to peripherals
if initConnections() == false then
return false
end
-- Start the reactor
startup()
-- Start our monitoring and control loop
running = true
local loopCount = 1
os.startTimer(1)
while running do
local e, p1, p2, p3, p4, p5 = os.pullEvent()
if e == "key" then
if p1 == 16 then -- 'q' key
running = false
elseif p1 == 13 then -- '=/+' key
crmin = crmin + 10
elseif p1 == 12 then -- '-' key
crmin = crmin - 10
elseif p1 == 18 then -- 'e'
r.setActive(false)
elseif p1 == 19 then -- 'r'
r.setActive(true)
end
elseif e == "modem_message" and p2 == wcCtl then
local rid, cmd = string.gmatch(p4, "%S+")
if rid == reactorID then
if cmd == 'stop' then
r.setActive(false)
elseif cmd == 'start' then
r.setActive(true)
elseif cmd == 'crminup' then
crmin = crmin + 10
elseif cmd == 'crmindown' then
crmin = crmin - 10
end
end
elseif e == "timer" then
os.startTimer(1)
end
-- Constrain the control rod settings
if crmin < 0 then
crmin = 0
elseif crmin > 100 then
crmin = 100
end
-- Run reactor controls every 10 cycles - this allows for some time for things to stabilize after each adjustment
if loopCount > 10 then
manageReactor()
loopCount = 1
end
-- Update the displays after every loop
displayStatus()
txStatus()
loopCount = loopCount + 1
end
cleanup()
return true
end
-- Initialize connections to peripherals. Only return false if we can't talk to the reactor
function initConnections()
-- Connect to reactor
r = wrapThis("BigReactors-Reactor")
if r == nil then
print("No reactor found")
return false
end
-- Check to see if we have a valid reactor
if r.getConnected() == false then
print("Reactor not connected")
return false
end
-- Connect monitor
mon = wrapThis("monitor")
if mon == nil then
print("No monitor found, full status display disabled")
else
-- Start things off with current status
displayStatus()
end
-- Connect wireless network - wireless modem needs to be on top
modem = peripheral.wrap("top")
if modem == nil then
print("No wireless modem found. No remote status/control enabled")
else
txStatus()
-- Open control channel
modem.open(wcCtl)
end
return true
end
-- Configure initial control rod settings and activate reactor
function startup()
-- Verify initial control rod state and activate reactor (if needed)
local curTemp, curFuelPct, curRFPct, curRFGen, ctlRod, active = getStats()
if ctlRod < crmin then
r.setAllControlRodLevels(crmin)
end
if active == false then
r.setActive(true)
end
end
-- Cleanup reactor before exiting
-- Note: This will shutdown the reactor to avoid having the reactor run without supervision
function cleanup()
r.setActive(false)
end
-- Adjust reactor status based on temperature, energy level and control rod settings
function manageReactor()
local cRT = r.getControlRodLevel(0)
-- First, if the control rods are lower than our minimum setting, just bump it up
if cRT < crmin then
r.setAllControlRodLevels(crmin)
end
local curTemp, curFuelPct, curRFPct, curRFGen, ctlRod, active = getStats()
ctlSet = ctlRod
ctTargR = 0
-- If the reactor stored energy is above the target, start pushing the control rods in - scale from crmin to 100
-- Better to run the reactor at 50% stable than turn it full on/off to maintain energy reserve?
-- Scale insertion speed based on how far above the target we are? - similar to temp adjustments
-- Figure out the target control rod setting based on energy storage
if curRFPct > storpct then
ctScale = 100 - crmin
esScale = 100 - storpct
esLvl = curRFPct - storpct
esMult = esLvl / esScale
ctTargR = ctScale * esMult + crmin
ctTargR = math.floor(ctTargR)
end
-- Look for control rod insertion, adjust and return if matched
-- If the reactor temp is too high, start pushing the control rods in - continue if matched
if curTemp > tempmax then
local dT = curTemp - tempmax
if dT > 100 then
dR = 15
elseif dT > 30 then
dR = 10
elseif dT > 20 then
dR = 5
elseif dT > 10 then
dR = 2
elseif dT > 5 then
dR = 1
end
ctlSet = ctlRod + dR
end
-- Apply energy load adjustment
if ctTargR > ctlSet then
ctlSet = ctTargR
end
-- If we are pushing the control rods in, then we need to do that here and then return - Note - a hot and charged reactor will scale control rods very quickly
if ctlSet > 100 then
ctlSet = 100
end
if ctlSet > ctlRod then
r.setAllControlRodLevels(ctlSet)
return
end
-- If the reactor temp is too low, retract control rods
if curTemp < tempmin then
local dT = tempmin - curTemp
if dT > 100 then
dR = 15
elseif dT > 70 then
dR = 10
elseif dT > 40 then
dR = 5
elseif dT > 20 then
dR = 2
elseif dT > 10 then
dR = 1
end
ctlSet = ctlSet - dR
end
-- Apply energy load adjustment
if ctTargR > ctlSet then
ctlSet = ctTargR
end
-- Retract control rods
if ctlSet < crmin then
ctlSet = crmin
end
if ctlSet <= ctlRod then
r.setAllControlRodLevels(ctlSet)
end
end
args = {...}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment