Advertisement
AlexMastang

Mekanism Advanced Fission Reactor Control

Aug 21st, 2022 (edited)
452
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.63 KB | None | 0 0
  1. --------------------------------------------------------
  2. -- This program won’t work properly with a sodium
  3. -- cooled reactor as it won’t calculate the boiler,
  4. -- meaning the reactor will overheat and shutdown
  5. -- several times.
  6. --
  7. --
  8. -- By Alexmaster75
  9. --------------------------------------------------------
  10.  
  11. -- peripherals
  12. local reactor = peripheral.find("fissionReactorLogicAdapter")
  13. local turbine = peripheral.find("turbineValve")
  14. local monitor = peripheral.find("monitor")
  15.  
  16. -- variables
  17. local cfg_n = "cfg.txt"
  18. local log_n = "log.txt"
  19. local cfg, log
  20. local data = {}
  21. local reboot = 1
  22. local max_energy = 0.8
  23. local min_energy = 0.2
  24. local limit_rate = 1.0
  25. local deact_side = "left"
  26. local scram_side = "right"
  27. local power = true
  28. local max_burn_rate = 0
  29. local max_turbine_energy = 0
  30. local max_steam = 0
  31. local status = ""
  32. local x, y
  33. local N = 10 -- this is the number responsible for how many samples are required for the estimation
  34. local smpl = {} -- and this is the array that stores those values
  35. local S = 0
  36.  
  37. local j = 0
  38. local k = 0
  39.  
  40. -- this shit is for the different timings, you know, you don't want the screen being updated like 500 times per second
  41. local t = 0.0
  42. local t_max = 10.0
  43. local ta = 0.05 -- a brake to the program (if the value is <=0 the program just explodes as it either goes full power or just error)
  44. local t0 = tonumber(os.date("%S")) -- this variable is used for time estimation sector
  45. local td = 5 -- this regulates the delay between each estimation (seconds)
  46. local ts = 0 -- this variable is responsible to save the time elapsed since last execution
  47.  
  48.  
  49. -- all of these below are just functions, after the double space
  50. -- it just prints a divider
  51. local function divider(char)
  52.     local w, h = term.getSize()
  53.     for i=1,w do
  54.         write(char)
  55.     end
  56.     print()
  57. end
  58.  
  59. -- checks if the file exists
  60. local function file_exists(file)
  61.     local f = io.open(file, "rb")
  62.     if (f) then f:close() end
  63.     return f ~= nil
  64. end
  65.  
  66. -- gets real time (hh/mm/ss) and returns it as string
  67. local function rtime()
  68.     local ts = os.date("%X")
  69.     return ts
  70. end
  71. -- end of all functions
  72.  
  73.  
  74. -- =======================================
  75. --  ALL OF THIS BELOW IS THE MAIN PROGRAM
  76. -- =======================================
  77.  
  78. -- this part regards the checks for the communication, it makes sure all required components are connected and working properly
  79. -- waits for a response from the peripherals
  80. term.clear()
  81. term.setCursorPos(1, 1)
  82. term.setBackgroundColor(colors.black)
  83. term.setTextColor(colors.yellow)
  84. print("MEKANISM AUTOMATED CONTROL SYSTEM")
  85. term.setTextColor(colors.white)
  86. divider("=")
  87. print(("[%s] Checking response from peripherals.."):format(rtime()))
  88. x, y = term.getCursorPos()
  89. while (not (reactor and turbine)) do
  90.     if (t > t_max) then
  91.         error("Too long without a response")
  92.     end
  93.     reactor = peripheral.find("fissionReactorLogicAdapter")
  94.     turbine = peripheral.find("turbineValve")
  95.     monitor = peripheral.find("monitor")
  96.     term.setCursorPos(1, y)
  97.     print(("T: %.1f"):format(t))
  98.     t = t + 0.1
  99.     os.sleep(0.1)
  100. end
  101. print(("[%s] Done!"):format(rtime()))
  102. -- if the program finds them, it checks if they actually work
  103. print(("[%s] Checking if they're formed.."):format(rtime()))
  104. x, y = term.getCursorPos()
  105. t = 0.0
  106. while (not (reactor.isFormed() and turbine.isFormed())) do
  107.     if (t > t_max) then
  108.         error("Too long without a response")
  109.     end
  110.     term.setCursorPos(1, y)
  111.     print(("T: %.1f"):format(t))
  112.     t = t + 0.1
  113.     os.sleep(0.1)
  114. end
  115. print(("[%s] Done!"):format(rtime()))
  116. -- if the program has arrived at this point then all the components are working properly
  117. -- but there's still a chance of not being truly online, so it waits for a valid response
  118. print(("[%s] Checking if the systems are online.."):format(rtime()))
  119. x, y = term.getCursorPos()
  120. t = 0.0
  121. while (reactor.getMaxBurnRate() == nil and turbine.getMaxEnergy() == nil) do
  122.     if (t > t_max) then
  123.         error("Too long without a response")
  124.     end
  125.     term.setCursorPos(1, y)
  126.     print(("T: %.1f"):format(t))
  127.     t = t + 0.1
  128.     os.sleep(0.1)
  129. end
  130. print(("[%s] Done!"):format(rtime()))
  131.  
  132. print(("[%s] Checking the config file.."):format(rtime()))
  133. -- reads (or creates is it doesn't exist) the config file
  134. if (not file_exists(cfg_n)) then
  135.     -- creates it
  136.     print(("[%s] Config file didn't found"):format(rtime()))
  137.     print(("[%s] Creating it.."):format(rtime()))
  138.     cfg = fs.open(cfg_n, "w")
  139.     data = {reboot, limit_rate, max_energy, min_energy, deact_side, scram_side, td, N}
  140.     cfg.write(textutils.serialise(data))
  141.     cfg.close()
  142. else
  143.     -- reads it
  144.     print(("[%s] Config file found"):format(rtime()))
  145.     print(("[%s] Reading it.."):format(rtime()))
  146.     cfg = fs.open(cfg_n, "r")
  147.     data = textutils.unserialise(cfg.readAll())
  148.     cfg.close()
  149.  
  150.     reboot = data[1]
  151.     limit_rate = data[2]
  152.     max_energy = data[3]
  153.     min_energy = data[4]
  154.     deact_side = data[5]
  155.     scram_side = data[6]
  156.     td = data[7]
  157.     N  = data[8]
  158. end
  159. print(("[%s] Done!"):format(rtime()))
  160. -- then it restarts the entire program if necessary
  161. if (reboot ~= 0) then
  162.     cfg = fs.open(cfg_n, "r")
  163.     data = textutils.unserialise(cfg.readAll())
  164.     data[1] = 0
  165.     cfg = fs.open(cfg_n, "w")
  166.     cfg.write(textutils.serialise(data))
  167.     cfg.close()
  168.     os.reboot()
  169. end
  170.  
  171. -- here all the constants regarding the external system are assigned
  172. max_burn_rate = reactor.getMaxBurnRate() * limit_rate -- this is a limit imposed to the burn rate, like if someone wants to operate at maximum 50%, the program multiplies the max rate times 0.5
  173. max_turbine_energy = turbine.getMaxEnergy()
  174. max_steam = turbine.getSteamCapacity()
  175.  
  176. print(("[%s] All set!"):format(rtime()))
  177. print(("[%s] Program started"):format(rtime()))
  178.  
  179. -- checks if a monitor is connected to the network/computer
  180. -- if so it redirects the terminal output to the monitor
  181. if (monitor) then
  182.     term.redirect(monitor)
  183. end
  184. term.clear()
  185. term.setCursorPos(1, 1)
  186. term.setBackgroundColor(colors.black)
  187. term.setTextColor(colors.yellow)
  188. print("MEKANISM AUTOMATED CONTROL SYSTEM")
  189. term.setTextColor(colors.white)
  190. divider("=")
  191. print(("> Min - Max Turbine Energy: %d - %d%s"):format(min_energy*100, max_energy*100, "%"))
  192. divider("=")
  193. x, y = term.getCursorPos()
  194. term.setCursorPos(1, y+8)
  195. divider("-")
  196.  
  197. -- resets the array to 0
  198. for i=1,N do
  199.     smpl[i] = 0
  200. end
  201.  
  202. -- saves the actual time (unix timestamp in seconds)
  203. ts = os.time(os.date("!*t"))
  204.  
  205. -- MAIN CYCLE
  206. while (true) do
  207.     -- just gets the reactor status
  208.  
  209.     -- safety conditions
  210.     if (reactor.getTemperature() > 1200.0 or turbine.getEnergy() >= max_turbine_energy or reactor.getWasteFilledPercentage() > 0.8 or reactor.getCoolantFilledPercentage() < 0.5) then
  211.         if (reactor.getStatus()) then
  212.             reactor.scram()
  213.         end
  214.         break
  215.     end
  216.        
  217.     -- the logic of this thing (smooth brain)
  218.     -- if the side is activated the program first shutdowns the reactor, then the computer
  219.     if (rs.getInput(scram_side)) then
  220.         if (reactor.getStatus()) then
  221.             reactor.scram()
  222.         end
  223.         break
  224.     end
  225.  
  226.     -- this is for efficiency/safety. it basically makes the energy go in a range between the minimum & maximum energy in the turbine
  227.     if (turbine.getEnergyFilledPercentage() < min_energy) then
  228.         power = true
  229.     end
  230.     if (turbine.getEnergyFilledPercentage() > max_energy) then
  231.         power = false
  232.     end
  233.  
  234.     -- if the energy reserve is in range, then the logic can proceed
  235.     if (power and not rs.getInput(deact_side)) then
  236.         reactor.setBurnRate(max_burn_rate - (turbine.getEnergy() * max_burn_rate / max_turbine_energy))
  237.         -- it activates the reactor if it appears ready for operation or it's just off
  238.         if (not reactor.getStatus() and reactor.getCoolantFilledPercentage() > 0.5) then
  239.             reactor.activate()
  240.         end
  241.     else
  242.         if (reactor.getStatus()) then
  243.             reactor.scram()
  244.         end
  245.     end
  246.  
  247.     -- program output
  248.     term.setCursorPos(1, y)
  249.     -- start big print: it gets the status of the reactor and decides weather to print ON in green or OFF in red
  250.     write("> Status: ")
  251.     if (reactor.getStatus()) then
  252.         status = "ON"
  253.         term.setTextColor(colors.lime)
  254.     else
  255.         status = "OFF"
  256.         term.setTextColor(colors.red)
  257.     end
  258.     print(status, "         ")
  259.     term.setTextColor(colors.white)
  260.     -- end big print
  261.     print(("> Rods Percentage: %.1f%s          "):format((100 - (reactor.getBurnRate() * 100 / max_burn_rate)), "%"))
  262.     print(("> Reactor Temp.: %.2f °C          "):format(reactor.getTemperature() - 273.15))
  263.     print(("> Burn Rate: %.2f / %.2f mB/t          "):format(reactor.getBurnRate(), max_burn_rate))
  264.     print(("> Fuel: %.1f%s          "):format(reactor.getFuelFilledPercentage()*100, "%"))
  265.     print(("> Eta Fuel (HH/MM/SS): %2d:%2d:%2d          "):format(S/3600, (S/60)%60, S%60))
  266.     print(("> Heating Rate: %d mB/t          "):format(reactor.getHeatingRate()))
  267.     print(("> Coolant: %.1f%s          "):format(reactor.getCoolantFilledPercentage()*100, "%"))
  268.     term.setCursorPos(1, y+9)
  269.     print(("> Turbine Energy: %.1f / %.1f MFE (%.1f%s)         "):format((turbine.getEnergy()/2500000), (max_turbine_energy/2500000), turbine.getEnergyFilledPercentage()*100, "%"))
  270.     print(("> Flow Rate: %d mB/t          "):format(turbine.getFlowRate()))
  271.     print(("> Turbine Prod.: %.1f kFE/t          "):format(turbine.getProductionRate()/2500))
  272.     print(("> Turbine Gas Cap.: %d B          "):format(max_steam/1000))
  273.  
  274.     -- this sector of the cycle decides the time left for the
  275.     if (math.abs(tonumber(os.date("%S")) - t0) > td) then
  276.         t0 = tonumber(os.date("%S"))
  277.        
  278.         k = 0
  279.         j = 0
  280.        
  281.         -- moves all the values forward, for example:
  282.         -- { 23, 67, 12, 3 } -> { 0, 23, 67, 12 }
  283.         for i=N,1,-1 do
  284.             smpl[i] = smpl[i-1]
  285.         end
  286.  
  287.         -- assigns the reactor's burn rate to the first value of the array
  288.         smpl[1] = reactor.getActualBurnRate()
  289.  
  290.         -- cycles through the array picking up all the values that are different from 0, then it sums them toghether in the variable "k", also incrementing "j" by 1 each time
  291.         for i=1,N do
  292.             if (smpl[i] ~= 0) then
  293.                 k = k + smpl[i]
  294.                 j = j + 1
  295.             end
  296.         end
  297.         -- because "j" is the divider for "k", there could be a problem if the array is empty (full of 0), resulting in "j" being 0 and you can't divide by 0
  298.         -- therefore, it checks if it is anywhere near 0 and if so, assigns 1 to it
  299.         if (j <= 0) then j = 1 end
  300.  
  301.         -- this is just an arithmetic average of all the values in the array
  302.         -- *20 is for the burn rate (velocity) being in mB/t, we want it in seconds so because 1 second = 20 ticks we do that
  303.         k = k*20 / j
  304.         -- calculation of seconds
  305.         S = math.floor(reactor.getFuel().amount / k)
  306.     end
  307.  
  308.     os.sleep(ta)
  309. end
  310.  
  311. -- saves the time elapsed since last execution (TESLE)
  312. ts = os.time(os.date("!*t")) - ts
  313.  
  314. -- if for some reason the code encounters an exeption, it cuts to this point to make it smooth
  315. x, y = term.getCursorPos()
  316. while (x < 17) do
  317.     x = x + 1
  318. end
  319. term.setCursorPos(1, y)
  320. divider("-")
  321. print(("> Saving in /%s..."):format(log_n))
  322. log = io.open(log_n, "w")
  323. log:write(("LOG OF THE %s\n"):format(os.date()))
  324. log:write(("========================\nTESLE (HH/MM/SS): %2d:%2d:%2d\n"):format(ts/3600, (ts/60)%60, ts%60))
  325. log:write("========================\nReactor stats:\n")
  326. log:write(("> Reactor Temp.: %.2f °C\n"):format(reactor.getTemperature() - 273.15))
  327. log:write(("> Burn Rate: %.2f / %.2f mB/t\n"):format(reactor.getBurnRate(), max_burn_rate))
  328. log:write(("> Heating Rate: %d mB/t\n"):format(reactor.getHeatingRate()))
  329. log:write(("> Coolant: %s (%.1f%s)\n"):format(reactor.getCoolant().name, reactor.getCoolantFilledPercentage()*100, "%"))
  330. log:write(("> Fuel: %s (%.1f%s)\n"):format(reactor.getFuel().name, reactor.getFuelFilledPercentage()*100, "%"))
  331. log:write(("> Waste: %s (%.1f%s)\n"):format(reactor.getWaste().name, reactor.getWasteFilledPercentage()*100, "%"))
  332. log:write("------------------------\nTurbine stats:\n")
  333. log:write(("> Turbine Energy: %.1f / %.1f MFE (%.1f%s)\n"):format((turbine.getEnergy()/2500000), (max_turbine_energy/2500000), turbine.getEnergyFilledPercentage()*100, "%"))
  334. log:write(("> Flow Rate: %d mB/t\n"):format(turbine.getFlowRate()))
  335. log:write(("> Turbine Prod.: %.1f kFE/t\n"):format(turbine.getProductionRate()/2500))
  336. log:write(("> Turbine Gas Cap.: %d B\n"):format(max_steam/1000))
  337. log:close()
  338. print("> Saved successfully")
  339. print("> Terminated")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement