View difference between Paste ID: azx3KLJ7 and 8d0JJ5Qi
SHOW: | | - or go back to the newest paste.
1
os.loadAPI("button")
2
3
 print("Running Reactor and Turbine Controller\n")
4
5
--Based on the PID Controller first posted by Copper280z on Reddit:
6
--http://www.reddit.com/r/feedthebeast/comments/2pxwzo/big_reactors_like_br_turbines_but_dont_like/
7
--Heavily Modified by LezChap for use with Multiple Turbines and Turbine Control
8
--Turbine Graphic Code used from Direwolf20's Reactor Controler (youtube.com/watch?v=l7ZwSFVYITU)
9
--Button API modified from Direwolf20 (youtube link above)
10
11
local turbines = {peripheral.find("BigReactors-Turbine")}
12
local reactor = peripheral.find("BigReactors-Reactor")
13
local mon = peripheral.find("monitor")
14
local totalTurbines = #turbines
15
16
--Change number below for your setup
17
rednet.open("bottom") --modem connected to Capacitor Relay Computer
18
local rodsPerTurbine = 5.5  --how much Control Rod needed to produce >2000mb of steam in your reactor, helps prevent reactor from jumping between on/off quickly
19
local goalRPM = 1792  --all RPM-based settings based on this number
20
local shutdownPower = 200000000  --make sure this is less then max Capacitor capacity
21
22
--Tweak these numbers if needed, as per Copper280z's post on Reddit (linked in header)
23
local P = .4
24
local I = .000001
25
local D = 12
26
27
--initiating variables
28
local integral = 0
29
local derivative = 0 
30
31
local error = 0
32
local prevError = 0
33
34
local table = {}
35
local needEngaged = false
36
local needShutdown = false
37
local turbineSpeed = 0
38
39
local slowdown = {}
40
for k,v in ipairs(turbines) do
41
  slowdown[k] = false
42
  v.setActive(true)
43
  v.setInductorEngaged(false)
44
  v.setVentOverflow(true)
45
  v.setFluidFlowRateMax(0)
46
end
47
48
local reactorCooldown = false
49
50
local prevControlRod = 0
51
local ControlRod = 0  
52
53
local CRchange = 0
54
local newCR = 0
55
56
reactor.setAllControlRodLevels(100)
57
reactor.setActive(true)
58
59
local turbinePage = 0
60
local lastPage = 1
61
local pages = math.ceil(totalTurbines / 4)
62
mon.clear()
63
64
local turbineSpeed = 0
65
local countTurbines = 0
66
local timerCounter = 0
67
68
function PID()
69
  turbineSpeed = 0
70
  local turbineNeedSteam = false
71
  for k,v in pairs(turbines) do
72
    local tempSpeed = v.getRotorSpeed()
73
    turbineSpeed = turbineSpeed + tempSpeed
74
    --if one turbine is too low, make sure steam is produced
75
    if tempSpeed < (goalRPM - 6) then
76
      turbineNeedSteam = true
77
    end
78
  end
79
  turbineSpeed = turbineSpeed / totalTurbines
80
  error = goalRPM - turbineSpeed
81
  
82
  ControlRod = reactor.getControlRodLevel(0)
83
  local reactorTemp = reactor.getFuelTemperature()
84
  
85
  integral = integral + error
86
  
87
  derivative = error - prevError
88
  
89
  if integral > 1000 then
90
    integral = 1000
91
  end
92
  
93
  if integral < -1000 then
94
    integral = -1000
95
  end
96
  
97
  CRchange = P*error + I*integral + D*derivative
98
  
99
  prevError = error
100
  
101
  newCR = ControlRod - CRchange
102
  
103
  --check how many Turbines are using steam, only make enough for that # of Turbines
104
  countTurbines = 0
105
  for k,v in ipairs(slowdown) do
106
    if not v then
107
      countTurbines = countTurbines + 1
108
    end
109
  end
110
  local maxControlRods = 100-(countTurbines * rodsPerTurbine)
111
  
112
  if newCR < maxControlRods then
113
    newCR = maxControlRods
114
  end
115
  
116
  if newCR > 100 then
117
    newCR = 100
118
  end
119
  
120
  if turbineNeedSteam then
121
    newCR = maxControlRods
122
  end
123
124
  --prevents fuel waste from too-hot reactors - normally used during turbine Spin-up
125
  if reactorTemp < 200 and reactorCooldown then
126
    reactorCooldown = false
127
  end
128
    if reactorTemp > 1000 or reactorCooldown then
129
    newCR = 100
130
    reactorCooldown = true
131
  end
132-
  print("Control Rods: "..newCR.." Error: "..error)
