Jump to content




My Reactor Control Network


1 reply to this topic

#1 Kizz

  • Members
  • 99 posts
  • LocationLouisville, Kentucky

Posted 17 July 2014 - 01:42 PM

Hey Guys!

Earlier on, I posted a showing of my early network code here: Link

Since then I've done a lot of tweaking, programming and adjusting. I now have a fully functional remote reactor (Big Reactors) control suite.

In this post I will be sharing and explaining my main communication code, as well as a few screen shots and a video.


This program is not perfect, and is continually growing, but I feel sharing it may help someone overcome some of the issues I struggled to overcome.

Here's a (really crappy) walk-through of my base: https://www.youtube....I7N51zbMwygkVbw


I'll begin with a little walk-through of my system:

The Reactor:

Posted Image

As you can see, there is a single reactor core so it's a small reactor, and it is controlled by the computer infront.

This guy simply listens for commands over the network and reacts to requests for data.

Posted Image

The numbers you see here are my debug for changing the rod depth.


Continuing on, we go to the main computer floor:

Posted Image

There are a few systems in here, as well as some OpenComputer systems.


First we'll start with the big screen:

Posted Image

On boot, this is the first display screen. It refreshes about every 5 seconds and displays some pretty important information.


In the screen control computer you can see the options:

Posted Image

We'll select option 1, because the other options are just computer control options.

Posted Image

Then if we look at the big screen:

Posted Image

Above we now see some more detailed reactor information. Very useful for remote monitoring and such.

Moving on, we have our Reactor Server:

Posted Image

Option 1 is the same as on the big screen:

Posted Image

But option 2 allows us to have a little control over the reactor:

Posted Image

From there I can either shut down, or turn on the reactor. I can also adjust the control rod depth. Very useful for heat management and control.


Here's the whole building:

Posted Image

It's a 5 level building, with 3 levels underground. The top floor is the control room, and the very bottom is the reactor. It's stored pretty deep to prevent an explosion destroying our ME drives.

So all in all, it looks like a pretty simple system, but it really took a lot of programming to get there!

I'll start with the Screen code:

--Modem Communication Numbering
-- 1/2 Screen to Server 1
-- 2/1 Server 1 to Screen
--3/4 Screen to Power Emergency PC
-- 4/3 Pwr Em. PC to Screen
local modem = peripheral.wrap('back')
local screen = peripheral.wrap('top')

--begin functions
function ps(n) --similar to print
screen.write(n)
x,y=screen.getCursorPos()
y=y+1
screen.setCursorPos(1,y)
end
----------------------------------------
function w(n) --write same line
x1,y1=screen.getCursorPos()
screen.setCursorPos(1,y1)
screen.clearLine()
screen.write(n)
end
----------------------------------------
function reset() --places cursor to left again
x1,y1=screen.getCursorPos()
screen.setCursorPos(1,y1)
end
----------------------------------------
function send(chan,reply,m) --transmits a message
  modem.transmit(chan,reply,m)
end
----------------------------------------
function get(getchan,tmout)--listener, will timeout or wait, and passes message into msg
if tmout==nil then
tmout=100000000000000000000000000000 --change to call event w/o timer
end
modem.open(getchan) --opens port
chk=modem.isOpen(getchan) --checks to make sure it is open
  if chk==true then --will set listener
  --mm = os.pullEvent("modem_message")
  timeout = os.startTimer(tmout)
  while true do
   event = {os.pullEvent()}
   if event[1] == "modem_message" then
	--print'Worked'
	--print(event[1].." "..event[2].." "..event[3].." "..event[4].." "..event[5].." "..event[6])
	msg=event[5]
	returnchan=event[4]
	chan2=event[3]
	--print(chan2) --Used for debug
	success=1
	break
	elseif event[1] == "timer" and event[2] == timeout then
	success=0
	break
	elseif event[1] == "stop_main" then --to allow system to shutdown without cluttering screen
	return
   end
  end
end
if chk==false then
  --send error# to screen
  print('Error 100: Failed to Bind Port '..chan..'.')
