Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local term = require("term")
- local computer = require("computer")
- local sides = {
- TOP = 1,
- BOTTOM = 2,
- LEFT = 3,
- RIGHT = 4,
- "TOP",
- "BOTTOM",
- "LEFT",
- "RIGHT"
- }
- local function patch(base, head)
- for k, v in pairs(head) do
- if type(v) ~= "table" or type(base[k]) ~= "table" then
- base[k] = v
- else
- patch(base[k], v)
- end
- end
- end
- local Histogram
- do
- local characters = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
- local meta = {}
- meta.__index = meta
- local function calcHeight(height, perc)
- return math.floor(perc * height * 8)
- end
- local function getBarChars(height, halfHeight, totalHeight)
- if height < 0 then
- height = -height
- local spaces = math.floor(height / 8)
- local part = characters[8 - (height - spaces * 8) + 1]
- if spaces * 8 == height then
- part = ""
- end
- local blocks = totalHeight - halfHeight
- return characters[9]:rep(blocks) .. characters[1]:rep(spaces) .. part
- end
- local blocks = math.floor(height / 8)
- local part = characters[height - blocks * 8 + 1]
- if blocks * 8 == height then
- part = ""
- end
- local spaces = halfHeight - blocks - (part ~= "" and 1 or 0)
- return characters[1]:rep(spaces) .. part .. characters[9]:rep(blocks)
- end
- function meta:draw(container)
- if self.max == self.min and self.max == 0 then
- error("min and max are both 0!")
- end
- local loopStart, loopEnd = 1, container.width
- if self.align == sides.RIGHT then
- loopEnd = #self.values
- loopStart = loopEnd - container.width + 1
- end
- local max = self.max - self.min
- local min = 0
- local bar = 1
- local levelY = self.level.y
- if levelY > 0 and levelY < 1 then
- levelY = levelY * container.height
- elseif levelY < 0 then
- levelY = container.height + levelY + 1
- end
- levelY = math.floor(levelY)
- if levelY > container.height then
- levelY = container.height
- end
- local levelV = self.level.value or self.min
- if levelV < self.min or levelV > self.max then
- error("invalid level value set!")
- end
- for i = loopStart, loopEnd do
- local value = self.values[i] or levelV
- if value < self.min or value > self.max then
- error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. value)
- end
- local v = value - levelV
- local halfH = v < 0 and levelY or container.height - levelY
- local perc
- if v < 0 then
- perc = (levelV + value) / (levelV - self.min)
- else
- perc = (value - levelV) / (self.max - levelV)
- end
- if v == 0 and max == 0 then
- perc = 1
- end
- local height = calcHeight(halfH, perc)
- local chars = getBarChars(height, halfH, container.height)
- local fg, bg = self.colorFunc(i, perc, value, self, container)
- fg = fg or container.fg
- bg = bg or container.bg
- if v < 0 then
- fg, bg = bg, fg
- end
- if container.gpu.getForeground() ~= fg then
- container.gpu.setForeground(fg)
- end
- if container.gpu.getBackground() ~= bg then
- container.gpu.setBackground(bg)
- end
- if self.scaleChange or (#self.values < container.width) then
- container.gpu.set(container:getX() + bar - 1,
- container:getY(),
- chars,
- true)
- bar = bar + 1
- else
- if bar == 1 then
- container.gpu.copy(container.x + 1,
- container.y,
- container.width - 1,
- container.height,
- -1,
- 0)
- end
- if bar < 8 then
- container.gpu.set(container:getX() + bar - 1,
- container:getY(),
- chars,
- true)
- elseif loopEnd == bar then
- container.gpu.set(container:getX() + bar - 1,
- container:getY(),
- chars,
- true)
- end
- bar = bar + 1
- end
- end
- self.scaleChange = false
- end
- Histogram = function(tbl)
- local obj = {
- style = "Histogram",
- values = {},
- align = sides.LEFT,
- colorFunc = function() end,
- min = 0,
- max = 1,
- scaleChange = false,
- level = {
- y = 0,
- value = nil
- }
- }
- if tbl then
- patch(obj, tbl)
- end
- return setmetatable(obj, meta)
- end
- end
- local ProgressBar
- do
- local meta = {}
- meta.__index = meta
- local charsV = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"}
- local charsH = {" ", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█"}
- local function calcLength(perc, max)
- return math.floor(perc * 8 * max), max * 8
- end
- local function getChars(direction, len, max)
- if direction == sides.RIGHT then
- local blocks = math.floor(len / 8)
- local part = charsH[len - blocks * 8 + 1]
- if blocks * 8 == len then
- part = ""
- end
- local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
- return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
- elseif direction == sides.LEFT then
- local spaces = math.floor(len / 8)
- local part = charsH[9 - (len - spaces * 8)]
- if spaces * 8 == len then
- part = ""
- end
- local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
- return charsH[9]:rep(blocks) .. part .. charsH[1]:rep(spaces)
- elseif direction == sides.TOP then
- local blocks = math.floor(len / 8)
- local part = charsV[len - blocks * 8 + 1]
- if blocks * 8 == len then
- part = ""
- end
- local spaces = max / 8 - blocks - (part ~= "" and 1 or 0)
- return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
- elseif direction == sides.BOTTOM then
- local spaces = math.floor(len / 8)
- local part = charsV[9 - (len - spaces * 8)]
- if spaces * 8 == len then
- part = ""
- end
- local blocks = max / 8 - spaces - (part ~= "" and 1 or 0)
- return charsV[1]:rep(spaces) .. part .. charsV[9]:rep(blocks)
- end
- end
- function meta:draw(container)
- local min = 0
- local max = self.max - self.min
- if min == max and min == 0 then
- error("min and max values are the same")
- end
- local value = self.value - self.min
- if not (value >= min and value <= max) then
- error("incorrect min/max values: min = " .. self.min .. ", max = " .. self.max .. ", v = " .. self.value)
- end
- local perc = value / max
- local maxLength = container.width
- if self.direction == sides.TOP or self.direction == sides.BOTTOM then
- maxLength = container.height
- end
- local chars = getChars(self.direction, calcLength(perc, maxLength))
- local fg, bg = self.colorFunc(self.value, perc, self, container)
- fg = fg or container.fg
- bg = bg or container.bg
- if self.direction == sides.LEFT or self.direction == sides.BOTTOM then
- fg, bg = bg, fg
- end
- if container.gpu.getForeground() ~= fg then
- container.gpu.setForeground(fg)
- end
- if container.gpu.getBackground() ~= bg then
- container.gpu.setBackground(bg)
- end
- if self.direction == sides.TOP or self.direction == sides.BOTTOM then
- for x = 1, container.width, 1 do
- container.gpu.set(container:getX() + x - 1, container:getY(), chars, true)
- end
- else
- for y = 1, container.height, 1 do
- container.gpu.set(container:getX(), container:getY() + y - 1, chars)
- end
- end
- end
- ProgressBar = function(tbl)
- local obj = {
- style = "ProgressBar",
- direction = sides.RIGHT,
- min = 0,
- max = 1,
- value = 0,
- colorFunc = function(value, perc, prbar, container) end
- }
- if tbl then
- patch(obj, tbl)
- end
- return setmetatable(obj, meta)
- end
- end
- local Container
- do
- local meta = {}
- meta.__index = meta
- function meta:draw()
- if self.payload then
- local fg = self.gpu.getForeground()
- local bg = self.gpu.getBackground()
- if fg ~= self.fg then
- self.gpu.setForeground(self.fg)
- end
- if bg ~= self.bg then
- self.gpu.setBackground(self.bg)
- end
- if (self.payload.style == "ProgressBar") or (self.payload.style == "Histogram" and self.payload.scaleChange) then
- self.gpu.fill(self.x, self.y, self.width, self.height, " ")
- end
- self.payload:draw(self)
- if self.gpu.getForeground() ~= fg then
- self.gpu.setForeground(fg)
- end
- if self.gpu.getBackground() ~= bg then
- self.gpu.setBackground(bg)
- end
- end
- end
- function meta:getX()
- return self.x + self.payloadX - 1
- end
- function meta:getY()
- return self.y + self.payloadY - 1
- end
- Container = function(tbl)
- local obj = {
- gpu = component.gpu,
- fg = 0xffffff,
- bg = 0x000000,
- x = 1,
- y = 1,
- payloadX = 1,
- payloadY = 1,
- width = 80,
- height = 25,
- payload = nil
- }
- if tbl then
- patch(obj, tbl)
- end
- return setmetatable(obj, meta)
- end
- end
- term.clear()
- local gpu = term.gpu()
- gpu.setResolution(96, 30) --to make the text bigger, using a 3x2 monitor
- local matrix = component.list("induction_matrix")
- for a, _ in pairs(matrix) do matrix = component.proxy(a) end
- 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
- local transferCap = math.floor(matrix.getTransferCap() / 2.5)
- local powerBar = Container({
- x = 1,
- y = 1,
- width = 96,
- height = 5,
- payload = ProgressBar({
- value = 0,
- max = maxEnergy,
- colorFunc = function(_, perc )
- if perc >= .9 then
- return 0x00db00
- end
- if perc >= .75 then
- return 0x99db00
- end
- if perc >= .5 then
- return 0xffdb00
- end
- if perc >= .25 then
- return 0xff4900
- end
- if perc >= .1 then
- return 0xff0000
- end
- end
- })
- })
- local inputHist = Container({
- x = 1,
- y = 6,
- width = 96,
- height = 12,
- payload = Histogram({
- colorFunc = function(index, norm, value, self, container)
- return 0x0049ff
- end
- })
- })
- local outputHist = Container({
- x = 1,
- y = 18,
- width = 96,
- height = 12,
- payload = Histogram({
- realVals = {},
- level = { y = -12 },
- colorFunc = function(index, norm, value, self, container)
- return 0x000000, 0xff6d40
- end
- })
- })
- function centerText(_str, y, x1, x2) --function to write text centered between two X values
- local x1 = x1 or 1 --if no values for x are given, assume full screen.
- local x2 = x2 or 96
- local start = math.floor(((x2 - x1) - #_str)/2) + x1
- gpu.set(start, y, _str)
- end
- function comma_value(amount) --format big numbers with commas.
- local formatted = tostring(amount)
- while true do
- formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2')
- if (k==0) then
- break
- end
- end
- return formatted
- end
- function round(num, numDecimalPlaces)
- local mult = 10^(numDecimalPlaces or 0)
- num = math.floor(num * mult + 0.5) / mult
- if num ~= 100 then
- return string.format("%."..numDecimalPlaces.."f", num)
- end
- return "100"
- end
- function sum(table)
- local sum = 0
- for k,v in pairs(table) do
- sum = sum + v
- end
- return sum
- end
- function getMaxValue(table) --find the maximum value in a table
- local highest = 1
- for _,v in pairs(table) do
- if math.abs(v) > highest then highest = math.abs(v) end
- end
- return highest
- end
- function testScaleChange(val)
- if inputHist.payload.max ~= val then
- inputHist.payload.scaleChange = true
- outputHist.payload.scaleChange = true
- inputHist.payload.max = val
- outputHist.payload.max = val
- return true
- end
- return false
- end
- function setMaxValue(output) --Makes sure the Histograms are using the same scale
- local highest = 1
- local inMax = getMaxValue(inputHist.payload.values)
- local outMax = getMaxValue(outputHist.payload.realVals)
- if inMax > highest then highest = inMax end
- if outMax > highest then highest = outMax end
- --[[
- All this invert garbage (the following ~10 lines of code) is to reduce flickering when rendering the Histograms
- via the library. As far as I can tell, when rendering the output upsidedown via negative values, it toggled a
- gpu.setBackground change in the charts, overloading the GPU update allowance per tick/loop. I tried only
- updating the drawing of each chart object in consecutive loops, bit didn't like the desync and choppiness that
- caused. This was my next attempt at optimizing...and it works almost as well as separating the drawing
- processes into different tick loops, but still flickers a bit. I'll look into further optimizations to further
- reduce or eliminate the flickering.
- ]]--
- local maxList = {960, 2400, 9600, 24000, 48000, 96000, transferCap}
- table.sort(maxList)
- local change
- for _,v in ipairs(maxList) do
- if highest <= v then
- change = testScaleChange(v)
- highest = v
- break
- end
- end
- if change and (#outputHist.payload.realVals > 1) then
- for k, v in pairs(outputHist.payload.realVals) do
- if highest - v < 0 then error("Some math is wrong when inverting Output") end
- outputHist.payload.values[k] = highest - v
- end
- end
- if highest - output < 0 then error("Some math is wrong when inverting Output") end
- table.insert(outputHist.payload.values, (highest - output))
- end
- local average = "Average I/O Rate: 0 RF/t"
- local draws = 0
- while true do
- local startTime = computer.uptime()
- 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
- local input = math.floor(matrix.getInput() / 2.5)
- local output = math.floor(matrix.getOutput() / 2.5)
- table.insert(inputHist.payload.values, input)
- table.insert(outputHist.payload.realVals, output)
- powerBar.payload.value = energy
- if #inputHist.payload.values > 96 then --don't let the Histograms overflow their area and stop displaying data
- table.remove(inputHist.payload.values, 1)
- table.remove(outputHist.payload.values, 1)
- table.remove(outputHist.payload.realVals, 1)
- end
- setMaxValue(output)
- if draws % 10 == 0 then
- average = (sum(inputHist.payload.values) - sum(outputHist.payload.realVals))/96
- average = round(average, 0)
- average = comma_value(average)
- average = "Average I/O Rate: " .. average .. " RF/t"
- end
- powerBar:draw()
- centerText("Induction Matrix", 1)
- local tmp = comma_value(energy).." RF"
- centerText(tmp, 2)
- tmp = round(((energy/maxEnergy) * 100), 2) .. "%"
- centerText(tmp, 3)
- centerText(average, 4)
- inputHist:draw()
- gpu.set(1, 15, "Input Rate")
- local tmp = comma_value(input).." RF"
- gpu.set(2, 16, tmp)
- outputHist:draw()
- gpu.set(1, 19, "Output Rate")
- local tmp = comma_value(output).." RF"
- gpu.set(2, 20, tmp)
- draws = draws + 1
- local time = computer.uptime()-startTime
- local x,y
- if (time >= 0) and (time < .05) then
- _, _, x, y = event.pull( (.05 - time), "touch")
- end
- if x and y then goto quit end
- end
- ::quit::
Advertisement
Add Comment
Please, Sign In to add comment