1. local component = require("component") 2. local event = require("event") 3. local term = require("term") 4. local gpu = component.gpu 5.   6.  -- Safety Checks 7.   8. if not component.isAvailable("draconic_reactor") then 9.   print("Reactor not connected. Please connect computer to reactor with an Adapter block.") 10.   os.exit() 11. end 12. reactor = component.draconic_reactor 13. local flux_gates = {} 14. for x,y in pairs(component.list("flux_gate")) do 15.   flux_gates[#flux_gates+1] = x 16. end 17. if #flux_gates < 2 then 18.   print("Not enough flux gates connected; please connect inflow and outflow flux gates with Adapter blocks.") 19.   os.exit() 20. end 21. flux_in = component.proxy(flux_gates[1]) 22. flux_out = component.proxy(flux_gates[2]) 23. if not flux_in or not flux_out then 24.   print("Not enough flux gates connected; please connect inflow and outflow flux gates with Adapter blocks.") 25.   os.exit() 26. end 27.   28.  -- Functions 29.   30. function exit_msg(msg) 31.   term.clear() 32.   print(msg) 33.   os.exit() 34. end 35.   36. function modify_temp(offset) 37.   local new_temp = ideal_temp + offset 38.   if new_temp > 8000 then 39.     new_temp = 8000 40.   elseif new_temp < 2500 then 41.     new_temp = 2500 42.   end 43.   ideal_temp = new_temp 44. end 45.   46. function modify_field(offset) 47.   local new_strength = ideal_strength + offset 48.   if new_strength > 90 then 49.     new_strength = 90 50.   elseif new_strength < 1 then 51.     new_strength = 1 52.   end 53.   ideal_strength = new_strength 54. end 55.   56.  -- Buttons 57.   58. local adj_button_width = 19 59. local temp_adjust_x_offset = 68 60. local temp_adjust_y_offset = 2 61. local field_adjust_x_offset = temp_adjust_x_offset + adj_button_width + 2 62. local field_adjust_y_offset = 2 63.   64. local buttons = { 65.   start={ 66.     x=42, 67.     y=2, 68.     width=24, 69.     height=7, 70.     text="Start", 71.     action=function() 72.       if safe then 73.         state = "Charging" 74.         reactor.chargeReactor() 75.       elseif shutting_down then 76.         state = "Active" 77.         reactor.activateReactor() 78.       end 79.     end, 80.     condition=function() return safe or shutting_down end 81.   }, 82.   shutdown={ 83.     x=42, 84.     y=12, 85.     width=24, 86.     height=7, 87.     text="Shutdown", 88.     action=function() 89.       state = "Manual Shutdown" 90.       reactor.stopReactor() 91.     end, 92.     condition=function() return running end 93.   }, 94.   switch_gates={ 95.     x=2, 96.     y=20, 97.     width=24, 98.     height=3, 99.     text="Swap flux gates", 100.     action=function() 101.       local old_addr = flux_in.address 102.       flux_in = component.proxy(flux_out.address) 103.       flux_out = component.proxy(old_addr) 104.     end, 105.     condition=function() return safe end 106.   }, 107.   exit={ 108.     x=42, 109.     y=12, 110.     width=24, 111.     height=7, 112.     text="Exit", 113.     action=function() 114.       event_loop = false 115.     end, 116.     condition=function() return safe end 117.   }, 118.   temp_up_thousand={ 119.     x=temp_adjust_x_offset, 120.     y=temp_adjust_y_offset, 121.     width=adj_button_width, 122.     height=1, 123.     text="+ 1000", 124.     action=function() modify_temp(1000) end 125.   }, 126.   temp_up_hundred={ 127.     x=temp_adjust_x_offset, 128.     y=temp_adjust_y_offset+2, 129.     width=adj_button_width, 130.     height=1, 131.     text="+ 100", 132.     action=function() modify_temp(100) end 133.   }, 134.   temp_up_ten={ 135.     x=temp_adjust_x_offset, 136.     y=temp_adjust_y_offset+4, 137.     width=adj_button_width, 138.     height=1, 139.     text="+ 10", 140.     action=function() modify_temp(10) end 141.   }, 142.   temp_up_one={ 143.     x=temp_adjust_x_offset, 144.     y=temp_adjust_y_offset+6, 145.     width=adj_button_width, 146.     height=1, 147.     text="+ 1", 148.     action=function() modify_temp(1) end 149.   }, 150.   temp_down_thousand={ 151.     x=temp_adjust_x_offset, 152.     y=temp_adjust_y_offset+16, 153.     width=adj_button_width, 154.     height=1, 155.     text="- 1000", 156.     action=function() modify_temp(-1000) end 157.   }, 158.   temp_down_hundred={ 159.     x=temp_adjust_x_offset, 160.     y=temp_adjust_y_offset+14, 161.     width=adj_button_width, 162.     height=1, 163.     text=" - 100", 164.     action=function() modify_temp(-100) end 165.   }, 166.   temp_down_ten={ 167.     x=temp_adjust_x_offset, 168.     y=temp_adjust_y_offset+12, 169.     width=adj_button_width, 170.     height=1, 171.     text="- 10", 172.     action=function() modify_temp(-10) end 173.   }, 174.   temp_down_one={ 175.     x=temp_adjust_x_offset, 176.     y=temp_adjust_y_offset+10, 177.     width=adj_button_width, 178.     height=1, 179.     text="- 1", 180.     action=function() modify_temp(-1) end 181.   }, 182.   field_up_ten={ 183.     x=field_adjust_x_offset, 184.     y=field_adjust_y_offset+4, 185.     width=adj_button_width, 186.     height=1, 187.     text="+ 10", 188.     action=function() modify_field(10) end 189.   }, 190.   field_up_one={ 191.     x=field_adjust_x_offset, 192.     y=field_adjust_y_offset+6, 193.     width=adj_button_width, 194.     height=1, 195.     text="+ 1", 196.     action=function() modify_field(1) end 197.   }, 198.   field_down_ten={ 199.     x=field_adjust_x_offset, 200.     y=field_adjust_y_offset+12, 201.     width=adj_button_width, 202.     height=1, 203.     text="- 10", 204.     action=function() modify_field(-10) end 205.   }, 206.   field_down_one={ 207.     x=field_adjust_x_offset, 208.     y=field_adjust_y_offset+10, 209.     width=adj_button_width, 210.     height=1, 211.     text="- 1", 212.     action=function() modify_field(-1) end 213.   } 214. } 215.   216.  -- main code 217.   218. flux_in.setFlowOverride(0) 219. flux_out.setFlowOverride(0) 220. flux_in.setOverrideEnabled(true) 221. flux_out.setOverrideEnabled(true) 222.   223. local condition = reactor.getReactorInfo() 224. if not condition then 225.   print("Reactor not initialized, please ensure the stabilizers are properly laid out.") 226.   os.exit() 227. end 228.   229. ideal_strength = 50 230.   231. ideal_temp = 8000 232. cutoff_temp = 8100 233.   234.  -- tweakable pid gains 235.   236. inflow_P_gain = 1 237. inflow_I_gain = 0.04 238. inflow_D_gain = 0.1 239.   240. outflow_P_gain = 500 241. outflow_I_gain = 0.5 242. outflow_II_gain = 0.0000003 243. outflow_D_gain = 60000 244.   245.  -- initialize main loop 246.   247. inflow_I_sum = 0 248. inflow_D_last = 0 249.   250. outflow_I_sum = 0 251. outflow_II_sum = 0 252. outflow_D_last = 0 253.   254. state = "Standby" 255. shutting_down = false 256.   257. if condition.temperature > 25 then 258.   state = "Cooling" 259. end 260. if condition.temperature > 2000 then 261.   state = "Active" 262. end 263.   264.  -- Possible states: 265.   --Standby 266.   --Charging 267.   --Active 268.   --Manual Shutdown 269.   --Emergency Shutdown 270.   --Cooling 271.   272. event_loop = true 273. while event_loop do 274.   275.   if not component.isAvailable("draconic_reactor") then 276.     exit_msg("Reactor disconnected, exiting") 277.   end 278.   279.   if not component.isAvailable("flux_gate") then 280.     exit_msg("Flux gates disconnected, exiting") 281.   end 282.   283.   local info = reactor.getReactorInfo() 284.   285.   local inflow = 0 286.   local outflow = 0 287.   288.   shutting_down = state == "Manual Shutdown" or state == "Emergency Shutdown" 289.   running = state == "Charging" or state == "Active" 290.   safe = state == "Standby" or state == "Cooling" 291.   292.   if state == "Charging" then 293.     inflow = 200000 294.   295.     if info.temperature > 2000 then 296.       reactor.activateReactor() 297.       state = "Active" 298.     end 299.   elseif state == "Cooling" then 300.     if info.temperature < 25 then 301.       state = "Standby" 302.     end 303.     inflow = 10 304.     outflow = 20 305.   elseif state == "Standby" then 306.     inflow = 10 307.     outflow = 20 308.   else 309.     -- adjust inflow rate based on field strength 310.     311.     local field_error = (info.maxFieldStrength * (ideal_strength / 100)) - info.fieldStrength 312.     local proportional_field_error = field_error * inflow_P_gain 313.     inflow_I_sum = inflow_I_sum + field_error 314.     local integral_field_error = inflow_I_sum * inflow_I_gain 315.     local derivative_field_error = (field_error - inflow_D_last) * inflow_D_gain 316.     inflow_D_last = field_error 317.     local inflow_correction = proportional_field_error + integral_field_error + derivative_field_error 318.     if inflow_correction < 0 then 319.       inflow_I_sum = inflow_I_sum - field_error 320.     end 321.     inflow = inflow_correction 322.   323.     if not shutting_down then 324.   325.       -- adjust outflow rate based on core temperature 326.   327.       local temp_error = ideal_temp - info.temperature 328.       local proportional_temp_error = temp_error * outflow_P_gain 329.       outflow_I_sum = outflow_I_sum + temp_error 330.       local integral_temp_error = outflow_I_sum * outflow_I_gain 331.       if math.abs(temp_error) < 100 then 332.         outflow_II_sum = outflow_II_sum + integral_temp_error 333.       else 334.         outflow_II_sum = 0 335.       end 336.       local second_integral_temp_error = outflow_II_sum * outflow_II_gain 337.       local derivative_temp_error = (temp_error - outflow_D_last) * outflow_D_gain 338.       outflow_D_last = temp_error 339.       local outflow_correction = proportional_temp_error + integral_temp_error + second_integral_temp_error + derivative_temp_error 340.       if outflow_correction < 0 then 341.         outflow_I_sum = outflow_I_sum - temp_error 342.       end 343.       outflow = outflow_correction 344.   345.       -- cut off reactor in case of emergency 346.   347.       if info.temperature > cutoff_temp then 348.         print("Reactor temperature greater than", cutoff_temp, ", failsafe activated, shutting down") 349.         outflow = 0 350.         state = "Emergency Shutdown" 351.         reactor.stopReactor() 352.       end 353.     else 354.       if info.temperature < 2000 then 355.         state = "Cooling" 356.       end 357.     end 358.   end 359.   360.   if state ~= "Active" and not shutting_down then 361.     inflow_I_sum = 0 362.     inflow_D_last = 0 363.     outflow_I_sum = 0 364.     outflow_II_sum = 0 365.     outflow_D_last = 0 366.   end 367.   368.   if inflow < 0 then 369.     inflow = 0 370.   end 371.   if outflow < 0 then 372.     outflow = 0 373.   end 374.   375.   inflow = math.floor(inflow) 376.   outflow = math.floor(outflow) 377.   378.   flux_in.setFlowOverride(inflow) 379.   flux_out.setFlowOverride(outflow) 380.   381.   -- Draw screen 382.   383.   if term.isAvailable() then 384.   385.     -- Draw Values 386.   387.     local left_margin = 2 388.     local spacing = 2 389.   390.     local values = { 391.       "Status:              " .. state, 392.       "Field Strength:      " .. ((info.fieldStrength / info.maxFieldStrength) * 100) .. "%", 393.       "Energy Saturation:   " .. ((info.energySaturation / info.maxEnergySaturation) * 100) .. "%", 394.       "Fuel Concentration:  " .. ((1 - info.fuelConversion / info.maxFuelConversion) * 100) .. "%", 395.       "Temperature:         " .. info.temperature .. " F", 396.       "Reactor Efficiency:  " .. info.fuelConversionRate .. " nb/t", 397.       "Energy Inflow Rate:  " .. inflow .. " RF/t", 398.       "Energy Outflow Rate: " .. outflow .. " RF/t" 399.     } 400.   401.     if safe then 402.       values[#values+1] = "Click if flux gates need to be swapped" 403.     end 404.   405.     term.clear() 406.     407.     for i, v in ipairs(values) do 408.       term.setCursor(left_margin, i * spacing) 409.       term.write(v) 410.     end 411.   412.     -- Draw button values 413.   414.     term.setCursor(temp_adjust_x_offset, temp_adjust_y_offset+8) 415.     term.write("Target Temp: " .. ideal_temp .. " F") 416.     term.setCursor(field_adjust_x_offset, field_adjust_y_offset+8) 417.     term.write("Target Strength: " .. ideal_strength .. "%") 418.   419.     -- Draw Buttons 420.   421.     gpu.setForeground(0x000000) 422.   423.     for bname, button in pairs(buttons) do 424.       if button.depressed then 425.   426.         button.depressed = button.depressed - 1 427.         if button.depressed == 0 then 428.           button.depressed = nil 429.         end 430.       end 431.       if button.condition == nil or button.condition() then 432.         local center_color = 0xAAAAAA 433.         local highlight_color = 0xCCCCCC 434.         local lowlight_color = 0x808080 435.         if button.depressed then 436.           center_color = 0x999999 437.           highlight_color = 0x707070 438.           lowlight_color = 0xBBBBBB 439.         end 440.         gpu.setBackground(center_color) 441.         gpu.fill(button.x, button.y, button.width, button.height, " ") 442.         if button.width > 1 and button.height > 1 then 443.           gpu.setBackground(lowlight_color) 444.           gpu.fill(button.x+1, button.y+button.height-1, button.width-1, 1, " ") 445.           gpu.fill(button.x+button.width-1, button.y, 1, button.height, " ") 446.           gpu.setBackground(highlight_color) 447.           gpu.fill(button.x, button.y, 1, button.height, " ") 448.           gpu.fill(button.x, button.y, button.width, 1, " ") 449.         end 450.         gpu.setBackground(center_color) 451.         term.setCursor(button.x + math.floor(button.width / 2 - #button.text / 2), button.y + math.floor(button.height / 2)) 452.         term.write(button.text) 453.       end 454.     end 455.   456.     gpu.setBackground(0x000000) 457.     gpu.setForeground(0xFFFFFF) 458.   end   459.   460.   -- Wait for next tick, or manual shutdown 461.   462.   local event, id, op1, op2 = event.pull(0.05) 463.   if event == "interrupted" then 464.     if safe then 465.       break 466.     end 467.   elseif event == "touch" then 468.     469.     -- Handle Button Presses 470.   471.     local x = op1 472.     local y = op2 473.   474.     for bname, button in pairs(buttons) do 475.       if (button.condition == nil or button.condition()) and x >= button.x and x <= button.x + button.width and y >= button.y and y <= button.y + button.height then 476.         button.action() 477.         button.depressed = 3 478.       end 479.     end 480.   end 481. end 482.   483. term.clear()