end
end
----------------------------------------
function chksvr(sendchan,svrnum,gettmout) --allows me to easily check multiple servers without writing code for a month.
send(sendchan,2,"ping")
get(2,gettmout)
if success==1 and msg=="ping" then
screen.setCursorPos(1,svrnum+2)
w("Server "..svrnum..": Online")
end
if success==0 then
screen.setCursorPos(1,svrnum+2)
w("Server "..svrnum..": DOWN")
end
--sleep(5)
end
----------------------------------------
function main() --this guy will check that we're not shutting down, then run our various checks
	while true do
timeout2 = os.startTimer(2)

	eventData = {os.pullEvent()}
		if eventData[1] == "stop_main" then
   sleep(10000000) --here to prevent main moving on... >.<
			return
			elseif eventData[1] == "timer" and eventData[2] == timeout2 then
   --Power system checks/screen
   listenR()
  
  
   --Server Checks/Screen
   chksvr(1,1,.1) --Server 1 Status
   chksvr(3,2,.1) --Server 2 Status
   chksvr(5,3,.1) --Server 3 Status
  
   fx,fy=screen.getCursorPos()--Re-align stuff, may have a function to do this, but scrolling up is too much work.
   screen.setCursorPos(1,fy+1)
  
   ps("")--Following displays live data from reactor
   screen.clearLine()
   ps("	---Reactor Status---")
	  screen.clearLine()
   ps("Core Temp:   "..fuelTemp.."C")
	  screen.clearLine()
   ps("Casing Temp: "..casingTemp.."C")
	  screen.clearLine()
   ps("Fuel Amount: "..fuelLvl.."mB")
	  screen.clearLine()
   ps("Energy/tick: "..energyTick.."RF/t")
	  screen.clearLine()
   ps("Held Energy: "..storedEnergy.."RF")
	  screen.clearLine()
   sleep(.5)
  
  
  
  
   --Screen choice chk
		end  
end
end
----------------------------------------
function scch() --this guy handles user input, screen selection and begins the shutdown process
while true do
print'Welcome to the Screen Server'
print'Please select an option:'
print'1: Reactor Monitor'
print'2: System Control Touch Monitor'
print'3: System Shutdown'
sel=io.read()
sel=sel+1-1
if sel==0 then --Junk really
print'Scanning...'
--sleep(1)
end

if sel==1 then --Displays a static display of all reactor functions | Does not refresh
os.queueEvent("stop_main")
  screen.clear()
  screen.setCursorPos(1,1)
  ps('Current Reactor status:')
  ps("Reactor Active: "..tostring(active))
  ps("Stored Energy: "..storedEnergy.."RF")
  ps("Fuel Temp: "..fuelTemp.."C")
  ps("Casing Temp: "..casingTemp.."C")
  ps("Fuel Level: "..fuelLvl.."mB")
  ps("Waste Level: "..wasteLvl.."mB")
  ps("Energy/Tick: "..energyTick.."RF/t")
  ps("Reactivity: "..reactivity.."%")
  print('Press enter to continue...')
  io.read()
  screen.clear()
  screen.setCursorPos(1,1)
  os.reboot()
end

if sel==2 then --Future
print'Coming soon.'
end
if sel==3 then --Will stop everything and shut the system down
print'Shutting down!'
os.queueEvent("stop_main") --sends the shutdown event
--os.pullEvent("stop_main")
sleep(.5)
screen.clear()
screen.setCursorPos(1,1)
ps("Warning! System Shutting Down!")
sleep(5) --delay to allow the user to know what's happening
screen.clear()
os.shutdown() --BYE :D/>/>/>
end
term.clear()
term.setCursorPos(1,1) --just cause, we want to clean up after we're done selecting other choices
end
end
----------------------------------------
function listenR() --This function queues and grabs the latest reactor status information | Should have used a for or while loop...  :(/>/>/> but this allows debugging
--print'ListeningR...'
send(9,10,"req")
sleep(.8)--Not sure why but this is required for accurate reading!
get(10)
active=msg
--print(active)
sleep(.1)
get(11)
storedEnergy=msg
--print(storedEnergy)
sleep(.1)
get(12)
fuelTemp=msg
--print(fuelTemp)
sleep(.1)
get(13)
casingTemp=msg
--print(casingTemp)
sleep(.1)
get(14)
fuelLvl=msg
--print(fuelLvl)
sleep(.1)
get(15)
wasteLvl=msg
--print(wasteLvl)
sleep(.1)
get(16)
energyTick=msg
--print(energyTick)
sleep(.1)
get(17)
reactivity=msg
--print(reactivity)
sleep(.1)
end
-----------------------------------------
--end functions

--Body
screen.clear() --start by cleaning up incase of improper shutdown.
screen.setCursorPos(1,1)
ps("Kizz Tower Control: ONLINE") --Tell everyone we're alive
ps("")
ps("Loading...") --Tell people we're not ready yet
parallel.waitForAll(scch,main) --all systems go, let's run this baby!
--end body

Incase you want to see the code in a more readable format: http://pastebin.com/T62p4rVN

As of now I don't think I need to explain the code. It's pretty documented. If you would like some explanation, I will be more than happy to later :). Just leave a reply asking for it.

