Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local MONITOR_SIDE = "back"
- local UPDATE_DELAY = 0.5
- local AVG_SAMPLES = 10
- local CORE_TIER = 6
- local GRAPH_HISTORY = 4000
- local THEME = {
- bg = colors.black,
- header = colors.blue,
- border = colors.cyan,
- ok = colors.lime,
- warn = colors.orange,
- bad = colors.red,
- text = colors.white,
- dim = colors.lightGray,
- barBack = colors.gray,
- yellow = colors.yellow,
- }
- local function clamp(x, a, b)
- if x < a then return a end
- if x > b then return b end
- return x
- end
- local function formatNumber(n)
- n = tonumber(n) or 0
- local absn = math.abs(n)
- if absn >= 1e12 then return string.format("%.2f T", n / 1e12)
- elseif absn >= 1e9 then return string.format("%.2f G", n / 1e9)
- elseif absn >= 1e6 then return string.format("%.2f M", n / 1e6)
- elseif absn >= 1e3 then return string.format("%.2f K", n / 1e3)
- else return string.format("%.0f", n) end
- end
- local function writeAt(mon, x, y, s, fg, bg)
- if bg then mon.setBackgroundColor(bg) end
- if fg then mon.setTextColor(fg) end
- mon.setCursorPos(x, y)
- mon.write(s)
- end
- local function safeWrite(mon, x, y, s, fg, bg)
- local w, h = mon.getSize()
- if y < 1 or y > h or x > w then return end
- if x < 1 then
- local cut = 1 - x
- s = s:sub(cut + 1)
- x = 1
- end
- if #s <= 0 then return end
- local maxLen = w - x + 1
- if maxLen <= 0 then return end
- if #s > maxLen then s = s:sub(1, maxLen) end
- writeAt(mon, x, y, s, fg, bg)
- end
- local function fillRect(mon, x, y, w, h, bg)
- mon.setBackgroundColor(bg)
- for dy = 0, h - 1 do
- mon.setCursorPos(x, y + dy)
- mon.write(string.rep(" ", w))
- end
- end
- local function drawBox(mon, x, y, w, h, borderColor, title)
- fillRect(mon, x, y, w, h, THEME.bg)
- fillRect(mon, x, y, w, 1, borderColor)
- fillRect(mon, x, y + h - 1, w, 1, borderColor)
- mon.setBackgroundColor(borderColor)
- for dy = 0, h - 1 do
- mon.setCursorPos(x, y + dy); mon.write(" ")
- mon.setCursorPos(x + w - 1, y + dy); mon.write(" ")
- end
- if title and #title > 0 then
- local t = " " .. title .. " "
- local tx = x + 2
- local maxRight = x + w - 2
- if tx + #t - 1 > maxRight then
- t = t:sub(1, maxRight - tx + 1)
- end
- writeAt(mon, tx, y, t, THEME.text, THEME.bg)
- end
- end
- local function clearInner(mon, x, y, w, h)
- fillRect(mon, x + 1, y + 1, w - 2, h - 2, THEME.bg)
- end
- local function push(t, v, maxN)
- t[#t + 1] = v
- if #t > maxN then table.remove(t, 1) end
- end
- local function drawHeader(mon, text)
- local w = (mon.getSize())
- fillRect(mon, 1, 1, w, 1, THEME.header)
- local t = (#text > w) and text:sub(1, w) or text
- local x = math.max(1, math.floor((w - #t) / 2) + 1)
- safeWrite(mon, x, 1, t, THEME.text, THEME.header)
- end
- local function drawVerticalBar(mon, x, y, width, height, percent, color)
- percent = clamp(percent, 0, 100)
- local filled = math.floor(height * percent / 100 + 0.0001)
- fillRect(mon, x, y, width, height, THEME.barBack)
- mon.setBackgroundColor(color)
- for i = 0, filled - 1 do
- mon.setCursorPos(x, y + height - 1 - i)
- mon.write(string.rep(" ", width))
- end
- end
- -- Graph helpers
- local function graphUpdateScale(currentScale, newAbs)
- newAbs = math.max(newAbs, 1)
- if newAbs > currentScale then return newAbs end
- return math.max(1, currentScale * 0.992)
- end
- local function graphMapY(v, maxAbs, y, h)
- local mid = y + math.floor((h - 1) / 2)
- local amp = math.floor((h - 1) / 2)
- if amp < 1 then return mid end
- local n = clamp(v / maxAbs, -1, 1)
- local yy = mid - math.floor(n * amp + (n >= 0 and 0.5 or -0.5))
- return clamp(yy, y, y + h - 1)
- end
- local function plot(mon, x, y, gx, gy, gw, gh, bg)
- if x < gx or x > gx + gw - 1 or y < gy or y > gy + gh - 1 then return end
- mon.setBackgroundColor(bg)
- mon.setCursorPos(x, y)
- mon.write(" ")
- end
- local function drawGraphLine(mon, gx, gy, gw, gh, data, maxAbs)
- fillRect(mon, gx, gy, gw, gh, THEME.bg)
- local mid = gy + math.floor((gh - 1) / 2)
- mon.setBackgroundColor(THEME.dim)
- mon.setCursorPos(gx, mid)
- mon.write(string.rep(" ", gw))
- local start = math.max(1, #data - gw + 1)
- local prevX, prevY, prevV
- for i = start, #data do
- local col = (i - start) + 1
- local x1 = gx + col - 1
- local v = data[i] or 0
- local y1 = graphMapY(v, maxAbs, gy, gh)
- local c = THEME.dim
- if v > 0 then c = THEME.ok elseif v < 0 then c = THEME.bad end
- if prevX then
- local x0, y0 = prevX, prevY
- local dx = math.abs(x1 - x0)
- local dy = math.abs(y1 - y0)
- local sx = (x0 < x1) and 1 or -1
- local sy = (y0 < y1) and 1 or -1
- local err = dx - dy
- while true do
- local cc = c
- if prevV and prevV ~= 0 and v ~= 0 and ((prevV > 0 and v < 0) or (prevV < 0 and v > 0)) then
- cc = THEME.text
- end
- plot(mon, x0, y0, gx, gy, gw, gh, cc)
- if x0 == x1 and y0 == y1 then break end
- local e2 = 2 * err
- if e2 > -dy then err = err - dy; x0 = x0 + sx end
- if e2 < dx then err = err + dx; y0 = y0 + sy end
- end
- else
- plot(mon, x1, y1, gx, gy, gw, gh, c)
- end
- prevX, prevY, prevV = x1, y1, v
- end
- end
- local function drawGraphBars(mon, gx, gy, gw, gh, data, maxAbs)
- maxAbs = math.max(maxAbs or 1, 1)
- local top = gy
- local bot = gy + gh - 1
- local mid = gy + math.floor(gh / 2)
- if mid > bot then mid = bot end
- fillRect(mon, gx, gy, gw, gh, THEME.bg)
- mon.setBackgroundColor(THEME.dim)
- mon.setCursorPos(gx, mid)
- mon.write(string.rep(" ", gw))
- local upMax = mid - top
- local downMax = bot - mid
- local start = math.max(1, #data - gw + 1)
- for col = 1, gw do
- local i = start + col - 1
- local v = data[i] or 0
- local n = clamp(v / maxAbs, -1, 1)
- local x = gx + col - 1
- if n > 0 and upMax > 0 then
- local bh = math.ceil(n * upMax); if bh < 1 then bh = 1 end
- mon.setBackgroundColor(THEME.ok)
- for k = 1, bh do
- local yy = mid - k
- if yy >= top then mon.setCursorPos(x, yy); mon.write(" ") end
- end
- elseif n < 0 and downMax > 0 then
- local bh = math.ceil((-n) * downMax); if bh < 1 then bh = 1 end
- mon.setBackgroundColor(THEME.bad)
- for k = 1, bh do
- local yy = mid + k
- if yy <= bot then mon.setCursorPos(x, yy); mon.write(" ") end
- end
- end
- end
- end
- local function drawFluxGraphOld(mon, x, y, w, h, data, maxAbs)
- fillRect(mon, x, y, w, h, THEME.bg)
- maxAbs = math.max(maxAbs or 1, 1)
- local start = math.max(1, #data - w + 1)
- local col = 0
- for i = start, #data do
- col = col + 1
- local v = data[i] or 0
- local absV = math.abs(v)
- local barH = math.floor((absV / maxAbs) * h + 0.0001)
- barH = clamp(barH, 0, h)
- local c = THEME.dim
- if v > 0 then c = THEME.ok elseif v < 0 then c = THEME.bad end
- mon.setBackgroundColor(THEME.bg)
- for ry = 0, h - 1 do
- mon.setCursorPos(x + col - 1, y + ry)
- mon.write(" ")
- end
- mon.setBackgroundColor(c)
- for ry = 0, barH - 1 do
- mon.setCursorPos(x + col - 1, y + (h - 1 - ry))
- mon.write(" ")
- end
- end
- end
- -- Peripheral discovery
- local function findEnergyCore()
- for _, name in ipairs(peripheral.getNames()) do
- if peripheral.getType(name) == "draconic_rf_storage" then
- return peripheral.wrap(name), name
- end
- end
- return nil, nil
- end
- -- Monitor init
- local mon = peripheral.wrap(MONITOR_SIDE)
- if not mon then
- print("Error: monitor not found on " .. tostring(MONITOR_SIDE))
- return
- end
- if mon.setTextScale then mon.setTextScale(1) end
- local core, coreName = findEnergyCore()
- if not core then
- mon.setBackgroundColor(THEME.bg); mon.clear()
- writeAt(mon, 2, 2, "ERROR:", THEME.bad, THEME.bg)
- writeAt(mon, 2, 3, "Energy Core not detected", THEME.text, THEME.bg)
- return
- end
- -- Layout
- local bx1x, bx1y, bx1w, bx1h = 2, 3, 19, 21
- local bx2x, bx2y, bx2w, bx2h = 22, 3, 18, 8
- local bx3x, bx3y, bx3w, bx3h = 41, 3, 30, 8
- local bx4x, bx4y, bx4w, bx4h = 22, 12, 18, 12
- local bx5x, bx5y, bx5w, bx5h = 41, 12, 30, 12
- local gx, gy = bx5x + 2, bx5y + 2
- local gw, gh = bx5w - 4, bx5h - 4
- local modeLabels = { "LINE", "BARS", "BARS (OLD)" }
- local graphMode = 1
- local forceGraphRedraw = true
- local btnY = bx5y + bx5h - 1
- local btnLeftX = bx5x + 1
- local btnCenterX = btnLeftX + 2
- local btnRightX = btnCenterX + 2
- local function drawGraphButton(mode)
- mon.setBackgroundColor(THEME.border)
- mon.setTextColor(THEME.bg)
- mon.setCursorPos(btnLeftX, btnY); mon.write("<")
- mon.setCursorPos(btnCenterX, btnY); mon.write(tostring(mode))
- mon.setCursorPos(btnRightX, btnY); mon.write(">")
- end
- local function inButton(x, y)
- if y ~= btnY then return nil end
- if x == btnLeftX then return "left" end
- if x == btnRightX then return "right" end
- return nil
- end
- -- Initial paint
- mon.setBackgroundColor(THEME.bg)
- mon.clear()
- drawHeader(mon, "DRACONIC ENERGY CORE MONITOR")
- drawBox(mon, bx1x, bx1y, bx1w, bx1h, THEME.header, "Energy Buffer")
- drawBox(mon, bx2x, bx2y, bx2w, bx2h, THEME.border, "Capacity")
- drawBox(mon, bx3x, bx3y, bx3w, bx3h, THEME.border, "Stats")
- drawBox(mon, bx4x, bx4y, bx4w, bx4h, THEME.border, "Current Flow")
- drawBox(mon, bx5x, bx5y, bx5w, bx5h, THEME.border, "Graph")
- safeWrite(mon, bx2x + 2, bx2y + 2, "Stored:", THEME.text, THEME.bg)
- safeWrite(mon, bx2x + 2, bx2y + 4, "Max:", THEME.text, THEME.bg)
- safeWrite(mon, bx3x + 2, bx3y + 2, "Peak In:", THEME.text, THEME.bg)
- safeWrite(mon, bx3x + 2, bx3y + 4, "Peak Out:", THEME.text, THEME.bg)
- drawGraphButton(graphMode)
- -- State
- local previousEnergy = core.getEnergyStored() or 0
- local previousTime = os.clock()
- local fluxSamples = {}
- local graphData = {}
- local graphMaxAbs = 1
- local peakIn, peakOut = 0, 0
- local STABLE_RF_T = 100
- local function renderOnce()
- if not core or (coreName and not peripheral.isPresent(coreName)) then
- core, coreName = findEnergyCore()
- end
- if not core then
- safeWrite(mon, 2, 2, "ERROR: Energy Core not detected", THEME.bad, THEME.bg)
- return
- end
- local now = os.clock()
- local dt = now - previousTime
- if dt <= 0 then dt = UPDATE_DELAY end
- local energyStored = core.getEnergyStored() or 0
- local maxEnergy = core.getMaxEnergyStored() or 1
- if maxEnergy <= 0 then maxEnergy = 1 end
- local percent = clamp((energyStored / maxEnergy) * 100, 0, 100)
- local deltaRF = energyStored - previousEnergy
- local rateRFt = deltaRF / (dt * 20)
- push(fluxSamples, rateRFt, AVG_SAMPLES)
- local sum = 0
- for i = 1, #fluxSamples do sum = sum + fluxSamples[i] end
- local avgRFt = sum / math.max(#fluxSamples, 1)
- local currentIn, currentOut = 0, 0
- if avgRFt > 0 then currentIn = avgRFt else currentOut = -avgRFt end
- if currentIn > peakIn then peakIn = currentIn end
- if currentOut > peakOut then peakOut = currentOut end
- push(graphData, avgRFt, GRAPH_HISTORY)
- graphMaxAbs = graphUpdateScale(graphMaxAbs, math.abs(avgRFt))
- local barColor = THEME.ok
- if percent < 20 then barColor = THEME.bad
- elseif percent < 50 then barColor = THEME.warn
- elseif percent < 75 then barColor = THEME.yellow end
- -- Energy buffer
- drawVerticalBar(mon, bx1x + 3, bx1y + 3, 13, 14, percent, barColor)
- fillRect(mon, bx1x + 6, bx1y + bx1h - 3, 12, 1, THEME.bg)
- safeWrite(mon, bx1x + 7, bx1y + bx1h - 3, string.format("%.1f%%", percent), THEME.text, THEME.bg)
- -- Capacity
- fillRect(mon, bx2x + 2, bx2y + 3, bx2w - 4, 1, THEME.bg)
- fillRect(mon, bx2x + 2, bx2y + 5, bx2w - 4, 1, THEME.bg)
- safeWrite(mon, bx2x + 2, bx2y + 3, formatNumber(energyStored) .. " RF", THEME.text, THEME.bg)
- safeWrite(mon, bx2x + 2, bx2y + 5, formatNumber(maxEnergy) .. " RF", THEME.text, THEME.bg)
- -- Stats
- fillRect(mon, bx3x + 2, bx3y + 3, bx3w - 4, 1, THEME.bg)
- fillRect(mon, bx3x + 2, bx3y + 5, bx3w - 4, 1, THEME.bg)
- safeWrite(mon, bx3x + 2, bx3y + 3, formatNumber(peakIn) .. " RF/t", THEME.text, THEME.bg)
- safeWrite(mon, bx3x + 2, bx3y + 5, formatNumber(peakOut) .. " RF/t", THEME.text, THEME.bg)
- if avgRFt > STABLE_RF_T then
- safeWrite(mon, bx4x + 2, bx4y + 2, "INPUT", THEME.ok, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 3, "+" .. formatNumber(currentIn) .. " RF/t", THEME.text, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 5, "DELTA:", THEME.warn, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 6, "+" .. formatNumber(deltaRF) .. " RF", THEME.warn, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 9, ">>> CHARGE >>>", THEME.ok, THEME.bg)
- elseif avgRFt < -STABLE_RF_T then
- safeWrite(mon, bx4x + 2, bx4y + 2, "OUTPUT", THEME.bad, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 3, "-" .. formatNumber(currentOut) .. " RF/t", THEME.text, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 5, "DELTA:", THEME.warn, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 6, "-" .. formatNumber(math.abs(deltaRF)) .. " RF", THEME.warn, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 9, "<<< DISCHG <<<", THEME.bad, THEME.bg)
- else
- safeWrite(mon, bx4x + 2, bx4y + 2, "IDLE", THEME.text, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 5, "--- STABLE ---", THEME.dim, THEME.bg)
- safeWrite(mon, bx4x + 2, bx4y + 8, "~0 RF/t", THEME.dim, THEME.bg)
- end
- -- Graph
- if forceGraphRedraw then
- fillRect(mon, gx, gy, gw, gh, THEME.bg)
- forceGraphRedraw = false
- end
- if graphMode == 1 then
- drawGraphLine(mon, gx, gy, gw, gh, graphData, graphMaxAbs)
- elseif graphMode == 2 then
- drawGraphBars(mon, gx, gy, gw, gh, graphData, graphMaxAbs)
- else
- drawFluxGraphOld(mon, gx, gy, gw, gh, graphData, graphMaxAbs)
- end
- drawGraphButton(graphMode)
- -- Footer: Tier (right) + Day/Time (left)
- local mw, mh = mon.getSize()
- local tierStr = "TIER " .. tostring(CORE_TIER)
- fillRect(mon, mw - #tierStr - 1, mh - 1, #tierStr + 1, 1, THEME.bg)
- safeWrite(mon, mw - #tierStr, mh - 1, tierStr, THEME.dim, THEME.bg)
- local day = os.day()
- local tt = os.time()
- local hours = math.floor(tt)
- local minutes = math.floor((tt - hours) * 60)
- local timeStr = string.format("DAY %d %02d:%02d", day, hours, minutes)
- fillRect(mon, 1, mh - 1, 30, 1, THEME.bg)
- safeWrite(mon, 2, mh - 1, timeStr, THEME.dim, THEME.bg)
- previousEnergy = energyStored
- previousTime = now
- end
- -- Event loops
- local function uiLoop()
- while true do
- local ev = { os.pullEvent() }
- if ev[1] == "graph_left" then
- graphMode = graphMode - 1
- if graphMode < 1 then graphMode = #modeLabels end
- forceGraphRedraw = true
- fillRect(mon, gx, gy, gw, gh, THEME.bg)
- renderOnce()
- elseif ev[1] == "graph_right" then
- graphMode = graphMode + 1
- if graphMode > #modeLabels then graphMode = 1 end
- forceGraphRedraw = true
- fillRect(mon, gx, gy, gw, gh, THEME.bg)
- renderOnce()
- elseif ev[1] == "tick" then
- renderOnce()
- end
- end
- end
- local function tickLoop()
- while true do
- os.queueEvent("tick")
- sleep(UPDATE_DELAY)
- end
- end
- local function touchLoop()
- while true do
- local _, side, x, y = os.pullEvent("monitor_touch")
- if side == MONITOR_SIDE then
- local hit = inButton(x, y)
- if hit == "left" then os.queueEvent("graph_left")
- elseif hit == "right" then os.queueEvent("graph_right") end
- end
- end
- end
- parallel.waitForAny(uiLoop, tickLoop, touchLoop)
Advertisement
Comments
-
- i suggest you search for a monitor instead with this code line 264 - 269
- -- Monitor init
- local function findMonitor()
- for _, name in ipairs(peripheral.getNames()) do
- if peripheral.getType(name) == "monitor" then
- return peripheral.wrap(name), name
- end
- end
- return nil, nil
- end
- local mon, monitorName = findMonitor()
- if not mon then
- print("Error: No monitor found")
- return
- end
- if mon.setTextScale then
- mon.setTextScale(1)
- end
- it will also error out if no moniter is found
Add Comment
Please, Sign In to add comment