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