· 5 years ago · Jan 09, 2021, 10:46 PM
1-- Main configuration
2local tArgs = {...}
3local CONFIG_FILE = "power_conf"
4
5local CONFIG_TEMPLATE = [[-- adjust these configuration options as necessary.
6-- delay for checking all capacitors
7TICK_DELAY = ${tickDelay}
8
9-- threshold in percentages
10GREEN_ZONE = ${greenZone}
11YELLOW_ZONE = ${yellowZone}
12
13NORMAL_POWER_THRESHOLD = ${nomalPowerThreshold}
14LOW_POWER_THRESHOLD = ${lowPowerThreshold}
15
16-- configures what side to emit when low power
17-- a valid side is required.
18SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER = "${sideForRedstone}"
19
20-- Active monitors on startup
21MONITORS_ACTIVE = ${monitorsActive}
22]]
23
24local MONITORS_ACTIVE = {}
25
26do
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
45end
46
47local function interpolate(s, params)
48 return s:gsub('($%b{})', function(w) return params[w:sub(3, -2)] or w end)
49end
50
51local 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()
66end
67
68
69if 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
80else
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
100end
101
102term.setCursorPos(1, 1)
103term.clear()
104print("Starting RF Control Center...")
105sleep(2)
106
107-- Constants
108local GET_ENERGY_STORED_FUNCTION="getEnergyStored"
109local GET_MAX_ENERGY_STORED_FUNCTION="getMaxEnergyStored"
110
111local TICK_DELAY = CONFIG.TICK_DELAY
112local PAUSE_TIME_IN_SECONDS = TICK_DELAY / 20
113
114-- threshold in percentages
115local GREEN_ZONE = CONFIG.GREEN_ZONE
116local YELLOW_ZONE = CONFIG.YELLOW_ZONE
117
118local NORMAL_POWER_THRESHOLD = CONFIG.NORMAL_POWER_THRESHOLD
119local LOW_POWER_THRESHOLD = CONFIG.LOW_POWER_THRESHOLD
120
121-- configures what side to emit when low power
122local SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER = CONFIG.SIDE_TO_EMIT_REDSTONE_ON_LOW_POWER
123
124MONITORS_ACTIVE = CONFIG.MONITORS_ACTIVE
125
126-- state variables
127local clickableLines = {}
128local clickableOutputMonitors = {}
129local monitors = {}
130local capacitors = {}
131local dashboardButtons = {}
132local totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
133
134-- Capacitor basic functions
135do
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
169end
170
171
172-- Special Windows
173local nativeDisplayTabs = {}
174local nativeMonitorTabWindow
175local consoleWindow
176local monitorSelectionWindow
177
178do
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
193end
194
195-- TODO: break up this humongous script into smaller chunks that can be loaded
196-- via os.loadAPI().
197
198
199-- basic functions
200local 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
207end
208
209local 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
225end
226
227local 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
243end
244
245local 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)
257end
258
259local function resetRedstoneState()
260 for k,v in pairs(rs.getSides()) do
261 rs.setOutput(v, false)
262 end
263end
264-- basic functions
265
266
267-- line drawing API
268local 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)
277end
278
279local 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)
284end
285-- line drawing API
286
287
288-- window management
289local 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
300local 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
308end
309
310local function hideWindows(...)
311 for i, v in ipairs(arg) do
312 v.setVisible(false)
313 end
314end
315
316local 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
323end
324
325local 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
344end
345
346local 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
357end
358
359local 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)
366end
367
368local 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
389end
390
391local 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
468end
469
470-- window management
471
472
473-- capacitor management
474local 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
496end
497
498local function removeCapacitors(...)
499 for i, k in ipairs(arg) do
500 capacitors.remove(k)
501 end
502end
503
504local 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
535end
536
537local 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
545end
546-- capacitor management
547
548
549-- monitor management
550local 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
571end
572
573local 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
585end
586-- monitor management
587
588
589-- hotplug system
590local function doWhileMonitorSuspended(callback)
591 os.queueEvent("pause_monitor")
592 callback()
593 os.queueEvent("resume_monitor")
594end
595
596local 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
634end
635-- hotplug system
636
637
638-- information window for the capacitors
639local function addClickableLine(monitorName, key, currentY)
640 clickableLines[monitorName][key] = {
641 line = currentY
642 }
643end
644
645local 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)
686end
687
688local 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
700end
701
702local 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
719end
720
721-- information window for the capacitors
722
723
724-- main display
725local 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)
775end
776
777local 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)
870end
871
872local 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
896end
897
898local 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
920end
921
922local 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
950end
951
952local 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
966end
967
968local 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
982end
983-- main display
984
985
986-- monitor selection screen (if monitor is attached)
987local function addClickableOutputMonitor(k, currentY)
988 clickableOutputMonitors[k] = {
989 line = currentY
990 }
991end
992
993local 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()
1009end
1010
1011local 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
1065end
1066
1067local 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
1077end
1078
1079local 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
1121end
1122
1123local 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
1136end
1137
1138-- Initialization
1139initializeNativeDisplayTabs()
1140resetRedstoneState()
1141addCapacitors()
1142addMonitors()
1143
1144while true do
1145 parallel.waitForAll(mouseClickEventMonitor, nativeDisplay, monitorSelection, hotplugPeripherals, monitorCapacitors, nativeDashboardHandler, monitorDashboardHandler)
1146end