Next up we have the Reactor Server code. This server is located in the control room, and is not the computer attached to the reactor.

--Server 1 Control Server. Controls Main Reactor System
local modem = peripheral.wrap('back')

function send(chan,reply,m)
modem.transmit(chan,reply,m)
end
function get(chan,tmout)--listener
if tmout==nil then
tmout=100000000000000000000000000000 --change to call event w/o timer
end
modem.open(chan) --opens port
chk=modem.isOpen(chan) --checks to make sure it is open
  if chk==true then --will set listener
  --mm = os.pullEvent("modem_message")
  timeout = os.startTimer(tmout)
  while true do
   event = {os.pullEvent()}
   if event[1] == "modem_message" then
	--print'Worked'
	--print(event[1].." "..event[2].." "..event[3].." "..event[4].." "..event[5].." "..event[6])
	msg=event[5]
	returnchan=event[4]
	chan=event[3]
	return
	elseif event[1] == "timer" and event[2] == timeout then
	break
   end
  end
end
if chk==false then
  --send error# to screen
  print('Error 100: Failed to Bind Port '..chan..'.')
end
end
function listen()
print'Listening...'
while true do
get(1)
--print(msg)
send(2,1,"ping")
--sleep(.5)
--os.reboot()
end
end
function listenR()
print'ListeningR...'
while true do
send(9,10,"req")
get(10)
active=msg
--print(active)
get(11)
storedEnergy=msg
--print(storedEnergy)
get(12)
fuelTemp=msg
--print(fuelTemp)
get(13)
casingTemp=msg
--print(casingTemp)
get(14)
fuelLvl=msg
--print(fuelLvl)
get(15)
wasteLvl=msg
--print(wasteLvl)
get(16)
energyTick=msg
--print(energyTick)
get(17)
reactivity=msg
--print(reactivity)
sleep(5)
end
end
function test()
while true do
term.clear()
term.setCursorPos(1,1)
print'Welcome to Server 1.'
print'System online.'
print'Please make a choice:'
print'1: Reactor Monitor'
print'2: Reactor Control'
print'3: Shutdown'
print'4: Reboot'
print'5: Terminate'
ch=io.read()
ch=ch+1-1
if ch==1 then
print'Welcome to the Reactor Control Center'
print'Current status:'
print("Reactor Active: "..tostring(active))
print("Current Stored Energy: "..storedEnergy.."RF")
print("Fuel Temp: "..fuelTemp.."C")
print("Casing Temp: "..casingTemp.."C")
print("Fuel Level: "..fuelLvl.."mB")
print("Waste Level: "..wasteLvl.."mB")
print("Energy/Tick: "..energyTick.."RF/t")
print("Reactivity: "..reactivity.."%")
print'Press enter to continue...'
io.read()
end
if ch==2 then
print'---Reactor Control---'
print'Please make a choice:'
if active==true then
print'1: Turn Reactor off'
end
if active==false then
print'1: Turn Reactor on'
end
print'2: Set Control Rod Level (0-100%)'
sel=io.read()
sel=sel+1-1
if sel==1 and active==true then
send(100,1,"pwrOFF")
print'Shutting Reactor Down'
sleep(1)
end
if sel==1 and active==false then
send(100,1,"pwrON")
print'Powering on Reactor'
sleep(1)
end
if sel==2 then
print'Please input desired rod level for all rods(0-100% with no % symbol):'
rlvl=io.read()
rlvl=rlvl+1-1
send(100,1,"rodsLvl")
sleep(.1)
send(50,1,rlvl)
print('Sent '..rlvl.." to the reactor.")
sleep(1)
end
end
if ch==3 then
print'Shutting down!'
sleep(1)
os.shutdown()
end
if ch==4 then
print'Rebooting!'
sleep(1)
os.reboot()
end
if ch==5 then
print'Terminating program!'
sleep(1)
term.clear()
term.setCursorPos(1,1)
break
end
end
end
active=failed
storedEnergy=failed
fuelTemp=failed
casingTemp=failed
fuelLvl=failed
wasteLvl=failed
energyTick=failed
reactivity=failed
parallel.waitForAll(test,listen,listenR)

