MFeltmann

Draconic energy core monitor

Jul 30th, 2026 (edited)
7
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.25 KB | None | 0 0
  1. --[[
  2. Draconic Energy Core Monitor 2.0 for ATM10
  3. Features: auto-find monitor/core, dynamic layout, tier auto-detect, flux stats & graph.
  4. Install: Place on a CC:Tweaked computer, run; monitor must be placed nearby.
  5. --]]
  6.  
  7. -- Configuration
  8. UPDATE_DELAY = 0.25 -- seconds between updates
  9. GRAPH_POINTS = 600 -- history length for graph (max points)
  10. AVERAGE_SECONDS = 10 -- seconds for rolling average (unused in this version)
  11. AUTO_SCALE = true -- auto-scale graph Y-axis
  12. GRAPH_MODES = {"LINE","BAR","AREA","SMOOTH"} -- graph styles
  13.  
  14. -- Color theme
  15. local THEME = {
  16. bg = colors.black,
  17. text = colors.white,
  18. barEmpty = colors.gray,
  19. barFill = colors.lime,
  20. graphFillPos = colors.green,
  21. graphFillNeg = colors.red
  22. }
  23.  
  24. -- Safe write (handles colors)
  25. local function safeWrite(mon, x, y, txt, fg, bg)
  26. mon.setCursorPos(x, y)
  27. if fg then mon.setTextColor(fg) end
  28. if bg then mon.setBackgroundColor(bg) end
  29. mon.write(txt)
  30. end
  31.  
  32. -- Find first monitor and core peripheral
  33. local function findMonitor()
  34. for _,name in ipairs(peripheral.getNames()) do
  35. if peripheral.getType(name) == "monitor" then
  36. return peripheral.wrap(name), name
  37. end
  38. end
  39. return nil, nil
  40. end
  41.  
  42. local function findEnergyCore()
  43. for _,name in ipairs(peripheral.getNames()) do
  44. if peripheral.getType(name) == "draconic_rf_storage" then
  45. return peripheral.wrap(name), name
  46. end
  47. end
  48. return nil, nil
  49. end
  50.  
  51. -- Map maxEnergy to tier (ATM10 standard values)
  52. local function getCoreTier(maxEnergy)
  53. if maxEnergy >= 9223372036854775807 then return 8
  54. elseif maxEnergy >= 2140000000000 then return 7
  55. elseif maxEnergy >= 356000000000 then return 6
  56. elseif maxEnergy >= 59300000000 then return 5
  57. elseif maxEnergy >= 9880000000 then return 4
  58. elseif maxEnergy >= 1640000000 then return 3
  59. elseif maxEnergy >= 273000000 then return 2
  60. else return 1 end
  61. end
  62.  
  63. -- Format large number with suffix (k,M,G,T...)
  64. local function formatNumber(num)
  65. local abs = math.abs(num)
  66. if abs >= 1e12 then return string.format("%.2fT", num/1e12)
  67. elseif abs >= 1e9 then return string.format("%.2fG", num/1e9)
  68. elseif abs >= 1e6 then return string.format("%.2fM", num/1e6)
  69. elseif abs >= 1e3 then return string.format("%.1fk", num/1e3)
  70. else return tostring(num) end
  71. end
  72.  
  73. -- Initialization: find peripherals, wrap them, clear screen
  74. local mon, monName, core, coreName
  75. local function initialize()
  76. mon, monName = findMonitor()
  77. if not mon then
  78. print("Error: Monitor not found!")
  79. return false
  80. end
  81. core, coreName = findEnergyCore()
  82. if not core then
  83. print("Error: Energy Core not found!")
  84. return false
  85. end
  86. if mon.setTextScale then mon.setTextScale(1) end
  87. mon.setBackgroundColor(THEME.bg)
  88. mon.clear()
  89. return true
  90. end
  91.  
  92. -- Keep trying to initialize
  93. while not initialize() do
  94. os.sleep(1)
  95. end
  96.  
  97. -- Data history and stats
  98. local fluxHistory = {}
  99. local peakIn, peakOut = 0, 0
  100. local startTime = os.epoch("utc")/1000
  101. local lastEnergy = core.getEnergyStored()
  102. local lastTime = os.epoch("utc")/1000
  103. local graphModeIndex = 1
  104.  
  105. -- Main loop
  106. while true do
  107. -- Reinitialize if peripherals disconnected
  108. if not peripheral.isPresent(monName) or not peripheral.isPresent(coreName) then
  109. initialize()
  110. fluxHistory = {}
  111. peakIn, peakOut = 0, 0
  112. startTime = os.epoch("utc")/1000
  113. lastEnergy = core.getEnergyStored()
  114. lastTime = os.epoch("utc")/1000
  115. end
  116.  
  117. -- Read core energy (safe call)
  118. local ok, energy = pcall(core.getEnergyStored, core)
  119. if not ok then
  120. initialize()
  121. goto continue
  122. end
  123. local maxEnergy = core.getMaxEnergyStored()
  124. local stored = energy
  125. local percent = stored / maxEnergy * 100
  126. local freeSpace = maxEnergy - stored
  127.  
  128. -- Determine tier
  129. local tier = core.getTier and core.getTier() or getCoreTier(maxEnergy)
  130.  
  131. -- Read transfer per tick (net)
  132. local transfer = core.getTransferPerTick()
  133. -- Attempt to use separate input/output if available
  134. local inputRate, outputRate = 0, 0
  135. if core.getInputPerTick and core.getOutputPerTick then
  136. inputRate = core.getInputPerTick()
  137. outputRate = core.getOutputPerTick()
  138. else
  139. -- Fallback: compute net flux per tick (assuming 20 ticks/sec)
  140. local now = os.epoch("utc")/1000
  141. local dt = now - lastTime
  142. local delta = stored - lastEnergy
  143. local rate = delta / (dt * 20)
  144. if rate >= 0 then inputRate = rate else outputRate = -rate end
  145. lastEnergy = stored
  146. lastTime = now
  147. end
  148.  
  149. -- Update peaks
  150. if inputRate > peakIn then peakIn = inputRate end
  151. if outputRate > peakOut then peakOut = outputRate end
  152.  
  153. -- Record net flux (positive=charging, negative=discharging)
  154. table.insert(fluxHistory, inputRate - outputRate)
  155. if #fluxHistory > GRAPH_POINTS then table.remove(fluxHistory, 1) end
  156.  
  157. -- Begin rendering
  158. local w, h = mon.getSize()
  159. mon.setBackgroundColor(THEME.bg)
  160. mon.clear()
  161.  
  162. -- Title (centered)
  163. safeWrite(mon, math.floor((w-20)/2), 1, "DRACONIC ENERGY CORE", THEME.text, THEME.bg)
  164.  
  165. -- Storage bar (25% width)
  166. local barWidth = math.floor(w * 0.25)
  167. local filled = math.floor(barWidth * (stored / maxEnergy))
  168. for i=1,barWidth do
  169. if i <= filled then
  170. safeWrite(mon, i, 3, "█", THEME.text, THEME.barFill)
  171. else
  172. safeWrite(mon, i, 3, "█", THEME.text, THEME.barEmpty)
  173. end
  174. end
  175. safeWrite(mon, barWidth+2, 3,
  176. "Stored: "..formatNumber(stored).." / "..formatNumber(maxEnergy), THEME.text, THEME.bg)
  177. safeWrite(mon, barWidth+2, 4,
  178. string.format("Free: %s (%d%%)", formatNumber(freeSpace), math.floor(percent+0.5)), THEME.text, THEME.bg)
  179.  
  180. -- Tier
  181. safeWrite(mon, 1, 5, "Tier: "..tier, THEME.text, THEME.bg)
  182. -- Current In/Out
  183. safeWrite(mon, 1, 6, "In: "..formatNumber(inputRate).." RF/t", THEME.text, THEME.bg)
  184. safeWrite(mon, math.floor(w/2)+1, 6, "Out: "..formatNumber(outputRate).." RF/t", THEME.text, THEME.bg)
  185. -- Peak In/Out
  186. safeWrite(mon, 1, 7, "Peak In: "..formatNumber(peakIn).." RF/t", THEME.text, THEME.bg)
  187. safeWrite(mon, math.floor(w/2)+1, 7, "Peak Out: "..formatNumber(peakOut).." RF/t", THEME.text, THEME.bg)
  188. -- ETA to full/empty
  189. if inputRate > 0 then
  190. local sec = (maxEnergy - stored) / (inputRate * 20)
  191. safeWrite(mon, 1, 8, "ETA Full: "..os.date("%H:%M:%S", sec), THEME.text, THEME.bg)
  192. else
  193. safeWrite(mon, 1, 8, "ETA Full: --:--:--", THEME.text, THEME.bg)
  194. end
  195. if outputRate > 0 then
  196. local sec = stored / (outputRate * 20)
  197. safeWrite(mon, math.floor(w/2)+1, 8, "ETA Empty: "..os.date("%H:%M:%S", sec), THEME.text, THEME.bg)
  198. else
  199. safeWrite(mon, math.floor(w/2)+1, 8, "ETA Empty: --:--:--", THEME.text, THEME.bg)
  200. end
  201.  
  202. -- Uptime
  203. local elapsed = os.epoch("utc")/1000 - startTime
  204. safeWrite(mon, 1, 9,
  205. "Runtime: "..os.date("!%Hh%Mm%Ss", elapsed), THEME.text, THEME.bg)
  206.  
  207. -- Separator line before graph
  208. for x=1,w do safeWrite(mon, x, 10, "-", THEME.text, THEME.bg) end
  209.  
  210. -- Graph area (from line 11 to bottom)
  211. local graphOffset = 10
  212. local graphHeight = h - graphOffset
  213. local maxVal, minVal = 0, 0
  214. for _,v in ipairs(fluxHistory) do
  215. if v > maxVal then maxVal = v end
  216. if v < minVal then minVal = v end
  217. end
  218. if maxVal == minVal then
  219. maxVal = math.max(1, maxVal); minVal = -maxVal
  220. end
  221. local maxAbs = math.max(math.abs(maxVal), math.abs(minVal))
  222. local scale = (graphHeight/2) / maxAbs
  223.  
  224. for i=1,w do
  225. local idx = #fluxHistory - w + i
  226. if idx >= 1 and fluxHistory[idx] then
  227. local val = fluxHistory[idx]
  228. local color = (val >= 0) and THEME.graphFillPos or THEME.graphFillNeg
  229. local barLen = math.floor(val * scale + 0.5)
  230. if GRAPH_MODES[graphModeIndex] == "BAR" then
  231. -- Draw vertical bar from center line
  232. if barLen >= 0 then
  233. for y=1,barLen do
  234. safeWrite(mon, i, graphOffset + math.floor(graphHeight/2) - y,
  235. "█", THEME.text, color)
  236. end
  237. else
  238. for y=1,-barLen do
  239. safeWrite(mon, i, graphOffset + math.floor(graphHeight/2) + y,
  240. "█", THEME.text, color)
  241. end
  242. end
  243. else
  244. -- LINE/AREA/SMOOTH: draw a point
  245. local midLine = graphOffset + math.floor(graphHeight/2)
  246. safeWrite(mon, i, midLine - barLen, "●", THEME.text, color)
  247. end
  248. end
  249. end
  250.  
  251. -- Handle touch to toggle graph mode
  252. local timer = os.startTimer(UPDATE_DELAY)
  253. while true do
  254. local event, side, x, y = os.pullEvent()
  255. if event == "timer" then break end
  256. if event == "monitor_touch" and side == monName then
  257. graphModeIndex = (graphModeIndex % #GRAPH_MODES) + 1
  258. break
  259. end
  260. end
  261.  
  262. ::continue::
  263. end
  264.  
Advertisement
Add Comment
Please, Sign In to add comment