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