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