CAHbl4

Power Control

Sep 17th, 2021 (edited)
434
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Power Control script for GregTech batteries
  2. -- Author: CAHCAHbl4
  3. -- Version: 2.0.4
  4. -- License: MIT
  5.  
  6. -- The script supports plenty of GT energy storage in any combination.
  7. -- https://i.imgur.com/rMFNnXB.gif
  8.  
  9. -- Setup: place OpenComputers Adapter next to Battery Buffer or multiblock controller
  10. --        and connect to the computer with cables.
  11. local colors = require("colors")
  12.  
  13. local settings = {
  14.   screenRefreshInterval = 0.2, -- In seconds. Supports values down to 0.05.
  15.   batteryPollInterval = 1, -- In seconds. Doesn't make sense to set lower than 1.
  16.   listSize = 20, -- The size of the historical data. 20 * 1(batteryPollInterval) = 20 seconds of history.
  17.   useMedian = false, -- If enabled script will use median instead of average to calculate In/Out.
  18.   redstoneControl = true, -- Enable redstone control behavior.
  19.   allowMultiply = false, -- Redstone control may work unproperly for multiply battery monitoring. Enable it at your own risk.
  20.   summaryOnly = false -- If enabled script will display a single screen with summed up values.
  21. }
  22.  
  23. local redstone = {
  24.   levels = {
  25.     green = {start = 80, stop = 100},
  26.     yellow = {start = 50, stop = 80, color = colors.yellow, frequency = 1001},
  27.     red = {start = 30, stop = 50, color = colors.red, frequency = 1002}
  28.   }
  29. }
  30.  
  31. local palette = {
  32.   w = 0xFFFFFF, -- white
  33.   bk = 0x000000, -- black
  34.   r = 0xCC0000, -- red
  35.   g = 0x009200, -- green
  36.   b = 0x0000C0, -- blue
  37.   y = 0xFFDB00 -- yellow
  38. }
  39.  
  40. local template = {
  41.   width = 32,
  42.   background = palette.bk,
  43.   foreground = palette.w,
  44.   chargebar = {
  45.     background = palette.b,
  46.     charge = palette.g,
  47.     symbol = palette.w,
  48.     arrows = 5,
  49.     levels = {
  50.       green = palette.g,
  51.       yellow = palette.y,
  52.       red = palette.r
  53.     }
  54.   },
  55.   lines = {
  56.     "$name$?totalNum>1 and name~='Summary'| $num:s,%d$|?:  $percent:s,%.2f$%  $stored:si,EU$",
  57.     "In: &g;$input:si,EU/t$&w; Out: &r;$output:si,EU/t$",
  58.     "          ?charge>=0|&g;|&r;?$charge:si,EU/t$",
  59.     "#chargebar#",
  60.     "?percent>=99.9|         &g;Fully charged|?" ..
  61.       "?percent==0|     &r;Completely discharged|?" ..
  62.         "?(percent<99.9 and charge>0)|Time to full:  &g;$left:t,2$|?" ..
  63.           "?(percent<99.9 and charge<0)|Time to empty:  &r;$left:t,2$|?"
  64.   }
  65. }
  66.  
  67. -- Template description
  68. -- $<value>:<formatter,arg1,arg2,...>$ - Render value using formatter. Formatter can be ommited.
  69. --                                       Default formater is string.
  70. --
  71. -- Available values:
  72. -- * name - Name of the battery.
  73. -- * num - Current battery number.
  74. -- * totalNum - Total number of batteries.
  75. -- * stored - Amount of total EU stored.
  76. -- * capacity - Amount of maximum EU can be stored.
  77. -- * percent - Current level of EU stored.
  78. -- * input - Current total input in EU/t.
  79. -- * output - Current total output in EU/t.
  80. -- * charge - Absolute value of current charge rate in EU/t.
  81. -- * level - Current redstone alert level.
  82. --
  83. -- Available formatters:
  84. -- * s - String formatter. Arguments - [format:string]
  85. -- * si - SI (System International) formatter. Arguments - [unit:string], [format:string]
  86. -- * t - Time span formatter. Arguments - [parts:number]
  87. --
  88. -- &<color:palette or RGB>; - Change foreground color.
  89. -- &&<color:palette or RGB>; - Change background color.
  90. -- ?<condition>|<true>|<false>? - Evaluate condition and insert value based on that.
  91.  
  92. -----------------------------------------------------------
  93. local event = require("event")
  94. local computer = require("computer")
  95. local component = require("component")
  96. local thread = require("thread")
  97. local sides = require("sides")
  98. local gpu = component.gpu
  99.  
  100. function split(string, delimiter)
  101.   local splitted = {}
  102.   for match in string:gmatch("([^" .. delimiter .. "]+)") do
  103.     table.insert(splitted, match)
  104.   end
  105.   return splitted
  106. end
  107.  
  108. function tablelength(table)
  109.   local count = 0
  110.   for _ in pairs(table) do
  111.     count = count + 1
  112.   end
  113.   return count
  114. end
  115.  
  116. ----------------------------------------------------------------------
  117. local formatters = {
  118.   s = function(value, format)
  119.     format = (format and format or "%.2f")
  120.  
  121.     return string.format(format, value)
  122.   end,
  123.   si = function(value, unit, format)
  124.     format = (format and format or "%.2f")
  125.     local incPrefixes = {"k", "M", "G", "T", "P", "E", "Z", "Y"}
  126.     local decPrefixes = {"m", "μ", "n", "p", "f", "a", "z", "y"}
  127.  
  128.     local prefix = ""
  129.     local scaled = value
  130.  
  131.     if value ~= 0 then
  132.       local degree = math.floor(math.log(math.abs(value), 10) / 3)
  133.       scaled = value * 1000 ^ -degree
  134.       if degree > 0 then
  135.         prefix = incPrefixes[degree]
  136.       elseif degree < 0 then
  137.         prefix = decPrefixes[-degree]
  138.       end
  139.     end
  140.  
  141.     return string.format(format, scaled) .. " " .. prefix .. (unit and unit or "")
  142.   end,
  143.   t = function(secs, parts)
  144.     parts = (parts and parts or 4)
  145.  
  146.     local units = {"d", "hr", "min", "sec"}
  147.     local result = {}
  148.     for i, v in ipairs({86400, 3600, 60}) do
  149.       if secs >= v then
  150.         result[i] = math.floor(secs / v)
  151.         secs = secs % v
  152.       end
  153.     end
  154.     result[4] = secs
  155.  
  156.     local resultString = ""
  157.     local i = 1
  158.     while parts ~= 0 and i ~= 5 do
  159.       if result[i] and result[i] > 0 then
  160.         if i > 1 then
  161.           resultString = resultString .. " "
  162.         end
  163.         resultString = resultString .. result[i] .. " " .. units[i]
  164.         parts = parts - 1
  165.       end
  166.       i = i + 1
  167.     end
  168.     return resultString
  169.   end
  170. }
  171.  
  172. ----------------------------------------------------------------------
  173. local widgets = {
  174.   chargebar = function(template)
  175.     local state = {}
  176.     state.template = template
  177.     state.center = math.ceil(template.width / 2)
  178.     state.tick = 1
  179.  
  180.     local getChar = function(charge, tick, i)
  181.       local char, start, charging
  182.       if charge > 0 then
  183.         start = state.center - math.ceil(template.chargebar.arrows / 2)
  184.         char = ">"
  185.         charging = true
  186.       elseif charge < 0 then
  187.         start = state.center + math.ceil(template.chargebar.arrows / 2)
  188.         char = "<"
  189.         charging = false
  190.       else
  191.         return " "
  192.       end
  193.  
  194.       if not charging and (i >= start - tick) and (i < start) then
  195.         return char
  196.       elseif charging and (i <= start + tick) and (i > start) then
  197.         return char
  198.       end
  199.       return " "
  200.     end
  201.  
  202.     return function(values)
  203.       local level = math.ceil(template.width * (values.percent / 100))
  204.       local maxDepth = gpu.maxDepth()
  205.  
  206.       local result
  207.  
  208.       if maxDepth == 1 then
  209.         result = "&&0xFFFFFF;&0x000000;"
  210.       else
  211.         result = "&" .. template.chargebar.symbol .. ";&&" .. template.chargebar.levels[values.level] .. ";"
  212.       end
  213.  
  214.       for i = 1, template.width do
  215.         if i > level then
  216.           if maxDepth == 1 then
  217.             result = result .. "&&0x000000;&0xFFFFFF;"
  218.           else
  219.             result = result .. "&&" .. template.chargebar.background .. ";"
  220.           end
  221.         end
  222.         result = result .. getChar(values.charge, state.tick, i)
  223.       end
  224.  
  225.       if state.tick == template.chargebar.arrows then
  226.         state.tick = 1
  227.       else
  228.         state.tick = state.tick + 1
  229.       end
  230.  
  231.       return result
  232.     end
  233.   end
  234. }
  235.  
  236. ----------------------------------------------------------------------
  237. List = {}
  238. function List.new(size)
  239.   local obj = {}
  240.   local list = {}
  241.   local count = 0
  242.  
  243.   function obj.push(value)
  244.     if count == 0 then
  245.       list[1] = value
  246.       count = count + 1
  247.     elseif #list < size then
  248.       list[#list + 1] = value
  249.       count = count + 1
  250.     else
  251.       for i = 1, #list do
  252.         if i < size then
  253.           list[i] = list[i + 1]
  254.         else
  255.           list[i] = value
  256.         end
  257.       end
  258.     end
  259.   end
  260.  
  261.   function obj.average()
  262.     if count == 0 then
  263.       return 0
  264.     end
  265.  
  266.     local result = 0
  267.     for _, v in ipairs(list) do
  268.       if type(v) == "number" then
  269.         result = result + v
  270.       end
  271.     end
  272.     return result / count
  273.   end
  274.  
  275.   function obj.median()
  276.     if count == 0 then
  277.       return 0
  278.     end
  279.  
  280.     local temp = {}
  281.  
  282.     for _, v in ipairs(list) do
  283.       if type(v) == "number" then
  284.         table.insert(temp, v)
  285.       end
  286.     end
  287.  
  288.     table.sort(temp)
  289.  
  290.     if math.fmod(#temp, 2) == 0 then
  291.       return (temp[#temp / 2] + temp[(#temp / 2) + 1]) / 2
  292.     else
  293.       return temp[math.ceil(#temp / 2)]
  294.     end
  295.   end
  296.  
  297.   return obj
  298. end
  299.  
  300. ----------------------------------------------------------------------
  301. SensorProxyDecorator = {}
  302. function SensorProxyDecorator.new(proxy)
  303.   function proxy.getSensorValue(c)
  304.     local info = proxy.getSensorInformation()
  305.     local line = info[c.line]
  306.     local match = line:match(c.pattern)
  307.     local cleaned = match:gsub("%D", "")
  308.     local result = tonumber(cleaned)
  309.     return result
  310.   end
  311.  
  312.   return proxy
  313. end
  314.  
  315. ----------------------------------------------------------------------
  316. GenericGTBlock = {}
  317. function GenericGTBlock.new(proxy, name)
  318.   local obj = {}
  319.  
  320.   obj.name = name or "Unknown"
  321.   obj.proxy = proxy
  322.   obj.inputHistory = List.new(settings.listSize)
  323.   obj.outputHistory = List.new(settings.listSize)
  324.  
  325.   function obj.runMonitoring()
  326.     while true do
  327.       obj.inputHistory.push(obj.proxy.getEUInputAverage())
  328.       obj.outputHistory.push(obj.proxy.getEUOutputAverage())
  329.       os.sleep(settings.batteryPollInterval)
  330.     end
  331.   end
  332.  
  333.   function obj.getStored()
  334.     return obj.proxy.getEUStored()
  335.   end
  336.  
  337.   function obj.getCapacity()
  338.     return obj.proxy.getEUMaxStored()
  339.   end
  340.  
  341.   function obj.getInput()
  342.     if settings.useMedian then
  343.       return obj.inputHistory.median()
  344.     else
  345.       return obj.inputHistory.average()
  346.     end
  347.   end
  348.  
  349.   function obj.getOutput()
  350.     if settings.useMedian then
  351.       return obj.outputHistory.median()
  352.     else
  353.       return obj.outputHistory.average()
  354.     end
  355.   end
  356.  
  357.   return obj
  358. end
  359.  
  360. ----------------------------------------------------------------------
  361. BatBuffer = {}
  362. function BatBuffer.new(proxy, name)
  363.   local obj = GenericGTBlock.new(proxy, name)
  364.  
  365.   local config = {
  366.     -- "§a1 199 934§r EU / §e1 232 768§r EU"
  367.     STORED = {line = 3, pattern = "§.(.+)§.+/.+$"},
  368.     CAPACITY = {line = 3, pattern = "^.+/.+§.(.+)§.+$"},
  369.     -- "32 768 EU/t"
  370.     INPUT = {line = 5, pattern = "^(.+)%sEU/t$"},
  371.     OUTPUT = {line = 7, pattern = "^(.+)%sEU/t$"}
  372.   }
  373.  
  374.   function obj.runMonitoring()
  375.     while true do
  376.       obj.inputHistory.push(obj.proxy.getSensorValue(config.INPUT))
  377.       obj.outputHistory.push(obj.proxy.getSensorValue(config.OUTPUT))
  378.       os.sleep(settings.batteryPollInterval)
  379.     end
  380.   end
  381.  
  382.   function obj.getStored()
  383.     return obj.proxy.getSensorValue(config.STORED)
  384.   end
  385.  
  386.   function obj.getCapacity()
  387.     return obj.proxy.getSensorValue(config.CAPACITY)
  388.   end
  389.  
  390.   return obj
  391. end
  392.  
  393. ----------------------------------------------------------------------
  394. Substation = {}
  395. function Substation.new(proxy, name)
  396.   local obj = GenericGTBlock.new(proxy, name)
  397.  
  398.   local config = {
  399.     -- "Stored EU: §a1 275 992 701§r"
  400.     STORED = {line = 3, pattern = "^.+§.(.+)§.+$"},
  401.     CAPACITY = {line = 4, pattern = "^.+§.(.+)§.+$"},
  402.     -- "Total Input: §91 352 912 896§r EU",
  403.     TOTAL_INPUT = {line = 10, pattern = "^.+§.(.+)§.+$"},
  404.     TOTAL_OUTPUT = {line = 11, pattern = "^.+§.(.+)§.+$"},
  405.     TOTAL_COSTS = {line = 12, pattern = "^.+§.(.+)§.+$"}
  406.   }
  407.   local lastTotalInput = 0
  408.   local lastTotalOutput = 0
  409.  
  410.   function obj:runMonitoring()
  411.     while true do
  412.       local currentTotalInput = obj.proxy.getSensorValue(config.TOTAL_INPUT)
  413.       local currentTotalOutput = obj.proxy.getSensorValue(config.TOTAL_OUTPUT)
  414.       local currentTotalCosts = obj.proxy.getSensorValue(config.TOTAL_COSTS)
  415.  
  416.       local currentInput = (currentTotalInput - lastTotalInput) / (settings.batteryPollInterval * 20)
  417.       local currentOutput =
  418.         (currentTotalOutput + currentTotalCosts - lastTotalOutput) / (settings.batteryPollInterval * 20)
  419.  
  420.       if lastTotalInput ~= 0 then
  421.         obj.inputHistory.push(currentInput)
  422.         obj.outputHistory.push(currentOutput)
  423.       end
  424.  
  425.       lastTotalInput = currentTotalInput
  426.       lastTotalOutput = currentTotalOutput + currentTotalCosts
  427.       os.sleep(settings.batteryPollInterval)
  428.     end
  429.   end
  430.  
  431.   function obj.getStored()
  432.     return obj.proxy.getSensorValue(config.STORED)
  433.   end
  434.  
  435.   function obj.getCapacity()
  436.     return obj.proxy.getSensorValue(config.CAPACITY)
  437.   end
  438.  
  439.   return obj
  440. end
  441.  
  442. ----------------------------------------------------------------------
  443. LESU = {}
  444. function LESU.new(proxy, name)
  445.   local obj = GenericGTBlock.new(proxy, name)
  446.  
  447.   local baseGetCapacity = obj.getCapacity
  448.   function obj.getCapacity()
  449.     return baseGetCapacity() / 2
  450.   end
  451.  
  452.   return obj
  453. end
  454.  
  455. ----------------------------------------------------------------------
  456. MFSU = {}
  457. function MFSU.new(proxy, name)
  458.   local obj = GenericGTBlock.new(proxy, name)
  459.   local lastStored
  460.  
  461.   function obj.runMonitoring()
  462.     while true do
  463.       local currentStored = obj.proxy.getStored()
  464.  
  465.       if lastStored then
  466.         if currentStored > lastStored then
  467.           obj.inputHistory.push((currentStored - lastStored) / (settings.batteryPollInterval * 20))
  468.           obj.outputHistory.push(0)
  469.         elseif currentStored < lastStored then
  470.           obj.inputHistory.push(0)
  471.           obj.outputHistory.push((lastStored - currentStored) / (settings.batteryPollInterval * 20))
  472.         else
  473.           obj.inputHistory.push(0)
  474.           obj.outputHistory.push(0)
  475.         end
  476.       end
  477.  
  478.       lastStored = currentStored
  479.       os.sleep(settings.batteryPollInterval)
  480.     end
  481.   end
  482.  
  483.   function obj.getStored()
  484.     return obj.proxy.getStored()
  485.   end
  486.  
  487.   function obj.getCapacity()
  488.     return obj.proxy.getCapacity()
  489.   end
  490.  
  491.   return obj
  492. end
  493.  
  494. ----------------------------------------------------------------------
  495. ScreenController = {}
  496. function ScreenController.new(template)
  497.   local obj = {}
  498.   local width, height = template.width, #template.lines
  499.   gpu.setResolution(width, height)
  500.  
  501.   local _widgets = {}
  502.   for k, v in pairs(widgets) do
  503.     _widgets[k] = v(template)
  504.   end
  505.  
  506.   function obj.evaluateConditions(line, values)
  507.     return string.gsub(
  508.       line,
  509.       "?(.-)?",
  510.       function(pattern)
  511.         local condition, left, right = pattern:match("^(.*)|(.*)|(.*)$")
  512.         local f = ""
  513.         for key, value in pairs(values) do
  514.           f = f .. "local " .. key .. "="
  515.           if type(value) == "string" then
  516.             f = f .. "'" .. value .. "'\n"
  517.           else
  518.             f = f .. value .. "\n"
  519.           end
  520.         end
  521.         f = f .. "return " .. condition
  522.         f = load(f)
  523.         if f then
  524.           local result = f()
  525.           return result and left or right
  526.         end
  527.       end
  528.     )
  529.   end
  530.  
  531.   function obj.evaluateValues(line, values)
  532.     return string.gsub(
  533.       line,
  534.       "%$(.-)%$",
  535.       function(pattern)
  536.         local formatter
  537.         local variable, args = pattern:match("^(.+):(.+)$")
  538.         if not variable then
  539.           variable = pattern
  540.           formatter = "s"
  541.           args = {"%s"}
  542.         else
  543.           args = split(args, ",")
  544.           formatter = args[1]
  545.           table.remove(args, 1)
  546.         end
  547.  
  548.         if formatter then
  549.           return formatters[formatter](values[variable], table.unpack(args))
  550.         end
  551.         return values[variable]
  552.       end
  553.     )
  554.   end
  555.  
  556.   function obj.evaluateWidgets(line, values)
  557.     return string.gsub(
  558.       line,
  559.       "#(.-)#",
  560.       function(pattern)
  561.         local name, args = pattern:match("^(.+):(.+)$")
  562.  
  563.         if not name then
  564.           name = pattern
  565.           args = {}
  566.         else
  567.           args = split(args, ",")
  568.         end
  569.  
  570.         if _widgets[name] then
  571.           return _widgets[name](values, args)
  572.         end
  573.       end
  574.     )
  575.   end
  576.  
  577.   function obj.render(values)
  578.     local buffer = gpu.allocateBuffer(width, height)
  579.     gpu.setActiveBuffer(buffer)
  580.     local i = 1
  581.     for _, line in pairs(template.lines) do
  582.       gpu.setBackground(template.background)
  583.       gpu.setForeground(template.foreground)
  584.  
  585.       local rendered = obj.evaluateConditions(line, values)
  586.       rendered = obj.evaluateValues(rendered, values)
  587.       rendered = obj.evaluateWidgets(rendered, values)
  588.  
  589.       local j, k = 1, 1
  590.  
  591.       while j <= #rendered do
  592.         local c = rendered:sub(j, j)
  593.         if c == "&" then
  594.           local cstr = ""
  595.           local bg = false
  596.  
  597.           if rendered:sub(j + 1, j + 1) == "&" then
  598.             bg = true
  599.             j = j + 1
  600.           end
  601.  
  602.           repeat
  603.             j = j + 1
  604.             local next = rendered:sub(j, j)
  605.             if next ~= ";" then
  606.               cstr = cstr .. next
  607.             end
  608.           until next == ";"
  609.           local color
  610.  
  611.           if palette[cstr] then
  612.             color = palette[cstr]
  613.           else
  614.             local hex = tonumber(cstr)
  615.             if hex then
  616.               color = hex
  617.             end
  618.           end
  619.  
  620.           if color then
  621.             if bg then
  622.               gpu.setBackground(color)
  623.             else
  624.               gpu.setForeground(color)
  625.             end
  626.           end
  627.  
  628.           j = j + 1
  629.         else
  630.           gpu.set(k, i, c)
  631.           k = k + 1
  632.           j = j + 1
  633.         end
  634.       end
  635.       i = i + 1
  636.     end
  637.     gpu.bitblt(0, 1, 1, width, height, buffer, 1, 1)
  638.     gpu.setActiveBuffer(0)
  639.     gpu.freeBuffer(buffer)
  640.   end
  641.  
  642.   function obj.reset()
  643.     local w, h = gpu.maxResolution()
  644.     gpu.freeAllBuffers()
  645.     gpu.setResolution(w, h)
  646.     gpu.fill(1, 1, w, h, " ")
  647.   end
  648.  
  649.   return obj
  650. end
  651.  
  652. ----------------------------------------------------------------------
  653. RedstoneController = {}
  654. function RedstoneController.new(frequencies, wirelessCards, bundledCards)
  655.   local obj = {}
  656.   local state = {}
  657.  
  658.   local getFirstUnasignedFrequency = function()
  659.     for _, frequency in pairs(frequencies) do
  660.       if not frequency.card then
  661.         return frequency
  662.       end
  663.     end
  664.   end
  665.  
  666.   if #frequencies > 0 then
  667.     for _, card in pairs(wirelessCards) do
  668.       local frequency = getFirstUnasignedFrequency()
  669.       card.setWirelessFrequency(frequency.value)
  670.       frequency.card = card
  671.     end
  672.   end
  673.  
  674.   for level, _ in pairs(redstone.levels) do
  675.     state[level] = false
  676.   end
  677.  
  678.   function obj.update(values)
  679.     local bundledColors, bundledOutput = {}, {}
  680.  
  681.     for level, config in pairs(redstone.levels) do
  682.       if values.percent <= config.start then
  683.         if config.frequency and not state[level] then
  684.           frequencies[level].card.setWirelessOutput(true)
  685.           state[level] = true
  686.         end
  687.  
  688.         if config.color then
  689.           bundledColors[config.color] = 255
  690.         end
  691.       elseif values.percent > config.stop then
  692.         if config.frequency and state[level] then
  693.           frequencies[level].card.setWirelessOutput(false)
  694.           state[level] = false
  695.         end
  696.  
  697.         if config.color then
  698.           bundledColors[config.color] = 0
  699.         end
  700.       end
  701.     end
  702.  
  703.     for i = 0, #sides - 1 do
  704.       bundledOutput[i] = bundledColors
  705.     end
  706.  
  707.     for _, card in pairs(bundledCards) do
  708.       card.setBundledOutput(bundledOutput)
  709.     end
  710.   end
  711.  
  712.   function obj.getLevel(values)
  713.     local currentLevel, lastStop = nil, 100
  714.  
  715.     for level, config in pairs(redstone.levels) do
  716.       if not currentLevel then
  717.         currentLevel = level
  718.       end
  719.  
  720.       if config.stop <= lastStop and values.percent <= config.stop then
  721.         currentLevel = level
  722.         lastStop = config.stop
  723.       end
  724.     end
  725.  
  726.     return currentLevel
  727.   end
  728.  
  729.   function obj.reset()
  730.     local bundledColors, bundledOutput = {}, {}
  731.  
  732.     for _, card in pairs(wirelessCards) do
  733.       card.setWirelessOutput(false)
  734.       card.setWirelessFrequency(0)
  735.     end
  736.  
  737.     for i = 0, #colors do
  738.       bundledColors[i] = 0
  739.     end
  740.  
  741.     for i = 0, #sides - 1 do
  742.       bundledOutput[i] = bundledColors
  743.     end
  744.  
  745.     for _, card in pairs(bundledCards) do
  746.       card.setBundledOutput(bundledOutput)
  747.     end
  748.   end
  749.  
  750.   return obj
  751. end
  752.  
  753. ----------------------------------------------------------------------
  754. function Main()
  755.   local lastUptime = computer.uptime()
  756.   local lastToggle = lastUptime
  757.   local num = 1
  758.  
  759.   local batteries = {}
  760.   local i = 1
  761.   for address, type in component.list() do
  762.     local proxy = SensorProxyDecorator.new(component.proxy(address))
  763.     if type == "gt_machine" then
  764.       local info = proxy.getSensorInformation()
  765.       if string.find(info[1], "substation") then
  766.         batteries[i] = Substation.new(proxy, "PSS")
  767.         i = i + 1
  768.       elseif string.find(info[1], "Progress") then
  769.         batteries[i] = LESU.new(proxy, "L.E.S.U.")
  770.         i = i + 1
  771.       elseif string.find(info[1], "Operational Data") then
  772.         batteries[i] = GenericGTBlock.new(proxy, "Lapatronic")
  773.         i = i + 1
  774.       end
  775.     elseif type == "gt_batterybuffer" then
  776.       batteries[i] = BatBuffer.new(proxy, "BattBuffer")
  777.       i = i + 1
  778.     elseif type == "mfsu" then
  779.       batteries[i] = MFSU.new(proxy, "MFSU")
  780.       i = i + 1
  781.     end
  782.   end
  783.  
  784.   local controller
  785.   if settings.redstoneControl then
  786.     local frequencies = {}
  787.     for key, level in pairs(redstone.levels) do
  788.       if level.frequency then
  789.         frequencies[key] = {value = level.frequency}
  790.       end
  791.     end
  792.  
  793.     local wireless, bundled = {}, {}
  794.     for address, _ in pairs(component.list("redstone")) do
  795.       local proxy = component.proxy(address)
  796.       if proxy.getWirelessInput then
  797.         table.insert(wireless, proxy)
  798.       end
  799.       if proxy.getBundledOutput then
  800.         table.insert(bundled, proxy)
  801.       end
  802.     end
  803.  
  804.     if tablelength(frequencies) > tablelength(wireless) then
  805.       print("Redstone setup requires " .. tablelength(frequencies) .. " wireless redstone cards.")
  806.       return -1
  807.     end
  808.  
  809.     if tablelength(bundled) < 1 then
  810.       print("Redstone setup requires at least 1 redstone component with bundled output.")
  811.       return -1
  812.     end
  813.  
  814.     controller = RedstoneController.new(frequencies, wireless, bundled)
  815.   else
  816.     controller = RedstoneController.new()
  817.   end
  818.  
  819.   if #batteries > 1 and not settings.allowMultiply then
  820.     print(
  821.       "Found multiply batteries connected. Redstone control may work unproperly. Change allowMultiply in settings to true if you want to proceed."
  822.     )
  823.     return -1
  824.   end
  825.  
  826.   for _, battery in ipairs(batteries) do
  827.     thread.create(
  828.       function()
  829.         battery:runMonitoring()
  830.       end
  831.     ):detach()
  832.   end
  833.  
  834.   local screen = ScreenController.new(template)
  835.  
  836.   repeat
  837.     local currentUptime = computer.uptime()
  838.  
  839.     if #batteries > 1 then
  840.       if currentUptime - lastToggle > settings.toggleInterval then
  841.         if num + 1 > #batteries then
  842.           num = -1
  843.         elseif num == -1 then
  844.           num = 1
  845.         else
  846.           num = num + 1
  847.         end
  848.         lastToggle = currentUptime
  849.       end
  850.     end
  851.  
  852.     local values = {
  853.       stored = 0,
  854.       capacity = 0,
  855.       percent = 0,
  856.       input = 0,
  857.       output = 0,
  858.       charge = 0,
  859.       num = num,
  860.       totalNum = #batteries
  861.     }
  862.  
  863.     local redstoneValues = {}
  864.  
  865.     for k, battery in ipairs(batteries) do
  866.       if settings.summaryOnly or num == -1 then
  867.         values.stored = values.stored + battery.getStored()
  868.         values.capacity = values.capacity + battery.getCapacity()
  869.         values.input = values.input + battery.getInput()
  870.         values.output = values.output + battery.getOutput()
  871.         values.name = "Summary"
  872.       elseif k == num then
  873.         values.stored = battery.getStored()
  874.         values.capacity = battery.getCapacity()
  875.         values.input = battery.getInput()
  876.         values.output = battery.getOutput()
  877.         values.name = battery.name
  878.       end
  879.       if settings.redstoneControl then
  880.         redstoneValues.stored = values.stored + battery.getStored()
  881.         redstoneValues.capacity = values.capacity + battery.getCapacity()
  882.         redstoneValues.input = values.input + battery.getInput()
  883.         redstoneValues.output = values.output + battery.getOutput()
  884.       end
  885.     end
  886.  
  887.     values.percent = values.stored / values.capacity * 100
  888.     values.charge = values.input - values.output
  889.  
  890.     if settings.redstoneControl then
  891.       redstoneValues.percent = values.stored / values.capacity * 100
  892.       redstoneValues.charge = values.input - values.output
  893.     end
  894.  
  895.     if values.charge > 0 then
  896.       values.left = (values.capacity - values.stored) / values.charge
  897.     elseif values.charge < 0 then
  898.       values.left = values.stored / -values.charge
  899.     else
  900.       values.left = 0
  901.     end
  902.     values.left = math.floor(values.left / 20)
  903.  
  904.     if settings.redstoneControl then
  905.       controller.update(redstoneValues)
  906.     end
  907.  
  908.     values.level = controller.getLevel(values)
  909.     screen.render(values)
  910.  
  911.     lastUptime = currentUptime
  912.   until event.pull(settings.screenRefreshInterval, "interrupted")
  913.   controller.reset()
  914.   screen.reset()
  915. end
  916.  
  917. Main()
  918.  
RAW Paste Data