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