Again, the pastebin, for easy reading: http://pastebin.com/SL656QmH


And finally, the code for the reactor computer. This is the computer directly attached to the reactor:

--Reactor Receiver PC
local reactor=peripheral.wrap('back')
local modem=peripheral.wrap('right')

function send(chan,reply,m)
modem.transmit(chan,reply,m)
end
function get(chan,tmout)--listener
if tmout==nil then
tmout=100000000000000000000000000000 --change to call event w/o timer
end
modem.open(chan) --opens port
chk=modem.isOpen(chan) --checks to make sure it is open
  if chk==true then --will set listener
  --mm = os.pullEvent("modem_message")
  timeout = os.startTimer(tmout)
  while true do
   event = {os.pullEvent()}
   if event[1] == "modem_message" then
	--print'Worked'
	--print(event[1].." "..event[2].." "..event[3].." "..event[4].." "..event[5].." "..event[6])
	msg=event[5]
	returnchan=event[4]
	chan=event[3]
	break
	elseif event[1] == "timer" and event[2] == timeout then
	break
   end
  end
end
if chk==false then
  --send error# to screen
  print('Error 100: Failed to Bind Port '..chan..'.')
end
end
--Reactor Control/Monitor Vars
function update()
chkCn=reactor.getConnected()
chkActive=reactor.getActive()
chkEnergyStored=reactor.getEnergyStored()
chkFuelTemp=reactor.getFuelTemperature()
chkCasingTemp=reactor.getCasingTemperature()
chkFuel=reactor.getFuelAmount()
chkWaste=reactor.getWasteAmount()
chkEnergyTick=reactor.getEnergyProducedLastTick()
chkReactivity=reactor.getFuelReactivity()
end
function reactorPowerCtrl(n)
reactor.setActive(n)
end
function setAllRodLvl(n)
reactor.setAllControlRodLevels(n)
end
function ejectWaste(n)
reactor.doEjectWaste(n)
end
--Passing code: Passes all reactor monitoring vars to Server 1
function request()
print'Listening'
while true do
update()
get(9)
if msg=="req" then
send(10,9,chkActive)
sleep(.1)
send(11,9,chkEnergyStored)
sleep(.1)
send(12,9,chkFuelTemp)
sleep(.1)
send(13,9,chkCasingTemp)
sleep(.1)
send(14,9,chkFuel)
sleep(.1)
send(15,9,chkWaste)
sleep(.1)
send(16,9,chkEnergyTick)
sleep(.1)
send(17,9,chkReactivity)
--sleep(5)
end
end
end
function comms()
chkCn=reactor.getConnected()
if chkCn==true then
request()
end
if chkCn==false then
print'Failed to connect to reactor...'
sleep(1)
end
end
function watchdog()
while true do
get(100)
--print(msg)
if msg=="pwrON" then
reactorPowerCtrl(true)
end
if msg=="pwrOFF" then
reactorPowerCtrl(false)
end
if msg=="rodsLvl" then
get(50)
print(msg)
rlvl=msg+1-1
  if rlvl>=0 and rlvl<=100 then
  setAllRodLvl(rlvl)
  end
end
end
end
parallel.waitForAll(comms,watchdog)

And the pastebin: http://pastebin.com/8bi29H56


Later on, if requested, I'd be happy to explain some more of this code.

Hope you enjoyed!

Edited by Kizz, 17 July 2014 - 01:46 PM.


#2 Kizz

  • Members
  • 99 posts
  • LocationLouisville, Kentucky

Posted 17 July 2014 - 01:53 PM

PS: The dang Code paste removes most of my Tabbing and commenting... I think it needs a little work >.>





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users