LezChap

Mekanism Induction Matrix Power Monitor for OC v3

May 1st, 2019
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.65 KB | None | 0 0
  1. local component = require("component")
  2. local event = require("event")
  3. local term = require("term")
  4. local computer = require("computer")
  5.  
  6. local sides = {
  7.   TOP = 1,
  8.   BOTTOM = 2,
  9.   LEFT = 3,
  10.   RIGHT = 4,
  11.   "TOP",
  12.   "BOTTOM",
  13.   "LEFT",
  14.   "RIGHT"
  15. }
  16.  
  17. local function patch(base, head)
  18.   for k, v in pairs(head) do
  19.     if type(v) ~= "table" or type(base[k]) ~= "table" then
  20.       base[k] = v
  21.     else
  22.       patch(base[k], v)
  23.     end
  24.   end
  25. end
  26.  
  27. local Histogram
  28. do
  29.   local characters = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
  30.   local meta = {}
  31.   meta.__index = meta
  32.  
  33.   local function calcHeight(height, perc)
  34.     return math.floor(perc * height * 8)
  35.   end
  36.  
  37.   local function getBarChars(height, halfHeight, totalHeight)
  38.     if height < 0 then
  39.       height = -height
  40.       local spaces = math.floor(height / 8)
  41.       local part = characters[8 - (height - spaces * 8) + 1]
  42.       if spaces * 8 == height then
  43.         part = ""
  44.       end
  45.       local blocks = totalHeight - halfHeight
  46.       return characters[9]:rep(blocks) .. characters[1]:rep(spaces) .. part
  47.     end
  48.     local blocks = math.floor(height / 8)
  49.     local part = characters[height - blocks * 8 + 1]
  50.     if blocks * 8 == height then
  51.       part = ""
  52.     end
  53.     local spaces = halfHeight - blocks - (part ~= "" and 1 or 0)
  54.     return characters[1]:rep(spaces) .. part .. characters[9]:rep(blocks)
  55.   end
  56.  
  57.   function meta:draw(container)
  58.     if self.max == self.min and self.max == 0 then
  59.       error("min and max are both 0!")
  60.     end
  61.     local loopStart, loopEnd = 1, container.width
  62.     if self.align == sides.RIGHT then
  63.       loopEnd = #self.values
  64.       loopStart = loopEnd - container.width + 1
  65.     end
  66.     local max = self.max - self.min
  67.     local min = 0
  68.     local bar = 1
  69.  
  70.     local levelY = self.level.y
  71.     if levelY > 0 and levelY < 1 then
  72.       levelY = levelY * container.height
  73.     elseif levelY < 0 then
  74.       levelY = container.height + levelY + 1
  75.     end
  76.     levelY = math.floor(levelY)
  77.     if levelY > container.height then
  78.       levelY = container.height
  79.     end
  80.  
  81.     local levelV = self.level.value or self.min
  82.     if levelV < self.min or levelV > self.max then
  83.       error("invalid level value set!")
  84.     end
  85.  
  86.     for i = loopStart, loopEnd do
  87.       local value = self.values[i] or levelV
  88.       if value < self.min or value > self.max then
  89.         error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. value)
  90.       end
  91.       local v = value - levelV
  92.       local halfH = v < 0 and levelY or container.height - levelY
  93.  
  94.       local perc
  95.       if v < 0 then
  96.         perc = (levelV + value) / (levelV - self.min)
  97.       else
  98.         perc = (value - levelV) / (self.max - levelV)
  99.       end
  100.       if v == 0 and max == 0 then
  101.         perc = 1
  102.       end
  103.  
  104.       local height = calcHeight(halfH, perc)
  105.       local chars = getBarChars(height, halfH, container.height)
  106.  
  107.       local fg, bg = self.colorFunc(i, perc, value, self, container)
  108.       fg = fg or container.fg
  109.       bg = bg or container.bg
  110.  
  111.       if v < 0 then
  112.         fg, bg = bg, fg
  113.       end
  114.  
  115.       if container.gpu.getForeground() ~= fg then
  116.         container.gpu.setForeground(fg)
  117.       end
  118.  
  119.       if container.gpu.getBackground() ~= bg then
  120.         container.gpu.setBackground(bg)
  121.       end
  122.  
  123.       if self.scaleChange or (#self.values < container.width) then
  124.         container.gpu.set(container:getX() + bar - 1,
  125.                           container:getY(),
  126.                           chars,
  127.                           true)
  128.         bar = bar + 1
  129.       else
  130.         if bar == 1 then
  131.           container.gpu.copy(container.x + 1,
  132.                              container.y,
  133.                              container.width - 1,
  134.                              container.height,
  135.                              -1,
  136.                              0)
  137.         end
  138.         if bar < 8 then
  139.           container.gpu.set(container:getX() + bar - 1,
  140.                             container:getY(),
  141.                             chars,
  142.                             true)
  143.         elseif loopEnd == bar then
  144.           container.gpu.set(container:getX() + bar - 1,
  145.                             container:getY(),
  146.                             chars,
  147.                             true)
  148.         end
  149.         bar = bar + 1
  150.       end
  151.     end
  152.     self.scaleChange = false
  153.   end
  154.  
  155.   Histogram = function(tbl)
  156.     local obj = {
  157.       style = "Histogram",
  158.       values = {},
  159.       align = sides.LEFT,
  160.       colorFunc = function() end,
  161.       min = 0,
  162.       max = 1,
  163.       scaleChange = false,
  164.       level = {
  165.         y = 0,
  166.         value = nil
  167.       }
  168.     }
  169.     if tbl then
  170.       patch(obj, tbl)
  171.     end
  172.     return setmetatable(obj, meta)
  173.   end
  174. end
  175.  
  176. local ProgressBar
  177. do
  178.   local meta = {}
  179.   meta.__index = meta
  180.   local charsV = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
  181.   local charsH = {" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"}
  182.  
  183.   local function calcLength(perc, max)
  184.     return math.floor(perc * 8 * max), max * 8
  185.   end
  186.  
  187.   local function getChars(direction, len, max)
  188.     if direction == sides.RIGHT then
  189.       local blocks = math.floor(len / 8)
  190.       local part = charsH[len - blocks * 8 + 1]
  191.       if blocks * 8 == len then
  192.         part = ""
  193.       end
  194.       local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
  195.       return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
  196.     elseif direction == sides.LEFT then
  197.       local spaces = math.floor(len / 8)
  198.       local part = charsH[9 - (len - spaces * 8)]
  199.       if spaces * 8 == len then
  200.         part = ""
  201.       end
  202.       local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
  203.       return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
  204.     elseif direction == sides.TOP then
  205.       local blocks = math.floor(len / 8)
  206.       local part = charsV[len - blocks * 8 + 1]
  207.       if blocks * 8 == len then
  208.         part = ""
  209.       end
  210.       local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
  211.       return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
  212.     elseif direction == sides.BOTTOM then
  213.       local spaces = math.floor(len / 8)
  214.       local part = charsV[9 - (len - spaces * 8)]
  215.       if spaces * 8 == len then
  216.         part = ""
  217.       end
  218.       local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
  219.       return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
  220.     end
  221.   end
  222.  
  223.   function meta:draw(container)
  224.     local min = 0
  225.     local max = self.max - self.min
  226.     if min == max and min == 0 then
  227.       error("min and max values are the same")
  228.     end
  229.     local value = self.value - self.min
  230.     if not (value >= min and value <= max) then
  231.       error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. self.value)
  232.     end
  233.     local perc = value / max
  234.     local maxLength = container.width
  235.     if self.direction == sides.TOP or self.direction == sides.BOTTOM then
  236.       maxLength = container.height
  237.     end
  238.     local chars = getChars(self.direction, calcLength(perc, maxLength))
  239.     local fg, bg = self.colorFunc(self.value, perc, self, container)
  240.     fg = fg or container.fg
  241.     bg = bg or container.bg
  242.     if self.direction == sides.LEFT or self.direction == sides.BOTTOM then
  243.       fg, bg = bg, fg
  244.     end
  245.     if container.gpu.getForeground() ~= fg then
  246.       container.gpu.setForeground(fg)
  247.     end
  248.     if container.gpu.getBackground() ~= bg then
  249.       container.gpu.setBackground(bg)
  250.     end
  251.     if self.direction == sides.TOP or self.direction == sides.BOTTOM then
  252.       for x = 1, container.width, 1 do
  253.         container.gpu.set(container:getX() + x - 1, container:getY(), chars, true)
  254.       end
  255.     else
  256.       for y = 1, container.height, 1 do
  257.         container.gpu.set(container:getX(), container:getY() + y - 1, chars)
  258.       end
  259.     end
  260.   end
  261.  
  262.   ProgressBar = function(tbl)
  263.     local obj = {
  264.       style = "ProgressBar",
  265.       direction = sides.RIGHT,
  266.       min = 0,
  267.       max = 1,
  268.       value = 0,
  269.       colorFunc = function(value, perc, prbar, container) end
  270.     }
  271.     if tbl then
  272.       patch(obj, tbl)
  273.     end
  274.     return setmetatable(obj, meta)
  275.   end
  276. end
  277.  
  278. local Container
  279. do
  280.   local meta = {}
  281.   meta.__index = meta
  282.  
  283.   function meta:draw()
  284.     if self.payload then
  285.       local fg = self.gpu.getForeground()
  286.       local bg = self.gpu.getBackground()
  287.       if fg ~= self.fg then
  288.         self.gpu.setForeground(self.fg)
  289.       end
  290.       if bg ~= self.bg then
  291.         self.gpu.setBackground(self.bg)
  292.       end
  293.      
  294.       if (self.payload.style == "ProgressBar") or (self.payload.style == "Histogram" and self.payload.scaleChange) then
  295.         self.gpu.fill(self.x, self.y, self.width, self.height, " ")
  296.       end
  297.       self.payload:draw(self)
  298.  
  299.       if self.gpu.getForeground() ~= fg then
  300.         self.gpu.setForeground(fg)
  301.       end
  302.       if self.gpu.getBackground() ~= bg then
  303.         self.gpu.setBackground(bg)
  304.       end
  305.     end
  306.   end
  307.  
  308.   function meta:getX()
  309.     return self.x + self.payloadX - 1
  310.   end
  311.  
  312.   function meta:getY()
  313.     return self.y + self.payloadY - 1
  314.   end
  315.  
  316.   Container = function(tbl)
  317.     local obj = {
  318.       gpu = component.gpu,
  319.       fg = 0xffffff,
  320.       bg = 0x000000,
  321.       x = 1,
  322.       y = 1,
  323.       payloadX = 1,
  324.       payloadY = 1,
  325.       width = 80,
  326.       height = 25,
  327.       payload = nil
  328.     }
  329.     if tbl then
  330.       patch(obj, tbl)
  331.     end
  332.     return setmetatable(obj, meta)
  333.   end
  334. end
  335.  
  336. term.clear()
  337. local gpu = term.gpu()
  338. gpu.setResolution(96, 30) --to make the text bigger, using a 3x2 monitor
  339.  
  340. local matrix = component.list("induction_matrix")
  341. for a, _ in pairs(matrix) do matrix = component.proxy(a) end
  342. local maxEnergy = math.floor(matrix.getMaxEnergy() / 2.5) --values are 2.5x higher than listed in the matrix gui for some reason, this fixes it
  343. local transferCap = math.floor(matrix.getTransferCap() / 2.5)
  344.  
  345. local powerBar = Container({
  346.     x = 1,
  347.     y = 1,
  348.     width = 96,
  349.     height = 5,
  350.     payload = ProgressBar({
  351.       value = 0,
  352.       max = maxEnergy,
  353.       colorFunc = function(_, perc )
  354.         if perc >= .9 then
  355.           return 0x00db00
  356.         end
  357.         if perc >= .75 then
  358.           return 0x99db00
  359.         end
  360.         if perc >= .5 then
  361.           return 0xffdb00
  362.         end
  363.         if perc >= .25 then
  364.           return 0xff4900
  365.         end
  366.         if perc >= .1 then
  367.           return 0xff0000
  368.         end
  369.       end
  370.     })
  371.   })
  372.  
  373. local inputHist = Container({
  374.   x = 1,
  375.   y = 6,
  376.   width = 96,
  377.   height = 12,
  378.   payload = Histogram({
  379.     colorFunc = function(index, norm, value, self, container)
  380.       return 0x0049ff
  381.     end
  382.   })
  383. })
  384.  
  385. local outputHist = Container({
  386.   x = 1,
  387.   y = 18,
  388.   width = 96,
  389.   height = 12,
  390.   payload = Histogram({
  391.     realVals = {},
  392.     level = { y = -12 },
  393.     colorFunc = function(index, norm, value, self, container)
  394.       return 0x000000, 0xff6d40
  395.     end
  396.   })
  397. })
  398.  
  399. function centerText(_str, y, x1, x2)  --function to write text centered between two X values
  400.   local x1 = x1 or 1  --if no values for x are given, assume full screen.
  401.   local x2 = x2 or 96
  402.   local start = math.floor(((x2 - x1) - #_str)/2) + x1
  403.   gpu.set(start, y, _str)
  404. end
  405.  
  406. function comma_value(amount) --format big numbers with commas.
  407.   local formatted = tostring(amount)
  408.   while true do  
  409.     formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
  410.     if (k==0) then
  411.       break
  412.     end
  413.   end
  414.   return formatted
  415. end
  416.  
  417. function round(num, numDecimalPlaces)
  418.   local mult = 10^(numDecimalPlaces or 0)
  419.   num = math.floor(num * mult + 0.5) / mult
  420.   if num ~= 100 then
  421.     return string.format("%."..numDecimalPlaces.."f", num)
  422.   end
  423.   return "100"
  424. end
  425.  
  426. function sum(table)
  427.     local sum = 0
  428.     for k,v in pairs(table) do
  429.         sum = sum + v
  430.     end
  431.     return sum
  432. end
  433.  
  434. function getMaxValue(table)  --find the maximum value in a table
  435.   local highest = 1
  436.   for _,v in pairs(table) do
  437.     if math.abs(v) > highest then highest = math.abs(v) end
  438.   end
  439.   return highest
  440. end
  441.  
  442. function testScaleChange(val)
  443.   if inputHist.payload.max ~= val then
  444.     inputHist.payload.scaleChange = true
  445.     outputHist.payload.scaleChange = true
  446.     inputHist.payload.max = val
  447.     outputHist.payload.max = val
  448.     return true
  449.   end
  450.   return false
  451. end
  452.  
  453. function setMaxValue(output)  --Makes sure the Histograms are using the same scale
  454.   local highest = 1
  455.   local inMax = getMaxValue(inputHist.payload.values)
  456.   local outMax = getMaxValue(outputHist.payload.realVals)
  457.  
  458.   if inMax > highest then highest = inMax end
  459.   if outMax > highest then highest = outMax end
  460.    --[[
  461.   All this invert garbage (the following ~10 lines of code) is to reduce flickering when rendering the Histograms
  462.   via the library.  As far as I can tell, when rendering the output upsidedown via negative values, it toggled a
  463.   gpu.setBackground change in the charts, overloading the GPU update allowance per tick/loop.  I tried only
  464.   updating the drawing of each chart object in consecutive loops, bit didn't like the desync and choppiness that
  465.   caused.  This was my next attempt at optimizing...and it works almost as well as separating the drawing
  466.   processes into different tick loops, but still flickers a bit.  I'll look into further optimizations to further
  467.   reduce or eliminate the flickering.
  468.   ]]--
  469.   local maxList = {960, 2400, 9600, 24000, 48000, 96000, transferCap}
  470.   table.sort(maxList)
  471.   local change
  472.   for _,v in ipairs(maxList) do
  473.     if highest <= v then
  474.       change = testScaleChange(v)
  475.       highest = v
  476.       break
  477.     end
  478.   end
  479.  
  480.   if change and (#outputHist.payload.realVals > 1) then  
  481.     for k, v in pairs(outputHist.payload.realVals) do
  482.       if highest - v < 0 then error("Some math is wrong when inverting Output") end
  483.       outputHist.payload.values[k] = highest - v
  484.     end
  485.   end
  486.   if highest - output < 0 then error("Some math is wrong when inverting Output") end
  487.   table.insert(outputHist.payload.values, (highest - output))
  488. end
  489.  
  490. local average = "Average I/O Rate: 0 RF/t"
  491. local draws = 0
  492. while true do
  493.   local startTime = computer.uptime()
  494.  
  495.   local energy = math.floor(matrix.getEnergy() / 2.5)  --values are 2.5x higher than listed in the matrix gui for some reason, this fixes it
  496.   local input = math.floor(matrix.getInput() / 2.5)
  497.   local output = math.floor(matrix.getOutput() / 2.5)
  498.    
  499.   table.insert(inputHist.payload.values, input)
  500.   table.insert(outputHist.payload.realVals, output)
  501.   powerBar.payload.value = energy
  502.  
  503.   if #inputHist.payload.values > 96 then --don't let the Histograms overflow their area and stop displaying data
  504.     table.remove(inputHist.payload.values, 1)
  505.     table.remove(outputHist.payload.values, 1)
  506.     table.remove(outputHist.payload.realVals, 1)
  507.   end
  508.   setMaxValue(output)
  509.    
  510.   if draws % 10 == 0 then
  511.     average = (sum(inputHist.payload.values) - sum(outputHist.payload.realVals))/96
  512.     average = round(average, 0)
  513.     average = comma_value(average)
  514.     average = "Average I/O Rate: " .. average .. " RF/t"
  515.   end
  516.  
  517.   powerBar:draw()
  518.   centerText("Induction Matrix", 1)
  519.   local tmp = comma_value(energy).." RF"  
  520.   centerText(tmp, 2)
  521.   tmp = round(((energy/maxEnergy) * 100), 2) .. "%"
  522.   centerText(tmp, 3)
  523.   centerText(average, 4)
  524.   inputHist:draw()
  525.   gpu.set(1, 15, "Input Rate")
  526.   local tmp = comma_value(input).." RF"
  527.   gpu.set(2, 16, tmp)
  528.   outputHist:draw()    
  529.   gpu.set(1, 19, "Output Rate")
  530.   local tmp = comma_value(output).." RF"
  531.   gpu.set(2, 20, tmp)
  532.   draws = draws + 1
  533.  
  534.   local time = computer.uptime()-startTime
  535.   local x,y
  536.   if (time >= 0) and (time < .05) then
  537.     _, _, x, y = event.pull( (.05 - time), "touch")
  538.   end
  539.   if x and y then goto quit end
  540. end
  541.  
  542. ::quit::
Advertisement
Add Comment
Please, Sign In to add comment