· 6 years ago · Apr 28, 2020, 06:46 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 * 2)
367
368 targetWindow.write(text:sub(1, maxTextSize))
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(padRight("Total Capacitors: "..tostring(#capacitors)))
790 print(padRight("Max Energy Storage: "..totalCapacity.." RF"))
791
792 local totalPercentRemaining = math.floor((totalEnergyAvailable / totalCapacity) * 100)
793 emitRedstoneSignalOnLowPower(totalPercentRemaining)
794
795 printZoneText(totalPercentRemaining, function() print(padRight("Energy 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 local count = 0
816 for i = offset, #capacitors do
817 local v = capacitors[i]
818 local name = string.format(" %03d", i)..": "
819 local percent = v.percent
820
821 printZoneText(percent, function() term.write(name) end)
822
823 local labelLength = string.len(name)
824 local powerBarLength = width - labelLength - 1
825 local powerBarReading = math.floor((width - labelLength - 1) * (percent/100))
826
827 local zoneColor = colors.red
828 local textColor = colors.white
829 if percent >= GREEN_ZONE then
830 zoneColor = colors.green
831 elseif percent >= YELLOW_ZONE and percent < GREEN_ZONE then
832 zoneColor = colors.yellow
833 textColor = colors.blue
834 end
835
836 local stats = padRight(string.format(" %d", percent).."%, "..v.flowRate.." RF/t", powerBarLength)
837
838 term.setTextColor(textColor)
839 term.setBackgroundColor(zoneColor)
840 j = 1
841 for c in stats:gmatch(".") do
842 if(j>powerBarReading) then
843 term.setBackgroundColor(colors.black)
844 end
845
846 term.write(c)
847
848 j = j + 1
849 end
850 term.setTextColor(colors.white)
851 term.setBackgroundColor(colors.black)
852
853 local currentX, currentY = getCursorPositionRelativeToParent(term.current())
854 addClickableLine(monitorName, v.name, currentY)
855
856 local termX, termY = term.getCursorPos()
857 term.setCursorPos(1, termY+2)
858 count = count + 1
859
860 if termY > (height - 4) then
861 max = count
862 break
863 end
864 end
865
866 local currentX, currentY = term.getCursorPos()
867 for k = currentY, height-1 do
868 term.setCursorPos(1, k)
869 term.clearLine()
870 end
871
872 renderPaginationButtons(monitorName, count)
873end
874
875local function displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, targetMonitor)
876 local listOfSummaryWindows = {
877 native = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
878 }
879
880 for k, v in pairs(monitors) do
881 listOfSummaryWindows[k] = v.windows.getSummaryWindow()
882 end
883
884 for k, v in pairs(listOfSummaryWindows) do
885 if targetMonitor == nil or (k == targetMonitor) then
886 local currentTerm = term.current()
887
888 term.redirect(v)
889
890 writeSummary(k, totalEnergyAvailable, totalCapacity, totalFlowRate)
891
892 term.redirect(currentTerm)
893
894 if k == targetMonitor then
895 return
896 end
897 end
898 end
899end
900
901local function monitorCapacitors()
902 totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
903
904 while true do
905 -- show reading
906 displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate)
907
908 -- need to call this first to get most current sample
909 getReading()
910
911 local samplingTimer = os.startTimer(PAUSE_TIME_IN_SECONDS)
912 while true do
913 local event, p1 = os.pullEvent()
914 if event == "timer" and p1 == samplingTimer then
915 totalEnergyAvailable, totalCapacity, totalFlowRate = getReading()
916 break
917 elseif event == "pause_monitor" then
918 os.pullEvent("resume_monitor")
919 break
920 end
921 end
922 end
923end
924
925local function changePages(monitor, x, y, isInformationWindowActive)
926 local selectedButton = getSelectedDashboardButton(monitor, x, y)
927 local showSummary = false
928
929 if selectedButton == "next" and not isInformationWindowActive then
930 local newOffset = dashboardButtons[monitor].offset + (dashboardButtons[monitor].max or 0)
931 if newOffset <= #capacitors then
932 dashboardButtons[monitor].offset = newOffset
933
934 showSummary = true
935 end
936 elseif selectedButton == "prev" and not isInformationWindowActive then
937 local newOffset = dashboardButtons[monitor].offset - (dashboardButtons[monitor].max or 0)
938 if newOffset > 0 then
939 dashboardButtons[monitor].offset = newOffset
940 else
941 dashboardButtons[monitor].offset = 1
942 end
943
944 showSummary = true
945 end
946
947 if showSummary then
948 displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, p1)
949 return true
950 end
951
952 return false
953end
954
955local function nativeDashboardHandler()
956 while true do
957 local event, x, y = os.pullEvent("dashboard_clicked")
958 local isInformationWindowActive = nativeDisplayTabs.getDashboardWindows().getInformationWindow().active
959
960 if not changePages("native", x, y, isInformationWindowActive) then
961 local selectedCapacitor = checkForSelectableLine("native", x, y)
962
963 local summaryWindow = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
964 local informationWindow = nativeDisplayTabs.getDashboardWindows().getInformationWindow()
965
966 toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
967 end
968 end
969end
970
971local function monitorDashboardHandler()
972 while true do
973 local event, monitor, x, y = os.pullEvent("monitor_touch")
974
975 if monitors[monitor].active == true then
976 local summaryWindow = monitors[monitor].windows.getSummaryWindow()
977 local informationWindow = monitors[monitor].windows.getInformationWindow()
978
979 if not changePages(monitor, x, y, informationWindow.active) then
980 local selectedCapacitor = checkForSelectableLine(monitor, x, y)
981 toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
982 end
983 end
984 end
985end
986-- main display
987
988
989-- monitor selection screen (if monitor is attached)
990local function addClickableOutputMonitor(k, currentY)
991 clickableOutputMonitors[k] = {
992 line = currentY
993 }
994end
995
996local function toggleMonitor(monitorName)
997 monitors[monitorName].active = not monitors[monitorName].active
998
999 if monitors[monitorName].active then
1000 console.log("Enabling "..monitorName)
1001 MONITORS_ACTIVE[monitorName] = true
1002 else
1003 console.log("Disabling "..monitorName)
1004 MONITORS_ACTIVE[monitorName] = nil
1005
1006 hideWindows(unpack(monitors[monitorName].windows))
1007 monitors[monitorName].peripheral.setBackgroundColor(colors.black)
1008 monitors[monitorName].peripheral.clear()
1009 end
1010
1011 saveSettings()
1012end
1013
1014local function showMonitorSelection(targetWindow)
1015 local currentTerm = term.current()
1016
1017 term.redirect(targetWindow)
1018 term.setCursorPos(1, 1)
1019 term.clear()
1020
1021 local width, height = term.getSize()
1022
1023 if tableSize(monitors) > 0 then
1024 printToWindow(term, 0, "Select Output Monitor: ")
1025 else
1026 printToWindow(term, 0, "No Monitors found.")
1027 end
1028
1029 printToWindow(term, 0, "")
1030
1031 local currentX, currentY = term.getCursorPos()
1032 term.setCursorPos(currentX + 2, currentY)
1033
1034 clickableOutputMonitors = {}
1035 for k, v in pairs(monitors) do
1036 currentX, currentY = getCursorPositionRelativeToParent(targetWindow)
1037 term.setBackgroundColor(colors.black)
1038
1039 if v.active == true then
1040 term.setBackgroundColor(colors.blue)
1041 showWindows(unpack(v.windows))
1042 end
1043
1044 label = padRight(" "..k, width-4)
1045 printToWindow(term, 0, label)
1046
1047 addClickableOutputMonitor(k, currentY)
1048 end
1049 term.setBackgroundColor(colors.black)
1050
1051 term.redirect(currentTerm)
1052
1053 while true do
1054 local event, x, y = os.pullEvent()
1055
1056 if "monitors_clicked" == event then
1057 for k, v in pairs(clickableOutputMonitors) do
1058 if v.line == y then
1059 toggleMonitor(k)
1060 return
1061 end
1062 end
1063 elseif event == "peripheral" or event == "peripheral_detach" then
1064 coroutine.yield()
1065 return
1066 end
1067 end
1068end
1069
1070local function monitorSelection()
1071 for k, v in pairs(MONITORS_ACTIVE) do
1072 if monitors[k] then
1073 monitors[k].active = true
1074 end
1075 end
1076
1077 while true do
1078 showMonitorSelection(monitorSelectionWindow)
1079 end
1080end
1081
1082local function nativeDisplay()
1083 while true do
1084 local currentTerm = term.current()
1085
1086 term.redirect(nativeMonitorTabWindow)
1087 nativeMonitorTabWindow.setVisible(true)
1088
1089 term.setCursorPos(1, 1)
1090 term.setBackgroundColor(colors.gray)
1091 term.clearLine()
1092 term.setTextColor(colors.yellow)
1093
1094 for i, v in ipairs(nativeDisplayTabs) do
1095 hideWindows(unpack(v.windows))
1096 end
1097
1098 for i, v in ipairs(nativeDisplayTabs) do
1099 local tab = v.tab
1100 tab.startX, tab.startY = getCursorPositionRelativeToParent(term.current())
1101
1102 if tab.active then
1103 term.setBackgroundColor(colors.black)
1104 showWindows(unpack(v.windows))
1105 else
1106 term.setBackgroundColor(colors.gray)
1107 end
1108
1109 term.write(" "..tab.label.." ")
1110 tab.endX, tab.endY = getCursorPositionRelativeToParent(term.current())
1111 end
1112 term.setTextColor(colors.white)
1113 term.redirect(currentTerm)
1114
1115 while true do
1116 local event, selectedTab = os.pullEvent("selected_tab")
1117
1118 if selectedTab then
1119 nativeDisplayTabs.setSelectedTab(selectedTab)
1120 break
1121 end
1122 end
1123 end
1124end
1125
1126local function mouseClickEventMonitor()
1127 while true do
1128 local event, type, x, y = os.pullEvent("mouse_click")
1129 local selectedTab = nativeDisplayTabs.getSelectedTab(x, y)
1130
1131 if selectedTab and nativeDisplayTabs.getDashboardWindows().getInformationWindow().active == false then
1132 os.queueEvent("selected_tab", selectedTab)
1133 elseif selectedTab == nil then
1134 local activeTab = nativeDisplayTabs[nativeDisplayTabs.getActiveTab()]
1135
1136 os.queueEvent(activeTab.tab.event, x, y)
1137 end
1138 end
1139end
1140
1141-- Initialization
1142initializeNativeDisplayTabs()
1143resetRedstoneState()
1144addCapacitors()
1145addMonitors()
1146
1147while true do
1148 parallel.waitForAll(mouseClickEventMonitor, nativeDisplay, monitorSelection, hotplugPeripherals, monitorCapacitors, nativeDashboardHandler, monitorDashboardHandler)
1149end