CCCPaste

Draconic Reactor Controller for CC

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()