· 5 years ago · Jul 23, 2020, 08:14 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="getEnergyStored"
113local GET_MAX_ENERGY_STORED_FUNCTION="getMaxEnergyStored"
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 sleep(TICK_DELAY)
603 local event, name = os.pullEvent()
604 local callback = nil
605
606 if event == "peripheral" then
607 console.log("Detected new peripheral: "..name)
608
609 callback = function()
610 addMonitors(name)
611 addCapacitors(name)
612 end
613 elseif event == "peripheral_detach" then
614 console.log("Peripheral removed: "..name)
615
616 callback = function()
617 removeMonitors(name)
618 removeCapacitors(name)
619 end
620 elseif event == "monitor_resize" then
621 console.log("Monitor resized: "..name)
622
623 callback = function()
624 monitors[name].peripheral.setCursorPos(1, 1)
625 monitors[name].windows = createDashboardWindows(monitors[name].peripheral)
626 dashboardButtons[name] = nil
627
628 if monitors[name].active == true then
629 showWindows(unpack(monitors[name].windows))
630 end
631 end
632 end
633
634 if callback ~= nil then
635 doWhileMonitorSuspended(callback)
636 end
637 end
638end
639-- hotplug system
640
641
642-- information window for the capacitors
643local function addClickableLine(monitorName, key, currentY)
644 clickableLines[monitorName][key] = {
645 line = currentY
646 }
647end
648
649local function toggleInformationWindow(summaryWindow, informationWindow, capacitorName)
650 if capacitorName == nil then
651 summaryWindow.active = true
652 informationWindow.active = false
653 else
654 summaryWindow.active = not summaryWindow.active
655 informationWindow.active = not informationWindow.active
656 end
657
658 local capacitor = capacitors.get(capacitorName)
659
660 if informationWindow.active == true then
661 widthOffset = 3
662 heightOffset = 3
663
664 informationWindow.setCursorPos(widthOffset, heightOffset)
665 local width, height = informationWindow.getSize()
666 local labelWidth = width - (widthOffset * 2)
667 local capacity = capacitor.peripheral[GET_MAX_ENERGY_STORED_FUNCTION]("north")
668
669 printToWindow(informationWindow, widthOffset, "Capacitor name:")
670 printToWindow(informationWindow, widthOffset, padRight(" "..capacitorName, labelWidth))
671 printToWindow(informationWindow, widthOffset, "Capacitor type:")
672 printToWindow(informationWindow, widthOffset, padRight(" "..peripheral.getType(capacitorName), labelWidth))
673 printToWindow(informationWindow, widthOffset, "Capacity:")
674 printToWindow(informationWindow, widthOffset, padRight(" "..capacity.." RF", labelWidth))
675 printToWindow(informationWindow, widthOffset, "Available:")
676 printToWindow(informationWindow, widthOffset, padRight(" "..capacitor.lastReading.." RF", labelWidth))
677
678 local closeLabel = " Click anywhere to close "
679
680 local x = math.floor(((width - string.len(closeLabel)) / 2 ) + 0.5)
681
682 informationWindow.setCursorPos(x, height-2)
683
684 informationWindow.setBackgroundColor(colors.red)
685 informationWindow.write(closeLabel)
686 informationWindow.setBackgroundColor(colors.black)
687 end
688
689 showWindows(summaryWindow, informationWindow)
690end
691
692local function checkForSelectableLine(monitorName, x, y)
693 if clickableLines[monitorName] == nil then
694 return nil
695 end
696
697 for k,v in pairs(clickableLines[monitorName]) do
698 if y == v.line then
699 return k
700 end
701 end
702
703 return nil
704end
705
706local function getSelectedDashboardButton(monitorName, x, y)
707 if x == nil or y == nil then
708 return nil
709 end
710
711 local v = dashboardButtons[monitorName]
712
713 local nextButtonSelected = (x >= v.next.startX and x <= v.next.endX) and (y >= v.next.startY and y <= v.next.endY)
714 local prevButtonSelected = (x >= v.prev.startX and x <= v.prev.endX) and (y >= v.prev.startY and y <= v.prev.endY)
715
716 if nextButtonSelected then
717 return "next"
718 elseif prevButtonSelected then
719 return "prev"
720 end
721
722 return nil
723end
724
725-- information window for the capacitors
726
727
728-- main display
729local function renderPaginationButtons(monitorName, max)
730 local width, height = term.getSize()
731 local nextButton = " Next "
732 local previousButton = " Prev "
733 local spacer = " "
734
735 local dashboardButtonsToRender = previousButton..spacer..nextButton
736 local buttonOffset = (width - (string.len(dashboardButtonsToRender))) / 2
737
738 term.setCursorPos(buttonOffset, height)
739 local x, y = getCursorPositionRelativeToParent(term.current())
740
741 if dashboardButtons[monitorName] == nil then
742 dashboardButtons[monitorName] = {
743 prev = {
744 startX = x,
745 startY = y,
746 endX = x,
747 endY = y
748 },
749
750 next = {
751 startX = x,
752 startY = y,
753 endX = x,
754 endY = y
755 },
756
757 offset = 1,
758 max = max
759 }
760 end
761
762 if dashboardButtons[monitorName].offset == 1 then
763 dashboardButtons[monitorName].max = max
764 end
765
766 term.setBackgroundColor(colors.red)
767 term.write(previousButton)
768 dashboardButtons[monitorName].prev.endX, dashboardButtons[monitorName].prev.endY = getCursorPositionRelativeToParent(term.current())
769
770 term.setBackgroundColor(colors.black)
771 term.write(spacer)
772
773 dashboardButtons[monitorName].next.startX, dashboardButtons[monitorName].next.startY = getCursorPositionRelativeToParent(term.current())
774 term.setBackgroundColor(colors.red)
775 term.write(nextButton)
776 dashboardButtons[monitorName].next.endX, dashboardButtons[monitorName].next.endY = getCursorPositionRelativeToParent(term.current())
777
778 term.setBackgroundColor(colors.black)
779end
780
781local function writeSummary(monitorName, totalEnergyAvailable, totalCapacity, totalFlowRate)
782 local width, height = term.getSize()
783 local gridLabel = os.getComputerLabel() or "No name set"
784 local gridLabelOffset = (width - (string.len(gridLabel))) / 2
785
786 term.setCursorPos(gridLabelOffset, 1)
787 term.write(gridLabel)
788 term.setCursorPos(1, 3)
789
790 print(padRight("Total Capacitors: "..tostring(#capacitors)))
791 print(padRight("Max Energy Storage: "..totalCapacity.." RF"))
792
793 local totalPercentRemaining = math.floor((totalEnergyAvailable / totalCapacity) * 100)
794 emitRedstoneSignalOnLowPower(totalPercentRemaining)
795
796 printZoneText(totalPercentRemaining, function() print(padRight("Energy Available: "..totalEnergyAvailable.." RF")) end)
797
798 if totalFlowRate < 0 then
799 term.setTextColor(colors.red)
800 elseif totalFlowRate > 0 then
801 term.setTextColor(colors.green)
802 else
803 term.setTextColor(colors.white)
804 end
805
806 print(padRight("Flow Rate: "..totalFlowRate.." RF/t"))
807 term.setTextColor(colors.white)
808
809 local currentX, currentY = term.getCursorPos()
810 term.setCursorPos(1, currentY+1)
811
812 clickableLines[monitorName] = {}
813 local pagination = dashboardButtons[monitorName] or {}
814 local offset = pagination.offset or 1
815
816 local count = 0
817 for i = offset, #capacitors do
818 local v = capacitors[i]
819 local name = string.format(" %03d", i)..": "
820 local percent = v.percent
821
822 printZoneText(percent, function() term.write(name) end)
823
824 local labelLength = string.len(name)
825 local powerBarLength = width - labelLength - 1
826 local powerBarReading = math.floor((width - labelLength - 1) * (percent/100))
827
828 local zoneColor = colors.red
829 local textColor = colors.white
830 if percent >= GREEN_ZONE then
831 zoneColor = colors.green
832 elseif percent >= YELLOW_ZONE and percent < GREEN_ZONE then
833 zoneColor = colors.yellow
834 textColor = colors.blue
835 end
836
837 local stats = padRight(string.format(" %d", percent).."%, "..v.flowRate.." RF/t", powerBarLength)
838
839 term.setTextColor(textColor)
840 term.setBackgroundColor(zoneColor)
841 j = 1
842 for c in stats:gmatch(".") do
843 if(j>powerBarReading) then
844 term.setBackgroundColor(colors.black)
845 end
846
847 term.write(c)
848
849 j = j + 1
850 end
851 term.setTextColor(colors.white)
852 term.setBackgroundColor(colors.black)
853
854 local currentX, currentY = getCursorPositionRelativeToParent(term.current())
855 addClickableLine(monitorName, v.name, currentY)
856
857 local termX, termY = term.getCursorPos()
858 term.setCursorPos(1, termY+2)
859 count = count + 1
860
861 if termY > (height - 4) then
862 max = count
863 break
864 end
865 end
866
867 local currentX, currentY = term.getCursorPos()
868 for k = currentY, height-1 do
869 term.setCursorPos(1, k)
870 term.clearLine()
871 end
872
873 renderPaginationButtons(monitorName, count)
874end
875
876local function displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, targetMonitor)
877 local listOfSummaryWindows = {
878 native = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
879 }
880
881 for k, v in pairs(monitors) do
882 listOfSummaryWindows[k] = v.windows.getSummaryWindow()
883 end
884
885 for k, v in pairs(listOfSummaryWindows) do
886 if targetMonitor == nil or (k == targetMonitor) then
887 local currentTerm = term.current()
888
889 term.redirect(v)
890
891 writeSummary(k, totalEnergyAvailable, totalCapacity, totalFlowRate)
892
893 term.redirect(currentTerm)
894
895 if k == targetMonitor then
896 return
897 end
898 end
899 end
900end
901
902local function monitorCapacitors()
903 totalEnergyAvailable, totalCapacity, totalFlowRate = 0, 0, 0
904
905 while true do
906 -- show reading
907 displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate)
908
909 -- need to call this first to get most current sample
910 getReading()
911
912 local samplingTimer = os.startTimer(PAUSE_TIME_IN_SECONDS)
913 while true do
914 local event, p1 = os.pullEvent()
915 if event == "timer" and p1 == samplingTimer then
916 totalEnergyAvailable, totalCapacity, totalFlowRate = getReading()
917 break
918 elseif event == "pause_monitor" then
919 os.pullEvent("resume_monitor")
920 break
921 end
922 end
923 end
924end
925
926local function changePages(monitor, x, y, isInformationWindowActive)
927 local selectedButton = getSelectedDashboardButton(monitor, x, y)
928 local showSummary = false
929
930 if selectedButton == "next" and not isInformationWindowActive then
931 local newOffset = dashboardButtons[monitor].offset + (dashboardButtons[monitor].max or 0)
932 if newOffset <= #capacitors then
933 dashboardButtons[monitor].offset = newOffset
934
935 showSummary = true
936 end
937 elseif selectedButton == "prev" and not isInformationWindowActive then
938 local newOffset = dashboardButtons[monitor].offset - (dashboardButtons[monitor].max or 0)
939 if newOffset > 0 then
940 dashboardButtons[monitor].offset = newOffset
941 else
942 dashboardButtons[monitor].offset = 1
943 end
944
945 showSummary = true
946 end
947
948 if showSummary then
949 displaySummary(totalEnergyAvailable, totalCapacity, totalFlowRate, p1)
950 return true
951 end
952
953 return false
954end
955
956local function nativeDashboardHandler()
957 while true do
958 local event, x, y = os.pullEvent("dashboard_clicked")
959 local isInformationWindowActive = nativeDisplayTabs.getDashboardWindows().getInformationWindow().active
960
961 if not changePages("native", x, y, isInformationWindowActive) then
962 local selectedCapacitor = checkForSelectableLine("native", x, y)
963
964 local summaryWindow = nativeDisplayTabs.getDashboardWindows().getSummaryWindow()
965 local informationWindow = nativeDisplayTabs.getDashboardWindows().getInformationWindow()
966
967 toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
968 end
969 end
970end
971
972local function monitorDashboardHandler()
973 while true do
974 local event, monitor, x, y = os.pullEvent("monitor_touch")
975
976 if monitors[monitor].active == true then
977 local summaryWindow = monitors[monitor].windows.getSummaryWindow()
978 local informationWindow = monitors[monitor].windows.getInformationWindow()
979
980 if not changePages(monitor, x, y, informationWindow.active) then
981 local selectedCapacitor = checkForSelectableLine(monitor, x, y)
982 toggleInformationWindow(summaryWindow, informationWindow, selectedCapacitor)
983 end
984 end
985 end
986end
987-- main display
988
989
990-- monitor selection screen (if monitor is attached)
991local function addClickableOutputMonitor(k, currentY)
992 clickableOutputMonitors[k] = {
993 line = currentY
994 }
995end
996
997local function toggleMonitor(monitorName)
998 monitors[monitorName].active = not monitors[monitorName].active
999
1000 if monitors[monitorName].active then
1001 console.log("Enabling "..monitorName)
1002 MONITORS_ACTIVE[monitorName] = true
1003 else
1004 console.log("Disabling "..monitorName)
1005 MONITORS_ACTIVE[monitorName] = nil
1006
1007 hideWindows(unpack(monitors[monitorName].windows))
1008 monitors[monitorName].peripheral.setBackgroundColor(colors.black)
1009 monitors[monitorName].peripheral.clear()
1010 end
1011
1012 saveSettings()
1013end
1014
1015local function showMonitorSelection(targetWindow)
1016 local currentTerm = term.current()
1017
1018 term.redirect(targetWindow)
1019 term.setCursorPos(1, 1)
1020 term.clear()
1021
1022 local width, height = term.getSize()
1023
1024 if tableSize(monitors) > 0 then
1025 printToWindow(term, 0, "Select Output Monitor: ")
1026 else
1027 printToWindow(term, 0, "No Monitors found.")
1028 end
1029
1030 printToWindow(term, 0, "")
1031
1032 local currentX, currentY = term.getCursorPos()
1033 term.setCursorPos(currentX + 2, currentY)
1034
1035 clickableOutputMonitors = {}
1036 for k, v in pairs(monitors) do
1037 currentX, currentY = getCursorPositionRelativeToParent(targetWindow)
1038 term.setBackgroundColor(colors.black)
1039
1040 if v.active == true then
1041 term.setBackgroundColor(colors.blue)
1042 showWindows(unpack(v.windows))
1043 end
1044
1045 label = padRight(" "..k, width-4)
1046 printToWindow(term, 0, label)
1047
1048 addClickableOutputMonitor(k, currentY)
1049 end
1050 term.setBackgroundColor(colors.black)
1051
1052 term.redirect(currentTerm)
1053
1054 while true do
1055 local event, x, y = os.pullEvent()
1056
1057 if "monitors_clicked" == event then
1058 for k, v in pairs(clickableOutputMonitors) do
1059 if v.line == y then
1060 toggleMonitor(k)
1061 return
1062 end
1063 end
1064 elseif event == "peripheral" or event == "peripheral_detach" then
1065 coroutine.yield()
1066 return
1067 end
1068 end
1069end
1070
1071local function monitorSelection()
1072 for k, v in pairs(MONITORS_ACTIVE) do
1073 if monitors[k] then
1074 monitors[k].active = true
1075 end
1076 end
1077
1078 while true do
1079 showMonitorSelection(monitorSelectionWindow)
1080 end
1081end
1082
1083local function nativeDisplay()
1084 while true do
1085 local currentTerm = term.current()
1086
1087 term.redirect(nativeMonitorTabWindow)
1088 nativeMonitorTabWindow.setVisible(true)
1089
1090 term.setCursorPos(1, 1)
1091 term.setBackgroundColor(colors.gray)
1092 term.clearLine()
1093 term.setTextColor(colors.yellow)
1094
1095 for i, v in ipairs(nativeDisplayTabs) do
1096 hideWindows(unpack(v.windows))
1097 end
1098
1099 for i, v in ipairs(nativeDisplayTabs) do
1100 local tab = v.tab
1101 tab.startX, tab.startY = getCursorPositionRelativeToParent(term.current())
1102
1103 if tab.active then
1104 term.setBackgroundColor(colors.black)
1105 showWindows(unpack(v.windows))
1106 else
1107 term.setBackgroundColor(colors.gray)
1108 end
1109
1110 term.write(" "..tab.label.." ")
1111 tab.endX, tab.endY = getCursorPositionRelativeToParent(term.current())
1112 end
1113 term.setTextColor(colors.white)
1114 term.redirect(currentTerm)
1115
1116 while true do
1117 local event, selectedTab = os.pullEvent("selected_tab")
1118
1119 if selectedTab then
1120 nativeDisplayTabs.setSelectedTab(selectedTab)
1121 break
1122 end
1123 end
1124 end
1125end
1126
1127local function mouseClickEventMonitor()
1128 while true do
1129 local event, type, x, y = os.pullEvent("mouse_click")
1130 local selectedTab = nativeDisplayTabs.getSelectedTab(x, y)
1131
1132 if selectedTab and nativeDisplayTabs.getDashboardWindows().getInformationWindow().active == false then
1133 os.queueEvent("selected_tab", selectedTab)
1134 elseif selectedTab == nil then
1135 local activeTab = nativeDisplayTabs[nativeDisplayTabs.getActiveTab()]
1136
1137 os.queueEvent(activeTab.tab.event, x, y)
1138 end
1139 end
1140end
1141
1142-- Initialization
1143initializeNativeDisplayTabs()
1144resetRedstoneState()
1145addCapacitors()
1146addMonitors()
1147
1148while true do
1149 parallel.waitForAll(mouseClickEventMonitor, nativeDisplay, monitorSelection, hotplugPeripherals, monitorCapacitors, nativeDashboardHandler, monitorDashboardHandler)
1150end