Last active
August 6, 2017 22:31
-
-
Save nitehawk/9753864 to your computer and use it in GitHub Desktop.
Big Reactors reactor control
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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