Gomo

RF Control Center

Nov 27th, 2020
1,278
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Main configuration
  2. local tArgs = {...}
  3. local CONFIG_FILE = "power_conf"
  4.  
  5. local CONFIG_TEMPLATE = [[-- adjust these configuration options as necessary.
  6. -- delay for checking all capacitors
  7. TICK_DELAY = ${tickDelay}
  8.  
  9. -- threshold in percentages
  10. GREEN_ZONE = ${greenZone}
  11. YELLOW_ZONE = ${yellowZone}
  12.  
  13. NORMAL_POWER_THRESHOLD = ${nomalPowerThreshold}
  14. LOW_POWER_THRESHOLD = ${lowPowerThreshold}
  15.  
  16. -- configures what side to emit when low power
  17. -- a valid side is required.
  18. SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER = "${sideForRedstone}"
  19.  
  20. -- Active monitors on startup
  21. MONITORS_ACTIVE = ${monitorsActive}
  22. ]]
  23.  
  24. local MONITORS_ACTIVE = {}
  25.  
  26. do
  27.     if #tArgs > 0 and tArgs[1] == "update" then
  28.         print("Updating RFCC...")
  29.         local updateFile = "/rfcc_update"
  30.         local pastebinKey = "TfeHE7Wy"
  31.         shell.run("pastebin", "get", pastebinKey, updateFile)
  32.  
  33.         if fs.exists(updateFile) then
  34.             local programPath = shell.getRunningProgram()
  35.             fs.delete(programPath)
  36.             fs.move(updateFile, programPath)
  37.             print("Success!")
  38.             return
  39.         else
  40.             print("Unable to retrieve update from pastebin.")
  41.             print("Check your connection, and try again.")
  42.             error()
  43.         end
  44.     end
  45. end
  46.  
  47. local function interpolate(s, params)
  48.     return s:gsub('($%b{})', function(w) return params[w:sub(3, -2)] or w end)
  49. end
  50.  
  51. local function saveSettings()
  52.     local h = fs.open("/"..CONFIG_FILE, "w")
  53.  
  54.     local settings = {
  55.         tickDelay = TICK_DELAY or 100,
  56.         greenZone = GREEN_ZONE or 70,
  57.         yellowZone = YELLOW_ZONE or 30,
  58.         nomalPowerThreshold = NORMAL_POWER_THRESHOLD or 90,
  59.         lowPowerThreshold = LOW_POWER_THRESHOLD or 10,
  60.         sideForRedstone = SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER or "bottom",
  61.         monitorsActive = textutils.serialize(MONITORS_ACTIVE)
  62.     }
  63.  
  64.     h.write(interpolate(CONFIG_TEMPLATE, settings))
  65.     h.close()
  66. end
  67.  
  68.  
  69. if fs.exists(CONFIG_FILE) == false then
  70.     print("I can't find my configuration file.")
  71.     print("Generating one for you.")
  72.  
  73.     saveSettings()
  74.  
  75.     print("")
  76.     print("Configuration file is located at /"..CONFIG_FILE)
  77.     print("You may edit this file to change my default settings.")
  78.     print("Once satisfied, run me again.")
  79.     return
  80. else
  81.     os.unloadAPI(CONFIG_FILE)
  82.  
  83.     if os.loadAPI("/"..CONFIG_FILE) == false then
  84.         error("Could not load the config file!")
  85.     end
  86.  
  87.     CONFIG = nil
  88.     for k, v in pairs(_G) do
  89.         if k == CONFIG_FILE then
  90.             CONFIG = v
  91.             break
  92.         end
  93.     end
  94.  
  95.     if CONFIG == nil then
  96.         print("I could not find the necessary config.")
  97.         print("You probably screwed something up.")
  98.         error()
  99.     end
  100. end
  101.  
  102. term.setCursorPos(1, 1)
  103. term.clear()
  104. print("Starting RF Control Center...")
  105. sleep(2)
  106.  
  107. -- Constants
  108. local GET_ENERGY_STORED_FUNCTION="getEnergyStored"
  109. local GET_MAX_ENERGY_STORED_FUNCTION="getMaxEnergyStored"
  110.  
  111. local TICK_DELAY = CONFIG.TICK_DELAY
  112. local PAUSE_TIME_IN_SECONDS = TICK_DELAY / 20
  113.  
  114. -- threshold in percentages
  115. local GREEN_ZONE = CONFIG.GREEN_ZONE
  116. local YELLOW_ZONE = CONFIG.YELLOW_ZONE
  117.  
  118. local NORMAL_POWER_THRESHOLD = CONFIG.NORMAL_POWER_THRESHOLD
  119. local LOW_POWER_THRESHOLD = CONFIG.LOW_POWER_THRESHOLD
  120.  
  121. -- configures what side to emit when low power
  122. local SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER = CONFIG.SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER
  123.  
  124. MONITORS_ACTIVE = CONFIG.MONITORS_ACTIVE
  125.  
  126. -- state variables
  127. local clickableLines = {}
  128. local clickableOutputMonitors = {}
  129. local monitors = {}
  130. local capacitors = {}
  131. local dashboardButtons = {}
  132. local totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
  133.  
  134. -- Capacitor basic functions
  135. do
  136.     capacitors.add = function(params)
  137.         table.insert(capacitors, params)
  138.     end
  139.  
  140.     capacitors.get = function(name)
  141.         for i, v in ipairs(capacitors) do
  142.             if name == v.name then
  143.                 return v
  144.             end
  145.         end
  146.  
  147.         return nil
  148.     end
  149.  
  150.     capacitors.remove = function(name)
  151.         local found = nil
  152.         for i, v in ipairs(capacitors) do
  153.             if name == v.name then
  154.                 found = i
  155.                 break
  156.             end
  157.         end
  158.  
  159.         if found then
  160.             table.remove(capacitors, found)
  161.         end
  162.     end
  163.  
  164.     capacitors.clear = function()
  165.         for i, v in ipairs(capacitors) do
  166.             capacitors[i] = nil
  167.         end
  168.     end
  169. end
  170.  
  171.  
  172. -- Special Windows
  173. local nativeDisplayTabs = {}
  174. local nativeMonitorTabWindow
  175. local consoleWindow
  176. local monitorSelectionWindow
  177.  
  178. do
  179.     local nativeTerm = term.native()
  180.     local width, height = term.getSize()
  181.     local x, y = 1, 2
  182.     local newWidth, newHeight = width, height - 1
  183.  
  184.     nativeTerm.setCursorPos(x, y)
  185.  
  186.     nativeMonitorTabWindow = window.create(nativeTerm, 1, 1, width, 1, false)
  187.  
  188.     consoleWindow = window.create(nativeTerm, x, y, newWidth, newHeight, false)
  189.     consoleWindow.active = true
  190.  
  191.     monitorSelectionWindow = window.create(nativeTerm, x, y, newWidth, newHeight, false)
  192.     monitorSelectionWindow.active = true
  193. end
  194.  
  195. -- TODO: break up this humongous script into smaller chunks that can be loaded
  196. --       via os.loadAPI().
  197.  
  198.  
  199. -- basic functions
  200. local function tableSize(targetTable)
  201.     local i = 0
  202.     for k, v in pairs(targetTable) do
  203.         i = i + 1
  204.     end
  205.  
  206.     return i
  207. end
  208.  
  209. local function padRight(text, width, padCharacter)
  210.     if width == nil then
  211.         width = term.getSize()
  212.     end
  213.  
  214.     padCount = width - string.len(text)
  215.  
  216.     if padCharacter == nil then
  217.         padCharacter = " "
  218.     end
  219.  
  220.     if padCount > 0 then
  221.         return text..string.rep(padCharacter, padCount)
  222.     else
  223.         return text
  224.     end
  225. end
  226.  
  227. local function padLeft(text, width, padCharacter)
  228.     if width == nil then
  229.         width = term.getSize()
  230.     end
  231.  
  232.     padCount = width - string.len(text)
  233.  
  234.     if padCharacter == nil then
  235.         padCharacter = " "
  236.     end
  237.  
  238.     if padCount > 0 then
  239.         return string.rep(padCharacter, padCount)..text
  240.     else
  241.         return text
  242.     end
  243. end
  244.  
  245. local function printZoneText(percent, callback)
  246.     if percent >= GREEN_ZONE then
  247.         term.setTextColor(colors.green)
  248.     elseif percent >= YELLOW_ZONE and percent < GREEN_ZONE then
  249.         term.setTextColor(colors.yellow)
  250.     else
  251.         term.setTextColor(colors.red)
  252.     end
  253.  
  254.     callback()
  255.  
  256.     term.setTextColor(colors.white)
  257. end
  258.  
  259. local function resetRedstoneState()
  260.     for k,v in pairs(rs.getSides()) do
  261.         rs.setOutput(v, false)
  262.     end
  263. end
  264. -- basic functions
  265.  
  266.  
  267. -- line drawing API
  268. local function drawVerticalLine(targetWindow, x, y, height)
  269.     targetWindow.setCursorPos(x, y)
  270.  
  271.     targetWindow.setBackgroundColor(colors.blue)
  272.     for i = 1, height do
  273.         targetWindow.write(" ")
  274.         targetWindow.setCursorPos(x, i)
  275.     end
  276.     targetWindow.setBackgroundColor(colors.black)
  277. end
  278.  
  279. local function drawHorizontalLine(targetWindow, x, y, width)
  280.     targetWindow.setCursorPos(x, y)
  281.     targetWindow.setBackgroundColor(colors.blue)
  282.     targetWindow.write(string.rep(" ", width))
  283.     targetWindow.setBackgroundColor(colors.black)
  284. end
  285. -- line drawing API
  286.  
  287.  
  288. -- window management
  289. local console = {
  290.     log = function(message)
  291.         local currentTerm = term.current()
  292.         term.redirect(consoleWindow)
  293.  
  294.         print(message)
  295.  
  296.         term.redirect(currentTerm)
  297.     end
  298. }
  299.  
  300. local function showWindows(...)
  301.     for i, v in ipairs(arg) do
  302.         if v.active == true then
  303.             v.setVisible(true)
  304.         else
  305.             v.setVisible(false)
  306.         end
  307.     end
  308. end
  309.  
  310. local function hideWindows(...)
  311.     for i, v in ipairs(arg) do
  312.         v.setVisible(false)
  313.     end
  314. end
  315.  
  316. local function getCursorPositionRelativeToParent(currentWindow)
  317.     -- determine offset of current window from parent
  318.     local x, y = currentWindow.getPosition()
  319.     local xOffset, yOffset = x - 1, y - 1
  320.    
  321.     local cursorX, cursorY = currentWindow.getCursorPos()
  322.     return cursorX + xOffset, cursorY + yOffset
  323. end
  324.  
  325. local function createInformationWindow(parentWindow)
  326.     local width, height = parentWindow.getSize()
  327.  
  328.     local widthOffset = 2
  329.     local heightOffset = 2
  330.  
  331.     local windowWidth = width - (widthOffset * 2)
  332.     local windowHeight = height - (heightOffset * 2)
  333.  
  334.     local informationWindow = window.create(parentWindow, 1 + widthOffset, 1 + heightOffset, windowWidth, windowHeight, false)
  335.     informationWindow.active = false
  336.  
  337.     drawHorizontalLine(informationWindow, 1, 1, windowWidth)
  338.     drawHorizontalLine(informationWindow, 1, windowHeight, windowWidth)
  339.  
  340.     drawVerticalLine(informationWindow, 1, 1, windowHeight)
  341.     drawVerticalLine(informationWindow, windowWidth, 1, windowHeight)
  342.  
  343.     return informationWindow
  344. end
  345.  
  346. local function createSummaryWindow(parentWindow, x, y)
  347.     local width, height = parentWindow.getSize()
  348.  
  349.     -- we make use of the parent window's cursor position to make it more convenient.
  350.     local x, y = parentWindow.getCursorPos()
  351.     local newHeight = height - (y - 1)
  352.  
  353.     local summaryWindow = window.create(parentWindow, x, y, width, newHeight, false)
  354.     summaryWindow.active = false
  355.  
  356.     return summaryWindow
  357. end
  358.  
  359. local function printToWindow(targetWindow, widthOffset, text)
  360.     local x, y = targetWindow.getCursorPos()
  361.     local width, height = targetWindow.getSize()
  362.     local maxTextSize = width - (widthOffset * 2)
  363.  
  364.     targetWindow.write(text:sub(1, maxTextSize))
  365.     targetWindow.setCursorPos(x, y+1)
  366. end
  367.  
  368. local function createDashboardWindows(parentWindow)
  369.     -- order is important here!
  370.     local summaryWindow = createSummaryWindow(parentWindow)
  371.     summaryWindow.active = true
  372.     local informationWindow = createInformationWindow(parentWindow)
  373.     informationWindow.active = false
  374.  
  375.     local windows = {
  376.         [1] = summaryWindow,
  377.         [2] = informationWindow,
  378.  
  379.         getSummaryWindow = function()
  380.             return summaryWindow
  381.         end,
  382.  
  383.         getInformationWindow = function()
  384.             return informationWindow
  385.         end
  386.     }
  387.  
  388.     return windows
  389. end
  390.  
  391. local function initializeNativeDisplayTabs()
  392.     local nativeTerm = term.native()
  393.     nativeTerm.setCursorPos(1, 2)
  394.  
  395.     local dashboardWindows = createDashboardWindows(nativeTerm)
  396.  
  397.     table.insert(nativeDisplayTabs, {
  398.         tab = {
  399.             label = "Dashboard",
  400.             event = "dashboard_clicked",
  401.             active = true,
  402.             startX = 0,
  403.             startY = 0
  404.         },
  405.  
  406.         windows = dashboardWindows
  407.     })
  408.     table.insert(nativeDisplayTabs, {
  409.         tab = {
  410.             label = "Monitors",
  411.             event = "monitors_clicked",
  412.             startX = 0,
  413.             startY = 0
  414.         },
  415.  
  416.         windows = { monitorSelectionWindow }
  417.     })
  418.     table.insert(nativeDisplayTabs, {
  419.         tab = {
  420.             label = "Console",
  421.             event = "console_clicked",
  422.             startX = 0,
  423.             startY = 0
  424.         },
  425.  
  426.         windows = { consoleWindow }
  427.     })
  428.  
  429.     nativeDisplayTabs.getSelectedTab = function(x, y)
  430.         if x == nil or y == nil then
  431.             return nil
  432.         end
  433.  
  434.         for i, v in ipairs(nativeDisplayTabs) do
  435.             local tab = v.tab
  436.             local withinX = x >= tab.startX and x <= tab.endX
  437.             local withinY = y >= tab.startY and y <= tab.endY
  438.  
  439.             if withinX and withinY then
  440.                 return i
  441.             end
  442.         end
  443.  
  444.         return nil
  445.     end
  446.  
  447.     nativeDisplayTabs.setSelectedTab = function(selected)
  448.         for i, v in ipairs(nativeDisplayTabs) do
  449.             if i == selected then
  450.                 v.tab.active = true
  451.             else
  452.                 v.tab.active = false
  453.             end
  454.         end
  455.     end
  456.  
  457.     nativeDisplayTabs.getActiveTab = function()
  458.         for i, v in ipairs(nativeDisplayTabs) do
  459.             if v.tab.active == true then
  460.                 return i
  461.             end
  462.         end
  463.     end
  464.  
  465.     nativeDisplayTabs.getDashboardWindows = function()
  466.         return dashboardWindows
  467.     end
  468. end
  469.  
  470. -- window management
  471.  
  472.  
  473. -- capacitor management
  474. local function addCapacitors(...)
  475.     local peripheralList = arg
  476.  
  477.     if #peripheralList == 0 then
  478.         peripheralList = peripheral.getNames()
  479.         capacitors.clear()
  480.     end
  481.  
  482.     for i, p in ipairs(peripheralList) do
  483.         local currentPeripheral = peripheral.wrap(p)
  484.  
  485.         if currentPeripheral[GET_ENERGY_STORED_FUNCTION] ~= nil and currentPeripheral[GET_MAX_ENERGY_STORED_FUNCTION] ~= nil and currentPeripheral[GET_ENERGY_STORED_FUNCTION]("north") ~= nil then
  486.             console.log("Adding new capacitor: "..p)
  487.             capacitors.add({
  488.                 name = p,
  489.                 peripheral = currentPeripheral,
  490.                 lastReading = 0,
  491.                 flowRate = 0,
  492.                 percent = 0
  493.             })
  494.         end
  495.     end
  496. end
  497.  
  498. local function removeCapacitors(...)
  499.     for i, k in ipairs(arg) do
  500.         capacitors.remove(k)
  501.     end
  502. end
  503.  
  504. local function getReading()
  505.     local totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
  506.  
  507.     for i, v in ipairs(capacitors) do
  508.         local currentReading = v.peripheral[GET_ENERGY_STORED_FUNCTION]("north") or 0
  509.         local capacity = v.peripheral[GET_MAX_ENERGY_STORED_FUNCTION]("north") or 0
  510.  
  511.         if currentReading ~= nil then
  512.             v.flowRate = (currentReading - v.lastReading) / TICK_DELAY
  513.             v.lastReading = currentReading
  514.  
  515.             if capacity == 0 then
  516.                 v.percent = 0
  517.             else
  518.                 v.percent = math.floor((currentReading / capacity) * 100)
  519.             end
  520.  
  521.             totalEnergyAvailable = totalEnergyAvailable + v.lastReading
  522.             totalFlowRate = totalFlowRate + v.flowRate
  523.         end
  524.  
  525.         totalCapacity = totalCapacity + capacity
  526.     end
  527.  
  528.     local sortByLastReading = function(a, b)
  529.         return a.percent > b.percent
  530.     end
  531.  
  532.     table.sort(capacitors, sortByLastReading)
  533.  
  534.     return totalEnergyAvailable, totalCapacity, totalFlowRate
  535. end
  536.  
  537. local function emitRedstoneSignalOnLowPower(percent)
  538.     if percent < LOW_POWER_THRESHOLD and rs.getOutput(SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER) == false then
  539.         console.log("Low power threshold reached.")
  540.         rs.setOutput(SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER, true)
  541.     elseif percent >= NORMAL_POWER_THRESHOLD and rs.getOutput(SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER) == true then
  542.         console.log("Back to normal power levels.")
  543.         rs.setOutput(SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER, false)
  544.     end
  545. end
  546. -- capacitor management
  547.  
  548.  
  549. -- monitor management
  550. local function addMonitors(...)
  551.     local monitorList = arg
  552.  
  553.     if #monitorList == 0 then
  554.         monitorList = peripheral.getNames()
  555.         monitors = {}
  556.     end
  557.  
  558.     for i, m in ipairs(monitorList) do
  559.         local currentPeripheral = peripheral.wrap(m)
  560.  
  561.         if "monitor" == peripheral.getType(m) and currentPeripheral.isColour() == true then
  562.             console.log("Adding new monitor: "..m)
  563.             currentPeripheral.setCursorPos(1, 1)
  564.             monitors[m] = {
  565.                 peripheral = currentPeripheral,
  566.                 windows = createDashboardWindows(currentPeripheral),
  567.                 active = false
  568.             }
  569.         end
  570.     end
  571. end
  572.  
  573. local function removeMonitors(...)
  574.     local activeMonitorsCount = tableSize(MONITORS_ACTIVE)
  575.  
  576.     for i, k in ipairs(arg) do
  577.         monitors[k] = nil
  578.         dashboardButtons[k] = nil
  579.         MONITORS_ACTIVE[k] = nil
  580.     end
  581.  
  582.     if activeMonitorsCount ~= tableSize(MONITORS_ACTIVE) then
  583.         saveSettings()
  584.     end
  585. end
  586. -- monitor management
  587.  
  588.  
  589. -- hotplug system
  590. local function doWhileMonitorSuspended(callback)
  591.     os.queueEvent("pause_monitor")
  592.     callback()
  593.     os.queueEvent("resume_monitor")
  594. end
  595.  
  596. local function hotplugPeripherals()
  597.     while true do
  598.         sleep(TICK_DELAY)
  599.         local event, name = os.pullEvent()
  600.         local callback = nil
  601.  
  602.         if event == "peripheral" then
  603.             console.log("Detected new peripheral: "..name)
  604.  
  605.             callback = function()
  606.                 addMonitors(name)
  607.                 addCapacitors(name)
  608.             end
  609.         elseif event == "peripheral_detach" then
  610.             console.log("Peripheral removed: "..name)
  611.  
  612.             callback = function()
  613.                 removeMonitors(name)
  614.                 removeCapacitors(name)
  615.             end
  616.         elseif event == "monitor_resize" then
  617.             console.log("Monitor resized: "..name)
  618.  
  619.             callback = function()
  620.                 monitors[name].peripheral.setCursorPos(1, 1)
  621.                 monitors[name].windows = createDashboardWindows(monitors[name].peripheral)
  622.                 dashboardButtons[name] = nil
  623.  
  624.                 if monitors[name].active == true then
  625.                     showWindows(unpack(monitors[name].windows))
  626.                 end
  627.             end
  628.         end
  629.  
  630.         if callback ~= nil then
  631.             doWhileMonitorSuspended(callback)
  632.         end
  633.     end
  634. end
  635. -- hotplug system
  636.  
  637.  
  638. -- information window for the capacitors
  639. local function addClickableLine(monitorName, key, currentY)
  640.     clickableLines[monitorName][key] = {
  641.         line = currentY
  642.     }
  643. end
  644.  
  645. local function toggleInformationWindow(summaryWindow, informationWindow, capacitorName)
  646.     if capacitorName == nil then
  647.         summaryWindow.active = true
  648.         informationWindow.active = false
  649.     else
  650.         summaryWindow.active = not summaryWindow.active
  651.         informationWindow.active = not informationWindow.active
  652.     end
  653.  
  654.     local capacitor = capacitors.get(capacitorName)
  655.  
  656.     if informationWindow.active == true then
  657.         widthOffset = 3
  658.         heightOffset = 3
  659.  
  660.         informationWindow.setCursorPos(widthOffset, heightOffset)
  661.         local width, height = informationWindow.getSize()
  662.         local labelWidth = width - (widthOffset * 2)
  663.         local capacity = capacitor.peripheral[GET_MAX_ENERGY_STORED_FUNCTION]("north")
  664.  
  665.         printToWindow(informationWindow, widthOffset, "Capacitor name:")
  666.         printToWindow(informationWindow, widthOffset, padRight("    "..capacitorName, labelWidth))
  667.         printToWindow(informationWindow, widthOffset, "Capacitor type:")
  668.         printToWindow(informationWindow, widthOffset, padRight("    "..peripheral.getType(capacitorName), labelWidth))
  669.         printToWindow(informationWindow, widthOffset, "Capacity:")
  670.         printToWindow(informationWindow, widthOffset, padRight("    "..capacity.." RF", labelWidth))
  671.         printToWindow(informationWindow, widthOffset, "Available:")
  672.         printToWindow(informationWindow, widthOffset, padRight("    "..capacitor.lastReading.." RF", labelWidth))
  673.  
  674.         local closeLabel = " Click anywhere to close "
  675.    
  676.         local x = math.floor(((width - string.len(closeLabel)) / 2 ) + 0.5)
  677.    
  678.         informationWindow.setCursorPos(x, height-2)
  679.  
  680.         informationWindow.setBackgroundColor(colors.red)
  681.         informationWindow.write(closeLabel)
  682.         informationWindow.setBackgroundColor(colors.black)
  683.     end
  684.  
  685.     showWindows(summaryWindow, informationWindow)
  686. end
  687.  
  688. local function checkForSelectableLine(monitorName, x, y)
  689.     if clickableLines[monitorName] == nil then
  690.         return nil
  691.     end
  692.  
  693.     for k,v in pairs(clickableLines[monitorName]) do
  694.         if y == v.line then
  695.             return k
  696.         end
  697.     end
  698.  
  699.     return nil
  700. end
  701.  
  702. local function getSelectedDashboardButton(monitorName, x, y)
  703.     if x == nil or y == nil then
  704.         return nil
  705.     end
  706.  
  707.     local v = dashboardButtons[monitorName]
  708.  
  709.     local nextButtonSelected = (x >= v.next.startX and x <= v.next.endX) and (y >= v.next.startY and y <= v.next.endY)
  710.     local prevButtonSelected = (x >= v.prev.startX and x <= v.prev.endX) and (y >= v.prev.startY and y <= v.prev.endY)
  711.  
  712.     if nextButtonSelected then
  713.         return "next"
  714.     elseif prevButtonSelected then
  715.         return "prev"
  716.     end
  717.  
  718.     return nil
  719. end
  720.  
  721. -- information window for the capacitors
  722.  
  723.  
  724. -- main display
  725. local function renderPaginationButtons(monitorName, max)
  726.     local width, height = term.getSize()
  727.     local nextButton = " Next "
  728.     local previousButton = " Prev "
  729.     local spacer = "     "
  730.  
  731.     local dashboardButtonsToRender = previousButton..spacer..nextButton
  732.     local buttonOffset = (width - (string.len(dashboardButtonsToRender))) / 2
  733.  
  734.     term.setCursorPos(buttonOffset, height)
  735.     local x, y = getCursorPositionRelativeToParent(term.current())
  736.  
  737.     if dashboardButtons[monitorName] ==  nil then
  738.         dashboardButtons[monitorName] = {
  739.             prev = {
  740.                 startX = x,
  741.                 startY = y,
  742.                 endX = x,
  743.                 endY = y
  744.             },
  745.  
  746.             next = {
  747.                 startX = x,
  748.                 startY = y,
  749.                 endX = x,
  750.                 endY = y
  751.             },
  752.  
  753.             offset = 1,
  754.             max = max
  755.         }
  756.     end
  757.  
  758.     if dashboardButtons[monitorName].offset == 1 then
  759.         dashboardButtons[monitorName].max = max
  760.     end
  761.  
  762.     term.setBackgroundColor(colors.red)
  763.     term.write(previousButton)
  764.     dashboardButtons[monitorName].prev.endX, dashboardButtons[monitorName].prev.endY = getCursorPositionRelativeToParent(term.current())
  765.  
  766.     term.setBackgroundColor(colors.black)
  767.     term.write(spacer)
  768.  
  769.     dashboardButtons[monitorName].next.startX, dashboardButtons[monitorName].next.startY = getCursorPositionRelativeToParent(term.current())
  770.     term.setBackgroundColor(colors.red)
  771.     term.write(nextButton)
  772.     dashboardButtons[monitorName].next.endX, dashboardButtons[monitorName].next.endY = getCursorPositionRelativeToParent(term.current())
  773.  
  774.     term.setBackgroundColor(colors.black)
  775. end
  776.  
  777. local function writeSummary(monitorName, totalEnergyAvailable, totalCapacity, totalFlowRate)
  778.     local width, height = term.getSize()
  779.     local gridLabel = os.getComputerLabel() or "No name set"
  780.     local gridLabelOffset = (width - (string.len(gridLabel))) / 2
  781.  
  782.     term.setCursorPos(gridLabelOffset, 1)
  783.     term.write(gridLabel)
  784.     term.setCursorPos(1, 3)
  785.  
  786.     print(padRight("Total Capacitors: "..tostring(#capacitors)))
  787.     print(padRight("Max Energy Storage: "..totalCapacity.." RF"))
  788.  
  789.     local totalPercentRemaining = math.floor((totalEnergyAvailable / totalCapacity) * 100)
  790.     emitRedstoneSignalOnLowPower(totalPercentRemaining)
  791.  
  792.     printZoneText(totalPercentRemaining, function() print(padRight("Energy Available: "..totalEnergyAvailable.." RF")) end)
  793.  
  794.     if totalFlowRate < 0 then
  795.         term.setTextColor(colors.red)
  796.     elseif totalFlowRate > 0 then
  797.         term.setTextColor(colors.green)
  798.     else
  799.         term.setTextColor(colors.white)
  800.     end
  801.  
  802.     print(padRight("Flow Rate: "..totalFlowRate.." RF/t"))
  803.     term.setTextColor(colors.white)
  804.  
  805.     local currentX, currentY = term.getCursorPos()
  806.     term.setCursorPos(1, currentY+1)
  807.  
  808.     clickableLines[monitorName] = {}
  809.     local pagination = dashboardButtons[monitorName] or {}
  810.     local offset = pagination.offset or 1
  811.  
  812.     local count = 0
  813.     for i = offset, #capacitors do
  814.         local v = capacitors[i]
  815.         local name = string.format(" %03d", i)..": "
  816.         local percent = v.percent
  817.  
  818.         printZoneText(percent, function() term.write(name) end)
  819.  
  820.         local labelLength = string.len(name)
  821.         local powerBarLength = width - labelLength - 1
  822.         local powerBarReading = math.floor((width - labelLength - 1) * (percent/100))
  823.  
  824.         local zoneColor = colors.red
  825.         local textColor = colors.white
  826.         if percent >= GREEN_ZONE then
  827.             zoneColor = colors.green
  828.         elseif percent >= YELLOW_ZONE and percent < GREEN_ZONE then
  829.             zoneColor = colors.yellow
  830.             textColor = colors.blue
  831.         end
  832.  
  833.         local stats = padRight(string.format(" %d", percent).."%, "..v.flowRate.." RF/t", powerBarLength)
  834.  
  835.         term.setTextColor(textColor)
  836.         term.setBackgroundColor(zoneColor)
  837.         j = 1
  838.         for c in stats:gmatch(".") do
  839.             if(j>powerBarReading) then
  840.                 term.setBackgroundColor(colors.black)
  841.             end
  842.  
  843.             term.write(c)
  844.  
  845.             j = j + 1
  846.         end
  847.         term.setTextColor(colors.white)
  848.         term.setBackgroundColor(colors.black)
  849.  
  850.         local currentX, currentY = getCursorPositionRelativeToParent(term.current())
  851.         addClickableLine(monitorName, v.name, currentY)
  852.  
  853.         local termX, termY = term.getCursorPos()
  854.         term.setCursorPos(1, termY+2)
  855.         count = count + 1
  856.  
  857.         if termY > (height - 4) then
  858.             max = count
  859.             break
  860.         end
  861.     end
  862.  
  863.     local currentX, currentY = term.getCursorPos()
  864.     for k = currentY, height-1 do
  865.         term.setCursorPos(1, k)
  866.         term.clearLine()
  867.     end
  868.  
  869.     renderPaginationButtons(monitorName, count)
  870. end
  871.  
  872. local function displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, targetMonitor)
  873.     local listOfSummaryWindows = {
  874.         native = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
  875.     }
  876.  
  877.     for k, v in pairs(monitors) do
  878.         listOfSummaryWindows[k] = v.windows.getSummaryWindow()
  879.     end
  880.  
  881.     for k, v in pairs(listOfSummaryWindows) do
  882.         if targetMonitor == nil or (k == targetMonitor) then
  883.             local currentTerm = term.current()
  884.  
  885.             term.redirect(v)
  886.  
  887.             writeSummary(k, totalEnergyAvailable, totalCapacity, totalFlowRate)
  888.  
  889.             term.redirect(currentTerm)
  890.  
  891.             if k == targetMonitor then
  892.                 return
  893.             end
  894.         end
  895.     end
  896. end
  897.  
  898. local function monitorCapacitors()
  899.     totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
  900.  
  901.     while true do
  902.         -- show reading
  903.         displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate)
  904.  
  905.         -- need to call this first to get most current sample
  906.         getReading()
  907.  
  908.         local samplingTimer = os.startTimer(PAUSE_TIME_IN_SECONDS)
  909.         while true do
  910.             local event, p1 = os.pullEvent()
  911.             if event == "timer" and p1 == samplingTimer then
  912.                 totalEnergyAvailable, totalCapacity, totalFlowRate = getReading()
  913.                 break
  914.             elseif event == "pause_monitor" then
  915.                 os.pullEvent("resume_monitor")
  916.                 break
  917.             end
  918.         end
  919.     end
  920. end
  921.  
  922. local function changePages(monitor, x, y, isInformationWindowActive)
  923.     local selectedButton = getSelectedDashboardButton(monitor, x, y)
  924.     local showSummary = false
  925.  
  926.     if selectedButton == "next" and not isInformationWindowActive then
  927.         local newOffset = dashboardButtons[monitor].offset + (dashboardButtons[monitor].max or 0)
  928.         if newOffset <= #capacitors then
  929.             dashboardButtons[monitor].offset = newOffset
  930.  
  931.             showSummary = true
  932.         end
  933.     elseif selectedButton == "prev" and not isInformationWindowActive then
  934.         local newOffset = dashboardButtons[monitor].offset - (dashboardButtons[monitor].max or 0)
  935.         if newOffset > 0 then
  936.             dashboardButtons[monitor].offset = newOffset
  937.         else
  938.             dashboardButtons[monitor].offset = 1
  939.         end
  940.  
  941.         showSummary = true
  942.     end
  943.  
  944.     if showSummary then
  945.         displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, p1)
  946.         return true
  947.     end
  948.  
  949.     return false
  950. end
  951.  
  952. local function nativeDashboardHandler()
  953.     while true do
  954.         local event, x, y = os.pullEvent("dashboard_clicked")
  955.         local isInformationWindowActive = nativeDisplayTabs.getDashboardWindows().getInformationWindow().active
  956.  
  957.         if not changePages("native", x, y, isInformationWindowActive) then
  958.             local selectedCapacitor = checkForSelectableLine("native", x, y)
  959.  
  960.             local summaryWindow = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
  961.             local informationWindow = nativeDisplayTabs.getDashboardWindows().getInformationWindow()
  962.  
  963.             toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
  964.         end
  965.     end
  966. end
  967.  
  968. local function monitorDashboardHandler()
  969.     while true do
  970.         local event, monitor, x, y = os.pullEvent("monitor_touch")
  971.  
  972.         if monitors[monitor].active == true then
  973.             local summaryWindow = monitors[monitor].windows.getSummaryWindow()
  974.             local informationWindow = monitors[monitor].windows.getInformationWindow()
  975.  
  976.             if not changePages(monitor, x, y, informationWindow.active) then
  977.                 local selectedCapacitor = checkForSelectableLine(monitor, x, y)
  978.                 toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
  979.             end
  980.         end
  981.     end
  982. end
  983. -- main display
  984.  
  985.  
  986. -- monitor selection screen (if monitor is attached)
  987. local function addClickableOutputMonitor(k, currentY)
  988.     clickableOutputMonitors[k] = {
  989.         line = currentY
  990.     }
  991. end
  992.  
  993. local function toggleMonitor(monitorName)
  994.     monitors[monitorName].active = not monitors[monitorName].active
  995.  
  996.     if monitors[monitorName].active then
  997.         console.log("Enabling "..monitorName)
  998.         MONITORS_ACTIVE[monitorName] = true
  999.     else
  1000.         console.log("Disabling "..monitorName)
  1001.         MONITORS_ACTIVE[monitorName] = nil
  1002.  
  1003.         hideWindows(unpack(monitors[monitorName].windows))
  1004.         monitors[monitorName].peripheral.setBackgroundColor(colors.black)
  1005.         monitors[monitorName].peripheral.clear()
  1006.     end
  1007.  
  1008.     saveSettings()
  1009. end
  1010.  
  1011. local function showMonitorSelection(targetWindow)
  1012.     local currentTerm = term.current()
  1013.  
  1014.     term.redirect(targetWindow)
  1015.     term.setCursorPos(1, 1)
  1016.     term.clear()
  1017.  
  1018.     local width, height = term.getSize()
  1019.  
  1020.     if tableSize(monitors) > 0 then
  1021.         printToWindow(term, 0, "Select Output Monitor: ")
  1022.     else
  1023.         printToWindow(term, 0, "No Monitors found.")
  1024.     end
  1025.  
  1026.     printToWindow(term, 0, "")
  1027.  
  1028.     local currentX, currentY = term.getCursorPos()
  1029.     term.setCursorPos(currentX + 2, currentY)
  1030.  
  1031.     clickableOutputMonitors = {}
  1032.     for k, v in pairs(monitors) do
  1033.         currentX, currentY = getCursorPositionRelativeToParent(targetWindow)
  1034.         term.setBackgroundColor(colors.black)
  1035.  
  1036.         if v.active == true then
  1037.             term.setBackgroundColor(colors.blue)
  1038.             showWindows(unpack(v.windows))
  1039.         end
  1040.  
  1041.         label = padRight("  "..k, width-4)
  1042.         printToWindow(term, 0, label)
  1043.  
  1044.         addClickableOutputMonitor(k, currentY)
  1045.     end
  1046.     term.setBackgroundColor(colors.black)
  1047.  
  1048.     term.redirect(currentTerm)
  1049.  
  1050.     while true do
  1051.         local event, x, y = os.pullEvent()
  1052.  
  1053.         if "monitors_clicked" == event then
  1054.             for k, v in pairs(clickableOutputMonitors) do
  1055.                 if v.line == y then
  1056.                     toggleMonitor(k)
  1057.                     return
  1058.                 end
  1059.             end
  1060.         elseif event == "peripheral" or event == "peripheral_detach" then
  1061.             coroutine.yield()
  1062.             return
  1063.         end
  1064.     end
  1065. end
  1066.  
  1067. local function monitorSelection()
  1068.     for k, v in pairs(MONITORS_ACTIVE) do
  1069.         if monitors[k] then
  1070.             monitors[k].active = true
  1071.         end
  1072.     end
  1073.  
  1074.     while true do
  1075.         showMonitorSelection(monitorSelectionWindow)
  1076.     end
  1077. end
  1078.  
  1079. local function nativeDisplay()
  1080.     while true do
  1081.         local currentTerm = term.current()
  1082.  
  1083.         term.redirect(nativeMonitorTabWindow)
  1084.         nativeMonitorTabWindow.setVisible(true)
  1085.  
  1086.         term.setCursorPos(1, 1)
  1087.         term.setBackgroundColor(colors.gray)
  1088.         term.clearLine()
  1089.         term.setTextColor(colors.yellow)
  1090.  
  1091.         for i, v in ipairs(nativeDisplayTabs) do
  1092.             hideWindows(unpack(v.windows))
  1093.         end
  1094.  
  1095.         for i, v in ipairs(nativeDisplayTabs) do
  1096.             local tab = v.tab
  1097.             tab.startX, tab.startY = getCursorPositionRelativeToParent(term.current())
  1098.  
  1099.             if tab.active then
  1100.                 term.setBackgroundColor(colors.black)
  1101.                 showWindows(unpack(v.windows))
  1102.             else
  1103.                 term.setBackgroundColor(colors.gray)
  1104.             end
  1105.  
  1106.             term.write(" "..tab.label.." ")
  1107.             tab.endX, tab.endY = getCursorPositionRelativeToParent(term.current())
  1108.         end
  1109.         term.setTextColor(colors.white)
  1110.         term.redirect(currentTerm)
  1111.  
  1112.         while true do
  1113.             local event, selectedTab = os.pullEvent("selected_tab")
  1114.  
  1115.             if selectedTab then
  1116.                 nativeDisplayTabs.setSelectedTab(selectedTab)
  1117.                 break
  1118.             end
  1119.         end
  1120.     end
  1121. end
  1122.  
  1123. local function mouseClickEventMonitor()
  1124.     while true do
  1125.         local event, type, x, y = os.pullEvent("mouse_click")
  1126.         local selectedTab = nativeDisplayTabs.getSelectedTab(x, y)
  1127.  
  1128.         if selectedTab and nativeDisplayTabs.getDashboardWindows().getInformationWindow().active == false then
  1129.             os.queueEvent("selected_tab", selectedTab)
  1130.         elseif selectedTab == nil then
  1131.             local activeTab = nativeDisplayTabs[nativeDisplayTabs.getActiveTab()]
  1132.  
  1133.             os.queueEvent(activeTab.tab.event, x, y)
  1134.         end
  1135.     end
  1136. end
  1137.  
  1138. -- Initialization
  1139. initializeNativeDisplayTabs()
  1140. resetRedstoneState()
  1141. addCapacitors()
  1142. addMonitors()
  1143.  
  1144. while true do
  1145.     parallel.waitForAll(mouseClickEventMonitor, nativeDisplay, monitorSelection, hotplugPeripherals, monitorCapacitors, nativeDashboardHandler, monitorDashboardHandler)
  1146. end
RAW Paste Data