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 |