132+
133
  reactor.setAllControlRodLevels(newCR)
134
  --print("Control Rods: "..newCR.." Error: "..error)
135
  --write("...")
136
  --write("P: "..P*error)
137
  --write("...")
138
  --write("  I: "..I*integral)
139
  --write("...")
140
  --print("  D: "..D*derivative)
141
  --print(newCR)
142
end
143
144
function doTurbineLogic()
145
  for k,v in ipairs(turbines) do
146
    local turbSpeed = v.getRotorSpeed()
147
    local flowRate = v.getFluidFlowRateMax()
148
    local maxFlowRate = v.getFluidFlowRateMaxMax()
149
    local safeShutdown = false
150
    local tooSlow = false
151
    local coils = v.getInductorEngaged()
152
153
    --Failsafes for turbines running too fast at shutdown (shouldn't happen)
154
    --or running too slow (during spin-up) to make sure coils engage/disengage at optimum range
155
    if turbSpeed > goalRPM + 50 then
156
      safeShutdown = false
157
    elseif turbSpeed < goalRPM - 10 then
158
      tooSlow = true
159
    else
160
      tooSlow = false
161
      safeShutdown = true
162
    end
163
    needEngaged = false
164
    needShutdown = false
165
166
    --does the system need power, or have too much?
167
    if table.monitor and table.currPower < 10000000 then
168
      needEngaged = true
169
      needShutdown = false
170
    elseif not table.montitor and table.currPower > shutdownPower then
171
      needEngaged = false
172
      needShutdown = true
173
    end
174
175
    --Adjust Steam Usage based on goalRPM (to make sure multiple-turbine setups split steam more evenly)
176
    if turbSpeed > goalRPM + 5 then
177
      slowdown[k] = true
178
    end
179
    if turbSpeed < goalRPM then
180
      slowdown[k] = false
181
    end
182
183
    --turn coils on/off based on conditions above
184
    if needEngaged and not tooSlow and not coils then
185
      v.setInductorEngaged(true)
186
    elseif needShutdown and safeShutdown and coils then
187
      v.setInductorEngaged(false)
188
    elseif tooSlow and coils then
189
      v.setInductorEngaged(false)
190
    end
191
192
    --turn flowrate for steam usage on/off based on goalRPM (helps keep RPMs even in multiple-turbine setups)
193
    if slowdown[k] then
194
      if flowRate ~= 0 then
195
        v.setFluidFlowRateMax(0)
196
      end
197
    else
198
      if flowRate ~= 2000 then
199
        local setRate = math.min(2000, maxFlowRate)
200
        v.setFluidFlowRateMax(setRate)
201
      end
202
    end
203
  end
204
end
205
206
function toint(num)  --removes decimals from strings
207
  return string.format("%d", tostring(num))
208
end  
209
210
function commaformat(num)  --puts a comma every 3 digits in a number (From Direwolf20)
211
  local formatted = num
212
  local neg = false
213
  if formatted < 0 then
214
    formatted = math.abs(formatted)
215
    neg = true
216
  end
217
  while true do
218
    formatted, k = string.gsub(formatted, "^(%d+)(%d%d%d)", '%1,%2')
219
    if k == 0 then
220
      break
221
    end
222
  end
223
  if neg then
224
    formatted = "-"..formatted
225
  end
226
  
227
  return formatted
228
end
229
    
230
231
function displayHeader()
232
--print("Updating displayHeader")
233
  mon.setTextScale(1)
234
  mon.setBackgroundColor(colors.black)
235
  mon.setTextColor(colors.white)
236
  mon.setCursorPos(1,1)
237
  mon.clearLine()
238
  
239
  --Start First Line of Header
240
  mon.write("Average RPM: ")
241
  if error > 4 or error < -4 then
242
    mon.setTextColor(colors.red)
243
  else
244
    mon.setTextColor(colors.green)
245
  end
246
  mon.write(toint(turbineSpeed))
247
  local x, y = mon.getSize()
248
  local middle = (x/2) + 3
249
  mon.setTextColor(colors.white)
250
  mon.setCursorPos(middle, 1)
251
  mon.write("Control Rods: ")
252
  if math.ceil(newCR) == 100 then
253
    mon.setTextColor(colors.green)
254
  else
255
    mon.setTextColor(colors.red)
256
  end
257
  mon.write(toint(math.ceil(newCR)))
258
  mon.write("%")
259
260
  --start second line of Header
261
  mon.setTextColor(colors.white)
262
  mon.setCursorPos(1,2)
263
  mon.clearLine()
264
  mon.write("Goal RPM: "..goalRPM)
265
  mon.setCursorPos(middle, 2)
266
  mon.write("Fuel Usage: ")
267
  local fuelUse = reactor.getFuelConsumedLastTick()
268
  fuelUse = math.floor(fuelUse*100)
269
  fuelUse = fuelUse/100
270
  if fuelUse > 0 then
271
    mon.setTextColor(colors.red)
272
  else
273
    mon.setTextColor(colors.green)
274
  end
275
  mon.write(fuelUse.."mb/t")
276
277
  --start third line of Header
278
  mon.setTextColor(colors.white)
279
  mon.setCursorPos(1,3)
280
  mon.clearLine()
281
  mon.write("Turbines Active: ")
282
  mon.write(toint(countTurbines).." of "..totalTurbines)
283
  mon.setTextColor(colors.white)
284
  mon.setCursorPos(middle, 3)
285
  mon.write("Fuel Level: ")
286
  local fuelLevel = (reactor.getFuelAmount() / reactor.getFuelAmountMax()) * 100
287
  if fuelLevel > 99 then
288
    mon.setTextColor(colors.green)
289
  else
290
    mon.setTextColor(colors.red)
291
  end
292
  mon.write(fuelLevel.."%")
293
294
  --start fourth line of Header
295
  mon.setTextColor(colors.white)
296
  mon.setCursorPos(1,4)
297
  mon.clearLine()
298
  mon.write("Power Produced: ")
299
  local totalEnergyProduced = 0
300
  for k,v in ipairs(turbines) do
301
    totalEnergyProduced = totalEnergyProduced + v.getEnergyProducedLastTick()
302
  end
303
  if totalEnergyProduced > (23500 * totalTurbines) then
304
    mon.setTextColor(colors.green)
305
  else
306
    mon.setTextColor(colors.red)
307
  end
308
  totalEnergyProduced = toint(totalEnergyProduced)
309
  totalEnergyProduced = tonumber(totalEnergyProduced)
310
  mon.write(commaformat(totalEnergyProduced).."RF/t")
311
  mon.setTextColor(colors.white)
312
  mon.setCursorPos(middle, 4)
313
  mon.write("Core Temp: ")
314
  local coreTemp = reactor.getFuelTemperature()
315
  if coreTemp > 750 then
316
    mon.setTextColor(colors.red)
317
  elseif coreTemp < 250 then
318
    mon.setTextColor(colors.green)
319
  else
320
    mon.setTextColor(colors.orange)
321
  end
322
  mon.write(math.floor(coreTemp).."c")
323
end
324
325
function displayTurbines(page)
326
  local turbineStartNum = (page * 4) + 1
327
  local turbinesOnPage = totalTurbines - turbineStartNum + 1
328
  if turbinesOnPage > 4 then
329
    turbinesOnPage = 4
330
  end
331
  if turbinesOnPage < 1 then
332
    turbinesOnPage = 1
333
  end
334
  local x,y = mon.getSize()
335
  local xInterval = math.floor(x/(turbinesOnPage + 1))
336
  local turbinePagePeriph = {}
337
  local temp = 1
338
  for i = turbineStartNum, (turbineStartNum + turbinesOnPage - 1) do
339
    turbinePagePeriph[temp] = turbines[i]
340
    --print(i.." "..turbines[i])
341
    temp = temp + 1
342
  end
343
  --clear text variables of Turbines
344
  --mon.setCursorPos(1,5)
345
  --mon.clearLine()
346
  mon.setCursorPos(1,18)
347
  mon.clearLine()
348
  mon.setCursorPos(1,19)
349
  mon.clearLine()
350
351
  for k,v in ipairs(turbinePagePeriph) do
352
    --display Turbine info on Monitor
353
    local xCenter = (k * xInterval)
354
355
    --display Turbine Title (Turbine #)
356
    local turbineTitle = "Turbine "..tostring(k + turbineStartNum - 1)
357
    local titleXStart = xCenter - math.floor(string.len(turbineTitle) / 2)
358
    mon.setTextColor(colors.lightBlue)
359
    mon.setCursorPos(titleXStart, 5)
360
    mon.write(turbineTitle)
361
362
    --display individual turbine RPMs
363
    local turbineRPM = toint(v.getRotorSpeed())
364
    local RPMXStart = xCenter - math.floor(string.len(tostring(turbineRPM)) / 2)
365
    mon.setCursorPos(RPMXStart, 18) 
366
    if goalRPM - turbineRPM > 4 or goalRPM - turbineRPM < -4 then
367
      mon.setTextColor(colors.red)
368
    else
369
      mon.setTextColor(colors.green)
370
    end
371
    mon.write(turbineRPM)
372
373
    --display individual turbine power outputs
374
    local powerMade = v.getEnergyProducedLastTick()
375
    if powerMade > 0 then
376
      mon.setTextColor(colors.green)
377
    else
378
      mon.setTextColor(colors.red)
379
    end
380
    powerMade = toint(powerMade)
381
    powerMade = tonumber(powerMade)
382
    powerMade = commaformat(powerMade).."RF"
383
    local powerXStart = xCenter - math.floor(string.len(powerMade) / 2)
384
    mon.setCursorPos(powerXStart, 19)
385
    mon.write(powerMade)
386
  end
387
388
  --display turbine graphics/coils
389
  for k,v in ipairs(turbinePagePeriph) do
390
    local xCenter = (k * xInterval) 
391
    local turbineXStart = xCenter - 3
392
    local coilColor
393
    if v.getInductorEngaged() then
394
      coilColor = colors.red
395
    else
396
      coilColor = colors.blue
397
    end
398
    mon.setBackgroundColor(colors.gray)
399
    mon.setCursorPos(turbineXStart, 6)
400
    mon.write("       ")
401
    for i=7,13 do
402
      mon.setCursorPos(turbineXStart, i)
403
      mon.setBackgroundColor(colors.gray)
404
      mon.write(" ")
405
      mon.setBackgroundColor(colors.lightGray)
406
      mon.write(" ")
407
      if i % 2 == 0 then
408
        mon.setBackgroundColor(colors.gray)
409
      end
410
      mon.write(" ")
411
      mon.setBackgroundColor(colors.gray)
412
      mon.write(" ")
413
      if i % 2 ~= 0 then
414
        mon.setBackgroundColor(colors.lightGray)
415
      end
416
      mon.write(" ")
417
      mon.setBackgroundColor(colors.lightGray)
418
      mon.write(" ")
419
      mon.setBackgroundColor(colors.gray)
420
      mon.write(" ")
421
    end
422
    for i=14,16 do
423
      mon.setCursorPos(turbineXStart, i)
424
      mon.setBackgroundColor(colors.gray)
425
      mon.write(" ")
426
      mon.setBackgroundColor(colors.lightGray)
427
      mon.write(" ")
428
      mon.setBackgroundColor(coilColor)
429
      mon.write(" ")
430
      mon.setBackgroundColor(colors.gray)
431
      mon.write(" ")
432
      mon.setBackgroundColor(coilColor)
433
      mon.write(" ")
434
      mon.setBackgroundColor(colors.lightGray)
435
      mon.write(" ")
436
      mon.setBackgroundColor(colors.gray)
437
      mon.write(" ")
438
    end
439
  mon.setCursorPos(turbineXStart, 17)
440
  mon.write("       ")
441
  mon.setBackgroundColor(colors.black)
442
  end
443
end
444
445
--change Turbine page
446
function turnPage(num)
447
  turbinePage = num
448
  mon.clear()
449
  displayScreen()
450
end
451
452
--show buttons to change pages
453
function displayPageButtons(page)
454
  button.clearTable()
455
  mon.setTextColor(colors.black)
456
  if page > 0 then
457
    local param = page - 1
458
    button.setTable("<<", turnPage, param, 1, 3, 10, 12)
459
  end
460
  if page < (pages-1) then
461
    local param = page + 1
462
    button.setTable(">>", turnPage, param, 48, 50, 10, 12)
463
  end
464
  button.screen()
465
end
466
467
function displayScreen()
468
  displayHeader()
469
  displayTurbines(turbinePage)
470
  if pages > 1 and lastPage ~= turbinePage then  --only refresh buttons if page changes
471
    displayPageButtons(turbinePage)
472
    lastPage = turbinePage
473
  end
474-
os.startTimer(.1)
474+
475
476
displayScreen()
477
os.startTimer(1)
478
479
while true do
480
  local event, param1, param2, param3 = os.pullEvent()
481-
    os.startTimer(.1)
481+
  --print("Outer loop called event: ".. event)
482
  --do timed events, restart timer
483
  if event == "timer" then
484
    os.startTimer(1)
485
    
486
    PID()
487
    if not (table.monitor == nil) then
488
      doTurbineLogic()
489
    end
490
    timerCounter = timerCounter + 1
491
   --print("timerCounter = ".. timerCounter)
492
    --update display every half second
493
    if timerCounter % 5 == 0 then
494
--print("timerCounter % 5 is 0")
495
      timerCounter = 0
496
      displayScreen()
497
    end
498
  end
499
  
500
  --import data from Capacitor Relay Computer
501
  if event == "rednet_message" then
502
    table = textutils.unserialize(param2)
503
  end
504
  
505
  --parse monitor clicks to API
506
  if event == "monitor_touch" then
507
    button.checkxy(param2, param3)
508
  end
509
end