MrElders

EnergyMonitor

Oct 22nd, 2025 (edited)
778
0
Never
1
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 7.85 KB | Gaming | 0 0
  1. -- power_in.lua — CC:Tweaked + Advanced Peripherals
  2. -- Monitors *incoming* power via an inline Energy Detector's getTransferRate()
  3. -- and renders a minimal-write UI to a monitor. Designed to be server-friendly.
  4. --
  5. -- Usage:
  6. --   power_in
  7. --   power_in <detector> <monitor> [intervalSeconds]
  8. --   power_in right top 0.5
  9. --
  10. -- Notes:
  11. -- - Place an Advanced Peripherals Energy Detector inline on your Mekanism Universal Cable.
  12. -- - The detector should face the incoming side (from your source into your base).
  13. -- - getTransferRate() is assumed to be FE/t (Forge Energy per tick).
  14.  
  15. local args = { ... }
  16. local DEFAULT_INTERVAL = tonumber(args[3]) or 1.0
  17.  
  18. -- ========= Utilities =========
  19. local function safeWrap(name)
  20.   if not name then return nil end
  21.   local ok, p = pcall(peripheral.wrap, name)
  22.   if ok and p then return p end
  23. end
  24.  
  25. local function findDetector(hint)
  26.   -- If a specific side/name provided, prefer that
  27.   local p = safeWrap(hint)
  28.   if p and type(p.getTransferRate) == "function" then return p, hint end
  29.   -- Otherwise, search all peripherals for something with getTransferRate()
  30.   for _, n in ipairs(peripheral.getNames()) do
  31.     local q = safeWrap(n)
  32.     if q and type(q.getTransferRate) == "function" then
  33.       return q, n
  34.     end
  35.   end
  36. end
  37.  
  38. local function findLargestMonitor(hint)
  39.   -- Prefer a specifically named monitor
  40.   local function isMonitor(p) return p and peripheral.getType(p) == "monitor" end
  41.   local mon = safeWrap(hint)
  42.   if isMonitor(mon) then return mon, hint end
  43.  
  44.   -- Otherwise pick the largest available monitor
  45.   local best, bestName, bestArea = nil, nil, -1
  46.   for _, n in ipairs(peripheral.getNames()) do
  47.     if peripheral.getType(n) == "monitor" then
  48.       local m = peripheral.wrap(n)
  49.       local w, h = m.getSize()
  50.       local area = (w or 0) * (h or 0)
  51.       if area > bestArea then best, bestName, bestArea = m, n, area end
  52.     end
  53.   end
  54.   return best, bestName
  55. end
  56.  
  57. local function fmtSI(n)
  58.   local abs = math.abs(n)
  59.   if abs >= 1e12 then return string.format("%.2fT", n/1e12)
  60.   elseif abs >= 1e9 then return string.format("%.2fG", n/1e9)
  61.   elseif abs >= 1e6 then return string.format("%.2fM", n/1e6)
  62.   elseif abs >= 1e3 then return string.format("%.2fk", n/1e3)
  63.   else return string.format("%.0f", n) end
  64. end
  65.  
  66. local function clamp(v, a, b) if v < a then return a elseif v > b then return b else return v end end
  67.  
  68. -- Render cache: avoid rewriting lines that didn’t change
  69. local lastFrame = {}
  70. local function blitLine(termLike, y, text, fg, bg)
  71.   -- fg/bg optional; only used if term supports color and we pass them
  72.   local w = select(1, termLike.getSize())
  73.   if #text < w then text = text .. string.rep(" ", w - #text) end
  74.   lastFrame[y] = lastFrame[y] or { text = "" }
  75.   if lastFrame[y].text == text then return end
  76.   termLike.setCursorPos(1, y)
  77.   if fg and bg and term.isColor() and termLike.isColor and termLike.isColor() and termLike.blit then
  78.     -- Build color strings if caller passed precomputed ones (not used here by default)
  79.     termLike.write(text)
  80.   else
  81.     termLike.write(text)
  82.   end
  83.   lastFrame[y].text = text
  84. end
  85.  
  86. local function clearFrameCache()
  87.   lastFrame = {}
  88. end
  89.  
  90. -- Simple sparkline using ASCII to avoid Unicode issues
  91. local sparkSet = { " ", ".", ":", "-", "=", "+", "*", "#", "%", "@" }
  92. local function sparkChar(v01)
  93.   local idx = math.floor(clamp(v01, 0, 0.9999) * #sparkSet) + 1
  94.   return sparkSet[idx]
  95. end
  96.  
  97. -- ========= Setup =========
  98. local detector, detectorName = findDetector(args[1])
  99. if not detector then
  100.   print("No Energy Detector with getTransferRate() found.")
  101.   print("Place an Advanced Peripherals Energy Detector inline on the cable.")
  102.   print("Optionally run: power_in <detector> <monitor> [interval]")
  103.   return
  104. end
  105.  
  106. local monitor, monitorName = findLargestMonitor(args[2])
  107. local usingTerm = false
  108. if not monitor then
  109.   print("No monitor detected; drawing to terminal.")
  110.   monitor = term.current()
  111.   usingTerm = true
  112. end
  113.  
  114. -- Prepare monitor
  115. local w, h = monitor.getSize()
  116. if not usingTerm then
  117.   -- Pick a reasonable text scale to maximize info density—favor 2 for typical big walls
  118.   local scale = 1
  119.   if w >= 64 and h >= 24 then scale = 2
  120.   elseif w >= 32 and h >= 12 then scale = 1
  121.   else scale = 0.5 end
  122.   -- Clamp to valid scales 0.5..5
  123.   if monitor.setTextScale then
  124.     monitor.setTextScale(scale)
  125.     w, h = monitor.getSize() -- recompute after scaling
  126.   end
  127. end
  128.  
  129. local function setPalette()
  130.   if monitor.setBackgroundColor then
  131.     pcall(monitor.setBackgroundColor, colors.black)
  132.     pcall(monitor.setTextColor, colors.white)
  133.   end
  134. end
  135.  
  136. monitor.setCursorBlink(false)
  137. setPalette()
  138. monitor.clear()
  139.  
  140. -- ========= Stats =========
  141. local interval = DEFAULT_INTERVAL
  142. local EMA_ALPHA = 0.2             -- smoothing for EMA
  143. local ema_rt = nil                -- EMA of FE/t
  144. local peak_rt = 0                 -- rolling peak (decays slowly)
  145. local PEAK_DECAY = 0.98           -- decay per sample to keep bar meaningful
  146. local history = {}                -- for sparkline (last N samples)
  147. local histMax = math.max(10, w - 2)
  148.  
  149. -- ========= Drawing =========
  150. local title = "Incoming Power"
  151. local function drawFrame()
  152.   clearFrameCache()
  153.   monitor.clear()
  154.   local dateStr = textutils.formatTime(os.time(), true) or ""
  155.   local header = title
  156.   if detectorName then header = header .. "  [" .. detectorName .. "]" end
  157.   if monitorName and not usingTerm then header = header .. "  -> " .. monitorName end
  158.   -- Trim header if too long
  159.   if #header > w then header = header:sub(1, w) end
  160.   blitLine(monitor, 1, header)
  161.   if h >= 3 then blitLine(monitor, 2, string.rep("-", w)) end
  162. end
  163.  
  164. local function drawStats(rt_fept)
  165.   local line = 3
  166.   if h < 5 then
  167.     -- Very small target—just one line
  168.     local s = ("FE/t: %s"):format(fmtSI(rt_fept))
  169.     blitLine(monitor, line, s:sub(1, w))
  170.     return
  171.   end
  172.  
  173.   -- EMA
  174.   ema_rt = ema_rt and (ema_rt + EMA_ALPHA * (rt_fept - ema_rt)) or rt_fept
  175.  
  176.   -- Peak with decay
  177.   peak_rt = math.max(peak_rt * PEAK_DECAY, rt_fept)
  178.  
  179.   -- Derived units
  180.   local rtf = fmtSI(rt_fept) .. " FE/t"
  181.   local rps = fmtSI(rt_fept * 20) .. " FE/s"
  182.   local emaf = fmtSI(ema_rt) .. " FE/t"
  183.   local peakf = fmtSI(peak_rt) .. " FE/t"
  184.  
  185.   blitLine(monitor, line,   ("Now : %s"):format(rtf):sub(1, w)); line = line + 1
  186.   blitLine(monitor, line,   ("EMA : %s"):format(emaf):sub(1, w)); line = line + 1
  187.   blitLine(monitor, line,   ("Peak: %s"):format(peakf):sub(1, w)); line = line + 1
  188.   blitLine(monitor, line,   ("Rate: %s"):format(rps):sub(1, w)); line = line + 1
  189.  
  190.   -- Bar (relative to peak to keep scale useful)
  191.   if line <= h then
  192.     local barWidth = math.max(0, w - 10)
  193.     local ratio = peak_rt > 0 and (rt_fept / peak_rt) or 0
  194.     local filled = math.floor(barWidth * clamp(ratio, 0, 1))
  195.     local bar = "[" .. string.rep("=", filled) .. string.rep(" ", barWidth - filled) .. "]"
  196.     blitLine(monitor, line, ("Load: %s"):format(bar):sub(1, w))
  197.     line = line + 1
  198.   end
  199.  
  200.   -- Sparkline
  201.   if line <= h then
  202.     -- maintain history
  203.     table.insert(history, rt_fept)
  204.     if #history > histMax then table.remove(history, 1) end
  205.     local m = 0
  206.     for i = 1, #history do if history[i] > m then m = history[i] end end
  207.     if m <= 0 then m = 1 end
  208.     local sparkw = math.min(w, #history)
  209.     local s = {}
  210.     for i = #history - sparkw + 1, #history do
  211.       local v = history[i] or 0
  212.       table.insert(s, sparkChar(v / m))
  213.     end
  214.     blitLine(monitor, line, table.concat(s))
  215.     line = line + 1
  216.   end
  217.  
  218.   -- Footer time
  219.   if line <= h then
  220.     local t = "Last update: " .. (textutils.formatTime(os.time(), true) or "")
  221.     blitLine(monitor, h, t:sub(1, w))
  222.   end
  223. end
  224.  
  225. -- ========= Main loop =========
  226. drawFrame()
  227.  
  228. while true do
  229.   -- Safe poll
  230.   local ok, val = pcall(detector.getTransferRate, detector)
  231.   local rt = (ok and type(val) == "number") and val or 0
  232.   setPalette()
  233.   drawStats(rt)
  234.   sleep(interval)
  235. end
  236.  
Advertisement
Comments
  • okahfou
    2 days
    # text 0.14 KB | 0 0
    1. UPTADE
    2. ⭐⭐ Make 3000$ with Swapzone Exchange Method ⭐⭐
    3. >>> docs.google.com/document/d/1IB4SkLZdU8hex0Kn-GyFHXSSV6kLUXhhOhxPwmEuuc4
Add Comment
Please, Sign In to add comment