Advertisement
iPlayG

Untitled

May 11th, 2025 (edited)
144
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.05 KB | None | 0 0
  1. ---------------------------------------------------
  2. -- Mekanism Fission Reactor Monitoring Program   --
  3. -- Created for CC:Tweaked and Mekanism           --
  4. -- Originally created by InternetUnexplorer on   --
  5. -- GitHub. Modified for personal use, by         --
  6. -- iPlay_G on Minecraft, Derrick355 on GitHub.   --
  7. -- As the original program did not contain any   --
  8. -- license, this will not contain any license.   --
  9. -- However, this software is provided "AS-IS",   --
  10. -- with NO WARRANTY of ANY kind. There is no     --
  11. -- guarentee of fitness for a particular         --
  12. -- purpose.                                      --
  13. -- Feel free to use it, but I, nor the           --
  14. -- original author are responsible for any       --
  15. -- issues arising from the use of this program.  --
  16. --                    -=@=-                      --
  17. -- Thank you for downloading this program.       --
  18. ---------------------------------------------------
  19.  
  20. version = "1.3"
  21.  
  22. --[[
  23. NOTES
  24. If you see 'CRITICAL' on your monitor as the
  25. state, that is OK. Critical is what real
  26. reactors are considered when operating normally.
  27. The word 'SCRAM' generally refers to an emergency
  28. shutdown, either automatically or manually.
  29.  
  30. It is generally recommended to edit settings
  31. in an IDE or text editor for ease of use.
  32.  
  33. It is assumed you have a 6 wide, 4 tall monitor
  34. attached via a modem.
  35.  
  36. References to an analog lever are common, since
  37. I used the Create mod, however if you don't
  38. have it, any way to input a variable-power
  39. redstone signal should work.
  40.  
  41. Binary levers are just vanillia levers.
  42.  
  43. I recommend putting this program on a disk,
  44. and having a startup script run this program.
  45. I have a second computer that I use to edit
  46. the program in-game so that the main one
  47. only runs this program, and nothing else.
  48.  
  49. Current rules: (SCRAMs the reactor if any violated)
  50. Reactor temp > 1100K
  51. Reactor damage > 10%
  52. Reactor waste level > 10%
  53. Coolant (cold) level < 95%
  54. Coolant (hot) level > 10%
  55. Turbine energy level > 95%
  56. Reactor fuel level < 10%
  57.  
  58.  
  59. RECOMMENDED & ASSUMED COMPUTER I/O SETUP
  60. TOP: Binary lever, used to enable/disable manual
  61.     burn rate control
  62. LEFT: Analog lever, used as coarse adjust on burn
  63.     control
  64. RIGHT: Analog lever, used as fine adjust on burn
  65.     control
  66. BOTTOM: Binary lever, used as reactor on/off
  67.     control
  68. BACK: Modem to connect to reactor, turbine, and
  69.     monitor
  70. FRONT: Alarm or siren. Outputs a redstone signal if
  71.     turbine power falls below a set threshold.
  72. ]]
  73.  
  74. --- VARIABLE SETTINGS ---
  75. -- Set the multiplier for the burn rate coarse adj.
  76. -- Default = 75
  77. burn_rate_coarse_multi = 1
  78.  
  79. -- Set the multiplier for the burn rate fine adj.
  80. -- Default = 5
  81. burn_rate_fine_multi = .1
  82.  
  83. -- Maximum auto burn rate to establish
  84. -- Default = 120
  85. burn_rate_auto_max = 15
  86.  
  87. -- What turbine power percent value should burn
  88. --  rate be max at? For this value and all lower,
  89. --  burn rate will be at the max defined above.
  90. -- Default = 0.5 (for 50%)
  91. min_turbine_power = 0.8
  92.  
  93. -- What turbine power percent value should burn
  94. --  rate be zero at? For this value and all higher,
  95. --  burn rate will be at zero.
  96. -- Default = 0.75 (for 75%)
  97. max_turbine_power = 0.9
  98.  
  99. -- What is the monitor called on the network?
  100. -- Default = monitor_0
  101. monitor_name = "monitor_0"
  102.  
  103. -- Are you playing on a server with Advanced
  104. --  peripherals? This is used so that if a server
  105. --  is restarting the computer can scram the
  106. --  reactor. Note, the server must issue some kind of
  107. --  restart warning beforehand.
  108. -- Default = true
  109. on_server = false
  110.  
  111. -- If the above is true, you must configure this line.
  112. -- What message in chat should the program look for to see if the server
  113. -- is restarting?
  114. -- Default = "SCRAM"
  115. restart_message = "SCRAM"
  116.  
  117. -- Do not modify anything past this line unless you know what you are doing.
  118.  
  119. ------------------------------------------------
  120.  
  121. local state, data, reactor, turbine, info_window, rules_window
  122.  
  123. local STATES = {
  124.     READY = 1, -- Reactor is off and can be started with the lever
  125.     RUNNING = 2, -- Reactor is running and all rules are met
  126.     ESTOP = 3, -- Reactor is stopped due to rule(s) being violated
  127.     UNKNOWN = 4, -- Reactor or turbine peripherals are missing
  128. }
  129.  
  130. ------------------------------------------------
  131.  
  132. local function redstone_restart()
  133.     if fs.exists("disk/temp/notify_restart") then
  134.         redstone.setOutput("front", true)
  135.     else
  136.         redstone.setOutput("front", false)
  137.     end
  138. end
  139.  
  140. redstone_restart()
  141.  
  142. local function check_for_restart_scram()
  143.     if fs.exists("disk/temp/scrammed") then
  144.         incoming_restart = true
  145.         fs.delete("disk/temp/scrammed")
  146.         fs.makeDir("disk/temp/notify_restart")
  147.         fs.makeDir("disk/temp/redstone_restart")
  148.     else
  149.         incoming_restart = false
  150.     end
  151.     return incoming_restart
  152. end
  153.  
  154. incoming_restart = check_for_restart_scram()
  155.  
  156. local function check_for_restart_notify()
  157.     if fs.exists("disk/temp/notify_restart") then
  158.         notify_restart = true
  159.         fs.delete("disk/temp/notify_restart")
  160.     else
  161.         notify_restart = false
  162.     end
  163.     return notify_restart
  164. end
  165.  
  166. notify_restart = check_for_restart_notify()
  167.  
  168. local function pollChatbox()
  169.     while true do
  170.         local box = peripheral.find('chatBox', function(_, chatbox) return chatbox.getOperationCooldown('chatMessage') == 0 end)
  171.         if box then
  172.             return box
  173.         end
  174.         sleep(0.1)
  175.     end
  176. end
  177.  
  178. ------------------------------------------------
  179.  
  180. local rules = {}
  181.  
  182. local function add_rule(name, fn)
  183.     table.insert(rules, function()
  184.         local ok, rule_met, value = pcall(fn)
  185.         if ok and value ~= false then
  186.             return rule_met, string.format("%s ( %s)", name, value)
  187.     elseif ok then
  188.         return rule_met, string.format("%s (%s)", name, value)
  189.         else
  190.             return false, name
  191.         end
  192.     end)
  193. end
  194.  
  195. local function update_data()
  196.     coarse_adjust = (tonumber(redstone.getAnalogInput("left")) * burn_rate_coarse_multi)
  197.     fine_adjust = (tonumber(redstone.getAnalogInput("right")) * burn_rate_fine_multi)
  198.     set_burn_rate = (coarse_adjust + fine_adjust)
  199.  
  200.     data = {
  201.         lever_on = redstone.getInput("bottom"),
  202.         burn_rate_limit = set_burn_rate,
  203.         burn_rate_limited = redstone.getInput("top"),
  204.  
  205.         reactor_on = reactor.getStatus(),
  206.         reactor_burn_rate = reactor.getBurnRate(),
  207.         reactor_max_burn_rate = reactor.getMaxBurnRate(),
  208.         reactor_temp = reactor.getTemperature(),
  209.         reactor_damage = reactor.getDamagePercent(),
  210.         reactor_coolant = reactor.getCoolantFilledPercentage(),
  211.         reactor_hot_coolant = reactor.getHeatedCoolantFilledPercentage(),
  212.         reactor_waste = reactor.getWasteFilledPercentage(),
  213.         reactor_fuel = reactor.getFuelFilledPercentage(),
  214.         reactor_max_fuel = reactor.getFuelCapacity(),
  215.         reactor_fuel_level = reactor.getFuel(),
  216.  
  217.         turbine_energy = turbine.getEnergyFilledPercentage(),
  218.         turbine_power = turbine.getEnergy(),
  219.         max_turbine_power = turbine.getMaxEnergy()
  220.     }
  221.  
  222. end
  223.  
  224. add_rule("REACTOR TEMPERATURE      <=  1100K  ", function()
  225.     local value = string.format("%3dK", math.ceil(data.reactor_temp))
  226.     return data.reactor_temp <= 1100, value
  227. end)
  228.  
  229. add_rule("REACTOR DAMAGE           <=    10%  ", function()
  230.     local value = string.format("%3d%%", math.ceil(data.reactor_damage * 100))
  231.     return data.reactor_damage <= 0.10, value
  232. end)
  233.  
  234. add_rule("REACTOR FUEL LEVEL       >=    10%  ", function()
  235.     local value = string.format("%3d%%", math.floor(data.reactor_fuel * 100))
  236.     return data.reactor_fuel >= .10, valueA
  237. end)
  238.  
  239. add_rule("REACTOR WASTE LEVEL      <=    90%  ", function()
  240.     local value = string.format("%3d%%", math.ceil(data.reactor_waste * 100))
  241.     return data.reactor_waste <= 0.90, value
  242. end)
  243.  
  244. add_rule("COOLANT (COLD) LEVEL     >=    95%  ", function()
  245.     local value = string.format("%3d%%", math.floor(data.reactor_coolant * 100))
  246.     return data.reactor_coolant >= 0.95, value
  247. end)
  248.  
  249. add_rule("COOLANT (HOT) LEVEL      <=    10%  ", function()
  250.     local value = string.format("%3d%%", math.ceil(data.reactor_hot_coolant * 100))
  251.     return data.reactor_hot_coolant <= 0.10, value
  252. end)
  253.  
  254. add_rule("TURBINE ENERGY LEVEL     <=    95%  ", function()
  255.     local value = string.format("%3d%%", math.ceil(data.turbine_energy * 100))
  256.     return data.turbine_energy <= 0.95, value
  257. end)
  258.  
  259. if on_server then
  260.     add_rule("INCOMING SERVER RESTART  ==  FALSE  ", function()
  261.         return not incoming_restart, notify_restart
  262.     end)
  263. end
  264.  
  265. local function all_rules_met()
  266.     for i, rule in ipairs(rules) do
  267.         if not rule() then
  268.             return false
  269.         end
  270.     end
  271.     -- Allow manual emergency stop with SCRAM button
  272.     return state ~= STATES.RUNNING or data.reactor_on
  273. end
  274.  
  275. ------------------------------------------------
  276.  
  277. ------------------------------------------------
  278.  
  279. local monitor = peripheral.wrap(monitor_name)
  280.  
  281. term.redirect(monitor)
  282.  
  283. local function colored(text, fg, bg)
  284.     term.setTextColor(fg or colors.white)
  285.     term.setBackgroundColor(bg or colors.black)
  286.     term.write(string.upper(text))
  287. end
  288.  
  289. local function make_section(name, x, y, w, h, color)
  290.     for row = 1, h do
  291.         term.setCursorPos(x, y + row - 1)
  292.         local char = (row == 1 or row == h) and "\127" or " "
  293.         colored("\127" .. string.rep(char, w - 2) .. "\127", color or colors.gray)
  294.     end
  295.  
  296.     term.setCursorPos(x + 2, y)
  297.     colored(" " .. name .. " ")
  298.  
  299.     return window.create(term.current(), x + 2, y + 2, w - 4, h - 4)
  300. end
  301.  
  302. local function update_info()
  303.     local prev_term = term.redirect(info_window)
  304.  
  305.     term.clear()
  306.     term.setCursorPos(1, 1)
  307.  
  308.     if state == STATES.UNKNOWN then
  309.         colored("ERROR RETRIEVING DATA", colors.red)
  310.         return
  311.     end
  312.  
  313.     colored("REACTOR: ")
  314.     colored(data.reactor_on and "CRITICAL" or "SHUTDOWN", data.reactor_on and colors.green or colors.red)
  315.     colored(" LEVER: ")
  316.     colored(data.lever_on and "POWERED" or "SECURED", data.lever_on and colors.green or colors.red)
  317.     colored(" BURN RATE: ")
  318.     if data.burn_rate_limited == false then
  319.         colored(string.format("%4.1f", data.reactor_burn_rate), colors.blue)
  320.     else
  321.         colored(string.format("%4.1f", data.reactor_burn_rate), colors.yellow)
  322.     end
  323.     colored("/", colors.lightGray)
  324.     colored(string.format("%4.0f", data.reactor_max_burn_rate), colors.blue)
  325.  
  326.     term.setCursorPos(34, 2)
  327.     colored("SET LIMIT: ")
  328.     if data.burn_rate_limited then
  329.         colored(string.format("%4.1f", data.burn_rate_limit), colors.green)
  330.     else
  331.         colored(string.format("%4.1f", data.burn_rate_limit), colors.gray)
  332.     end
  333.  
  334.     term.setCursorPos(1, 4)
  335.  
  336.     colored("STATUS: ")
  337.     if state == STATES.READY then
  338.         colored("READY   - PULL LEVER TO STARTUP", colors.blue)
  339.     elseif state == STATES.RUNNING then
  340.         colored("RUNNING - PULL LEVER TO SHUTDOWN", colors.green)
  341.     elseif state == STATES.ESTOP and not all_rules_met() then
  342.         colored("SCRAM   - SAFETY RULE VIOLATED", colors.red)
  343.     elseif state == STATES.ESTOP then
  344.         colored("SCRAM   - TOGGLE LEVER TO RESET", colors.red)
  345.     end -- STATES.UNKNOWN cases handled above
  346.  
  347.     term.setCursorPos(1, 6)
  348.    
  349.     colored("STORED POWER: ")
  350.     colored(string.format("%4.0f", (data.turbine_power*4/1000000)), colors.green)
  351.     colored(" MFE", colors.green)
  352.     colored("/", colors.lightGray)
  353.  
  354.     colored(string.format("%4.0f", (data.max_turbine_power*4/1000000)), colors.blue)
  355.     colored(" MFE", colors.blue)
  356.    
  357.     term.setCursorPos(38, 6)
  358.     colored("(")
  359.     colored(string.format("%6.2f", (data.turbine_energy*100)) .. "%", colors.green)
  360.     colored(")")
  361.  
  362.     term.setCursorPos(1, 7)
  363.    
  364.     colored("STORED FUEL:  ")
  365.     colored(string.format("%4.0f", (data.reactor_fuel_level["amount"])), colors.green)
  366.     colored(" mB", colors.green)
  367.     colored("/", colors.lightGray)
  368.  
  369.     colored(string.format("%4.0f", (data.reactor_max_fuel)), colors.blue)
  370.     colored(" mB", colors.blue)
  371.  
  372.     term.setCursorPos(38, 7)
  373.     colored("(")
  374.     colored(string.format("%5.2f", (data.reactor_fuel*100)).. "%", colors.green)
  375.     colored(")")
  376.  
  377.     term.setCursorPos(1, 8)
  378.     colored("FUEL TYPE:    ", colors.white)
  379.     fuel_name = data.reactor_fuel_level["name"]
  380.     fuel_name = fuel_name:match"^.-:(.+)"
  381.     fuel_name = fuel_name.gsub(fuel_name, "_", " ")
  382.     colored(fuel_name, colors.green)
  383.  
  384.     term.redirect(prev_term)
  385.  
  386.     -- This can be commented to hide credits at the bottom
  387.     -- Would be nice if you didn't though
  388.     term.setCursorPos(5, 26)
  389.     colored("Made by InternetUnexplorer, forked by iPlay_G. v" .. version, colors.gray)
  390. end
  391.  
  392.  
  393. local estop_reasons = {}
  394.  
  395. local function update_rules()
  396.     local prev_term = term.redirect(rules_window)
  397.  
  398.     term.clear()
  399.  
  400.     if state ~= STATES.ESTOP then
  401.         estop_reasons = {}
  402.     end
  403.  
  404.     for i, rule in ipairs(rules) do
  405.         local ok, text = rule()
  406.         term.setCursorPos(1, i)
  407.         if ok and not estop_reasons[i] then
  408.             colored("[ SAFE ] ", colors.green)
  409.             colored(text, colors.lightGray)
  410.         else
  411.             colored("[ FAIL ] ", colors.red)
  412.             colored(text, colors.red)
  413.             estop_reasons[i] = true
  414.         end
  415.     end
  416.  
  417.     term.redirect(prev_term)
  418. end
  419.  
  420. ------------------------------------------------
  421.  
  422. local function main_loop()
  423.     -- Search for peripherals again if one or both are missing
  424.     if not state or state == STATES.UNKNOWN then
  425.         reactor = peripheral.find("fissionReactorLogicAdapter")
  426.         turbine = peripheral.find("turbineValve")
  427.     end
  428.  
  429.     if not pcall(update_data) then
  430.         -- Error getting data (either reactor or turbine is nil?)
  431.         data = {}
  432.         state = STATES.UNKNOWN
  433.     elseif data.reactor_on == nil then
  434.         -- Reactor is not connected
  435.         state = STATES.UNKNOWN
  436.     elseif data.turbine_energy == nil then
  437.         -- Turbine is not connected
  438.         state = STATES.UNKNOWN
  439.     elseif not state then
  440.         -- Program just started, get current state from lever
  441.         state = data.lever_on and STATES.RUNNING or STATES.READY
  442.     elseif state == STATES.READY and data.lever_on then
  443.         -- READY -> RUNNING
  444.         state = STATES.RUNNING
  445.         -- Activate reactor
  446.         pcall(reactor.activate)
  447.         data.reactor_on = true
  448.     elseif state == STATES.RUNNING and not data.lever_on then
  449.         -- RUNNING -> READY
  450.         state = STATES.READY
  451.     elseif state == STATES.ESTOP and not data.lever_on then
  452.         -- ESTOP -> READY
  453.         state = STATES.READY
  454.     elseif state == STATES.UNKNOWN then
  455.         -- UNKNOWN -> ESTOP
  456.         state = data.lever_on and STATES.ESTOP or STATES.READY
  457.         estop_reasons = {}
  458.     end
  459.  
  460.     -- Always enter ESTOP if safety rules are not met
  461.     if state ~= STATES.UNKNOWN and not all_rules_met() then
  462.         state = STATES.ESTOP
  463.     end
  464.  
  465.     -- SCRAM reactor if not running
  466.     if state ~= STATES.RUNNING and reactor then
  467.         pcall(reactor.scram)
  468.     end
  469.  
  470.     -- Make Windows
  471.     local width = term.getSize()
  472.     if state == STATES.ESTOP then
  473.         window_color = colors.red
  474.     elseif state == STATES.READY then
  475.         window_color = colors.green
  476.     else
  477.         window_color = colors.gray
  478.     end
  479.     info_window = make_section("STATUS", 2, 2, width - 2, 12, window_color)
  480.     rules_window = make_section("SAFETY RULES", 2, 15, width - 2, 11, window_color)
  481.  
  482.     -- Update info and rules windows
  483.     pcall(update_info)
  484.     pcall(update_rules)
  485.  
  486.     -- Update max burn rate to keep turbine below set energy.
  487.     if state ~= STATES.UNKNOWN and data.burn_rate_limited == false then
  488.    
  489.         local tgt_burn_pct = math.min(math.max(0.0, 1 - (data.turbine_energy - min_turbine_power) / (max_turbine_power - min_turbine_power)), 1.0)
  490.         pcall(reactor.setBurnRate, tgt_burn_pct * burn_rate_auto_max)
  491.     elseif state ~= STATES.UNKNOWN and data.burn_rate_limited == true then
  492.         pcall(reactor.setBurnRate, data.burn_rate_limit) -- Let burn rate be limited by an analog lever
  493.     end
  494.  
  495.     sleep() -- Other calls should already yield, this is just in case
  496.     return main_loop()
  497. end
  498.  
  499. term.setPaletteColor(colors.black, 0x000000)
  500. term.setPaletteColor(colors.gray, 0x343434)
  501. term.setPaletteColor(colors.lightGray, 0xababab)
  502. term.setPaletteColor(colors.red, 0xdb2d20)
  503. term.setPaletteColor(colors.green, 0x01a252)
  504. term.setPaletteColor(colors.blue, 0x01a0e4)
  505. term.setPaletteColor(colors.pink, 0xA9A9A9)
  506. term.clear()
  507.  
  508.  
  509. function scram_on_restart()
  510.     chatBox = peripheral.find("chatBox")
  511.     local event, username, message, uuid, isHidden = os.pullEvent("chat")
  512.     pollChatbox().sendMessage("username: " .. username .. " message: `".. message .. "`")
  513.     if message == restart_message and username == "sayCommand" then
  514.         --fs.copy("/disk/temp/scram.lua", "/disk/temp/scrammed.lua")
  515.         fs.makeDir("/disk/temp/scrammed")
  516.         pollChatbox().sendMessage("SCRAM aye")
  517.     end
  518. end
  519.  
  520. if on_server then
  521.     parallel.waitForAny(main_loop, scram_on_restart)
  522. else
  523.     parallel.waitForAny(main_loop, function()
  524.         os.pullEventRaw("terminate")
  525.     end)
  526. end
  527.  
  528. os.reboot()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement