Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- power_in.lua — CC:Tweaked + Advanced Peripherals
- -- Monitors *incoming* power via an inline Energy Detector's getTransferRate()
- -- and renders a minimal-write UI to a monitor. Designed to be server-friendly.
- --
- -- Usage:
- -- power_in
- -- power_in <detector> <monitor> [intervalSeconds]
- -- power_in right top 0.5
- --
- -- Notes:
- -- - Place an Advanced Peripherals Energy Detector inline on your Mekanism Universal Cable.
- -- - The detector should face the incoming side (from your source into your base).
- -- - getTransferRate() is assumed to be FE/t (Forge Energy per tick).
- local args = { ... }
- local DEFAULT_INTERVAL = tonumber(args[3]) or 1.0
- -- ========= Utilities =========
- local function safeWrap(name)
- if not name then return nil end
- local ok, p = pcall(peripheral.wrap, name)
- if ok and p then return p end
- end
- local function findDetector(hint)
- -- If a specific side/name provided, prefer that
- local p = safeWrap(hint)
- if p and type(p.getTransferRate) == "function" then return p, hint end
- -- Otherwise, search all peripherals for something with getTransferRate()
- for _, n in ipairs(peripheral.getNames()) do
- local q = safeWrap(n)
- if q and type(q.getTransferRate) == "function" then
- return q, n
- end
- end
- end
- local function findLargestMonitor(hint)
- -- Prefer a specifically named monitor
- local function isMonitor(p) return p and peripheral.getType(p) == "monitor" end
- local mon = safeWrap(hint)
- if isMonitor(mon) then return mon, hint end
- -- Otherwise pick the largest available monitor
- local best, bestName, bestArea = nil, nil, -1
- for _, n in ipairs(peripheral.getNames()) do
- if peripheral.getType(n) == "monitor" then
- local m = peripheral.wrap(n)
- local w, h = m.getSize()
- local area = (w or 0) * (h or 0)
- if area > bestArea then best, bestName, bestArea = m, n, area end
- end
- end
- return best, bestName
- end
- local function fmtSI(n)
- local abs = math.abs(n)
- if abs >= 1e12 then return string.format("%.2fT", n/1e12)
- elseif abs >= 1e9 then return string.format("%.2fG", n/1e9)
- elseif abs >= 1e6 then return string.format("%.2fM", n/1e6)
- elseif abs >= 1e3 then return string.format("%.2fk", n/1e3)
- else return string.format("%.0f", n) end
- end
- local function clamp(v, a, b) if v < a then return a elseif v > b then return b else return v end end
- -- Render cache: avoid rewriting lines that didn’t change
- local lastFrame = {}
- local function blitLine(termLike, y, text, fg, bg)
- -- fg/bg optional; only used if term supports color and we pass them
- local w = select(1, termLike.getSize())
- if #text < w then text = text .. string.rep(" ", w - #text) end
- lastFrame[y] = lastFrame[y] or { text = "" }
- if lastFrame[y].text == text then return end
- termLike.setCursorPos(1, y)
- if fg and bg and term.isColor() and termLike.isColor and termLike.isColor() and termLike.blit then
- -- Build color strings if caller passed precomputed ones (not used here by default)
- termLike.write(text)
- else
- termLike.write(text)
- end
- lastFrame[y].text = text
- end
- local function clearFrameCache()
- lastFrame = {}
- end
- -- Simple sparkline using ASCII to avoid Unicode issues
- local sparkSet = { " ", ".", ":", "-", "=", "+", "*", "#", "%", "@" }
- local function sparkChar(v01)
- local idx = math.floor(clamp(v01, 0, 0.9999) * #sparkSet) + 1
- return sparkSet[idx]
- end
- -- ========= Setup =========
- local detector, detectorName = findDetector(args[1])
- if not detector then
- print("No Energy Detector with getTransferRate() found.")
- print("Place an Advanced Peripherals Energy Detector inline on the cable.")
- print("Optionally run: power_in <detector> <monitor> [interval]")
- return
- end
- local monitor, monitorName = findLargestMonitor(args[2])
- local usingTerm = false
- if not monitor then
- print("No monitor detected; drawing to terminal.")
- monitor = term.current()
- usingTerm = true
- end
- -- Prepare monitor
- local w, h = monitor.getSize()
- if not usingTerm then
- -- Pick a reasonable text scale to maximize info density—favor 2 for typical big walls
- local scale = 1
- if w >= 64 and h >= 24 then scale = 2
- elseif w >= 32 and h >= 12 then scale = 1
- else scale = 0.5 end
- -- Clamp to valid scales 0.5..5
- if monitor.setTextScale then
- monitor.setTextScale(scale)
- w, h = monitor.getSize() -- recompute after scaling
- end
- end
- local function setPalette()
- if monitor.setBackgroundColor then
- pcall(monitor.setBackgroundColor, colors.black)
- pcall(monitor.setTextColor, colors.white)
- end
- end
- monitor.setCursorBlink(false)
- setPalette()
- monitor.clear()
- -- ========= Stats =========
- local interval = DEFAULT_INTERVAL
- local EMA_ALPHA = 0.2 -- smoothing for EMA
- local ema_rt = nil -- EMA of FE/t
- local peak_rt = 0 -- rolling peak (decays slowly)
- local PEAK_DECAY = 0.98 -- decay per sample to keep bar meaningful
- local history = {} -- for sparkline (last N samples)
- local histMax = math.max(10, w - 2)
- -- ========= Drawing =========
- local title = "Incoming Power"
- local function drawFrame()
- clearFrameCache()
- monitor.clear()
- local dateStr = textutils.formatTime(os.time(), true) or ""
- local header = title
- if detectorName then header = header .. " [" .. detectorName .. "]" end
- if monitorName and not usingTerm then header = header .. " -> " .. monitorName end
- -- Trim header if too long
- if #header > w then header = header:sub(1, w) end
- blitLine(monitor, 1, header)
- if h >= 3 then blitLine(monitor, 2, string.rep("-", w)) end
- end
- local function drawStats(rt_fept)
- local line = 3
- if h < 5 then
- -- Very small target—just one line
- local s = ("FE/t: %s"):format(fmtSI(rt_fept))
- blitLine(monitor, line, s:sub(1, w))
- return
- end
- -- EMA
- ema_rt = ema_rt and (ema_rt + EMA_ALPHA * (rt_fept - ema_rt)) or rt_fept
- -- Peak with decay
- peak_rt = math.max(peak_rt * PEAK_DECAY, rt_fept)
- -- Derived units
- local rtf = fmtSI(rt_fept) .. " FE/t"
- local rps = fmtSI(rt_fept * 20) .. " FE/s"
- local emaf = fmtSI(ema_rt) .. " FE/t"
- local peakf = fmtSI(peak_rt) .. " FE/t"
- blitLine(monitor, line, ("Now : %s"):format(rtf):sub(1, w)); line = line + 1
- blitLine(monitor, line, ("EMA : %s"):format(emaf):sub(1, w)); line = line + 1
- blitLine(monitor, line, ("Peak: %s"):format(peakf):sub(1, w)); line = line + 1
- blitLine(monitor, line, ("Rate: %s"):format(rps):sub(1, w)); line = line + 1
- -- Bar (relative to peak to keep scale useful)
- if line <= h then
- local barWidth = math.max(0, w - 10)
- local ratio = peak_rt > 0 and (rt_fept / peak_rt) or 0
- local filled = math.floor(barWidth * clamp(ratio, 0, 1))
- local bar = "[" .. string.rep("=", filled) .. string.rep(" ", barWidth - filled) .. "]"
- blitLine(monitor, line, ("Load: %s"):format(bar):sub(1, w))
- line = line + 1
- end
- -- Sparkline
- if line <= h then
- -- maintain history
- table.insert(history, rt_fept)
- if #history > histMax then table.remove(history, 1) end
- local m = 0
- for i = 1, #history do if history[i] > m then m = history[i] end end
- if m <= 0 then m = 1 end
- local sparkw = math.min(w, #history)
- local s = {}
- for i = #history - sparkw + 1, #history do
- local v = history[i] or 0
- table.insert(s, sparkChar(v / m))
- end
- blitLine(monitor, line, table.concat(s))
- line = line + 1
- end
- -- Footer time
- if line <= h then
- local t = "Last update: " .. (textutils.formatTime(os.time(), true) or "")
- blitLine(monitor, h, t:sub(1, w))
- end
- end
- -- ========= Main loop =========
- drawFrame()
- while true do
- -- Safe poll
- local ok, val = pcall(detector.getTransferRate, detector)
- local rt = (ok and type(val) == "number") and val or 0
- setPalette()
- drawStats(rt)
- sleep(interval)
- end
Advertisement