· 3 years ago · Sep 19, 2022, 12:50 AM
1--[[ ╔══════════════════════════════════════════════╗
2 ┃ Advanced Fluid Monitoring System ┃
3 ┃ By: PaintPauller & Ace DP Dragon ┃
4 ┃ Version: 0.06 ┃
5 ╚══════════════════════════════════════════════╝
6
7
8--|| **************************************************************** || --
9 Local Variables
10--|| **************************************************************** ]]--
11
12-- Program Variables --
13local verNum = 0.06
14local imTesting = false
15local thisComputerID = os.getComputerID()
16local A_F_M_S = {} -- table of settings filled on Initialize()
17local firstTime = not fs.exists("A_F_M_S") -- used to display help page -WIP
18local defaultSettings = {
19 ["Role"] = "Master Server | Remote Prober | Remote Displays",
20 ["Probe Rate"] = 1, -- in seconds, rounded up to the nearest multiple of 0.05
21 ["Channel"] = 42069, -- 1 to 65535, channel the display server is receiving on
22 ["Debugging"] = false, -- f1 to toggle debugging mode
23 ["tanksMissingFluidCapacity"] = {},
24}
25
26-- CC Pretty API --
27local prettyAPI = require "cc.pretty"
28local pPrint = prettyAPI.pretty_print
29
30-- Menu Variables --
31local width, height = term.getSize() -- Normally: width = 51, height = 19
32local select = 1
33local selectedPage = 1
34local currentMenuState
35local pageInfo = {} -- filled in when drawing a paged menu
36local menuFilters = {
37 ["Configure Tanks"] = {
38 ["filter"] = "All Tanks",
39 ["options"] = {
40 -- [1] = "All Tanks",
41 -- [2] = "Not Configured"
42 -- [3+] = computerIDs
43 -- filled out when updateConfigureTanksMenuOptions(menuFilter) is run.
44 }
45 }
46}
47
48-- Fluid Prober Variables --
49local tankInfo
50
51-- Display Variables --
52 -- TODO WIP
53 -- Need to import from 1st program still
54
55-- Wireless Variables --
56local objectPayload
57local modem = peripheral.find("modem")
58
59-- May use later on, from old program --
60 -- local logs = {}
61
62
63--[[ **************************************************************** ||--
64 Helpful Functions
65--|| **************************************************************** ]]--
66
67---Prints the variable to the Debug Header
68---@param pauseCode boolean Pauses the code till any key is pressed
69---@param ... any any number of Variables
70local function debugPrint(pauseCode, ...)
71 if A_F_M_S.Debugging then
72 local debugValue
73 local variables = {...}
74
75 term.setCursorPos(1, 2)
76 term.clearLine()
77 term.setCursorPos(1, 3)
78 term.clearLine()
79
80 term.setCursorPos(3, 3)
81 if #variables == 1 then
82 debugValue = variables[1]
83 pPrint(variables[1])
84 else
85 for key, value in ipairs(variables) do
86 if debugValue then
87 debugValue = debugValue.." | "..tostring(value)
88 else
89 debugValue = tostring(value)
90 end
91 end
92 print(debugValue)
93 end
94 if pauseCode then
95 os.pullEvent("key")
96 end
97 end
98end
99
100---Saves a file with the variable inside
101---@param pauseCode boolean Pauses the code till any key is pressed, does not always work in the Remote Prober Menu!
102---@param variable any variable to debug
103---@param variableName string will be saved in A_F_M_S/debug folder
104---@param usePrettyAPI? boolean allows you to have functions in the variable, but will output to a single line!
105local function debugWrite(pauseCode, variable, variableName, usePrettyAPI)
106 local f = fs.open('A_F_M_S/debug/'..variableName..".lua", 'w')
107 if usePrettyAPI then
108 f.write(variableName.." = "..prettyAPI.render(prettyAPI.pretty(variable)))
109 else
110 f.write(variableName.." = "..textutils.serialise(variable))
111 end
112 f.close()
113 if pauseCode then
114 os.pullEvent("key")
115 end
116end
117
118---Splits a string into segments separated by a pattern
119---@param pattern string
120---@param str string
121---@return table result a table of strings
122local function strSplit(pattern, str)
123 local result, i = {}, 1
124 while true do
125 local a, b = str:find(pattern)
126 if not a then break end
127 local candidate = str:sub(1, a - 1)
128 if candidate ~= "" then
129 result[i] = candidate
130 end i=i+1
131 str = str:sub(b + 1)
132 end
133 if str ~= "" then
134 result[i] = str
135 end
136 return result
137end
138
139---Joins a table of strings together
140---@param splitString table a table of strings to join together
141---@return string newStr
142local function joinString(splitString)
143 local newStr = ""
144 for i = 1, #splitString do
145 if i == 1 then
146 newStr = splitString[i]
147 else
148 newStr = newStr..' '..splitString[i]
149 end
150 end
151 return newStr
152end
153
154---Converts a string to have the first or every word capitalized
155---@param str string string to manipulate
156---@param everyWord? boolean not required
157---@return string
158local function firstToUpper(str, everyWord)
159 if everyWord then
160 splitStr = strSplit(" ", str)
161 for i = 1, #splitStr do
162 splitStr[i] = splitStr[i]:sub(1, 1):upper()..splitStr[i]:sub(2)
163 end
164 return joinString(splitStr)
165 else
166 return str:sub(1, 1):upper()..str:sub(2)
167 end
168end
169
170---Returns true if the provided number is even
171---@param num number number to check if even or not
172---@return boolean
173local function isEven(num)
174 if (num % 2 == 0) then
175 return true
176 else
177 return false
178 end
179end
180
181---Formats a number to have commas
182---@param number number
183---@return string
184local function formatNum(number)
185 local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)')
186
187 -- Reverse the int-string and append a comma to all blocks of 3 digits
188 int = int:reverse():gsub("(%d%d%d)", "%1,")
189
190 -- Reverse the int-string back remove an optional comma and put the
191 -- optional minus and fractional part back
192 return minus..int:reverse():gsub("^,", "")..fraction
193end
194
195---Returns the length of a table, use when the key of a table is not a number
196---@param inputTable table
197---@return integer
198local function getTableLength(inputTable)
199 local i = 0
200 for num, _ in pairs(inputTable) do
201 i = i +1
202 end
203 return i
204end
205
206---Checks to see if the content of two tables is the same.
207---@param table_1 table
208---@param table_2 table
209---@param ignore_mt? boolean ignore metatables (a recursive function to tests tables inside tables)
210---@return boolean
211function comparTables(table_1, table_2, ignore_mt)
212 if table_1 == table_2 then return true end
213 local table_1_Type = type(table_1)
214 local table_2_Type = type(table_2)
215 if table_1_Type ~= table_2_Type then return false end
216 if table_1_Type ~= 'table' then return false end
217
218 if not ignore_mt then
219 local mt1 = getmetatable(table_1)
220 if mt1 and mt1.__eq then
221 --compare using built in method
222 return table_1 == table_2
223 end
224 end
225
226 local keySet = {}
227 for key1, value1 in pairs(table_1) do
228 local value2 = table_2[key1]
229 if value2 == nil or comparTables(value1, value2, ignore_mt) == false then
230 return false
231 end
232 keySet[key1] = true
233 end
234
235 for key2, _ in pairs(table_2) do
236 if not keySet[key2] then return false end
237 end
238 return true
239end
240
241
242---Updates the config file & A_F_M_S table with the new setting
243---@param settingToUpdate string name of the setting to update
244---@param value any new setting value
245---@return nil nil updates the A_F_M_S table
246local function updateSettings(settingToUpdate, value)
247 local currentSettings = A_F_M_S
248 currentSettings[settingToUpdate] = value
249 local f2 = fs.open('A_F_M_S/config', 'w')
250 f2.write(textutils.serialise(currentSettings))
251 f2.close()
252 A_F_M_S = currentSettings
253end
254
255
256--[[ **************************************************************** ||--
257 Fluid Prober Functions
258--|| **************************************************************** ]]--
259
260---Returns a table of wrapped fluidTanks or nil
261---@return table|nil
262local function wrapAttachedTanks() -- Will need to run on a timer to remove and add tanks!
263 local list = {
264 ["fluid_storage"] = {},
265 ["rsBridge"] = {},
266 ["meBridge"] = {}
267 }
268 local bool = false
269 for _, peripheralName in pairs(peripheral.getNames()) do
270 for _, peripheralType in pairs(table.pack(peripheral.getType(peripheralName))) do
271 if peripheralType == "fluid_storage" or peripheralType == "rsBridge" or peripheralType == "meBridge" then
272 local wrappedTank = peripheral.wrap(peripheralName)
273 list[peripheralType][peripheralName] = wrappedTank
274 bool = true
275 end
276 end
277 end
278 if bool then
279 return list
280 else
281 return nil
282 end
283end
284
285---Gets info on all attached tanks
286---@return table
287local function getTankInfo(attachedTanks)
288 local list = {}
289 local functions = {}
290 local i = 1
291 list[thisComputerID] = {}
292 if attachedTanks then
293 -- Normal Tanks --
294 for peripheralName, wrappedTank in pairs(attachedTanks.fluid_storage) do
295 functions[i] = function()
296 local tanks = wrappedTank.tanks()
297 if tanks then
298 list[thisComputerID][peripheralName] = {}
299 list[thisComputerID][peripheralName]["peripheralType"] = "Fluid Tank"
300 if tanks[1] then
301 local fluidForgeName = tanks[1].name
302 local fluidAmount = tanks[1].amount
303 local fluidName = firstToUpper(joinString(strSplit("_", strSplit(":", fluidForgeName)[2])), true)
304 list[thisComputerID][peripheralName]["fluidForgeName"] = fluidForgeName
305 list[thisComputerID][peripheralName]["fluidName"] = fluidName
306 list[thisComputerID][peripheralName]["fluidAmount"] = fluidAmount
307 else
308 list[thisComputerID][peripheralName]["fluidAmount"] = 0
309 end
310 end
311 end
312 i = i +1
313 end
314 -- -- RS Fluid --
315 -- for peripheralName, wrappedTank in pairs(attachedTanks.rsBridge) do
316 -- functions[i] = function()
317 -- local fluids = wrappedTank.listFluids()
318 -- if fluids then
319 -- for key, fluidInfo in ipairs(fluids) do
320 -- local fluidForgeName = fluidInfo.name
321 -- local fluidAmount = fluidInfo.amount
322 -- local fluidName = fluidInfo.displayName
323 -- list[thisComputerID][peripheralName..":"..fluidName] = {}
324 -- list[thisComputerID][peripheralName..":"..fluidName]["peripheralType"] = "RS Bridge"
325 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidForgeName"] = fluidForgeName
326 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidName"] = fluidName
327 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidAmount"] = fluidAmount
328 -- end
329 -- end
330 -- end
331 -- i = i +1
332 -- end
333 -- -- AE2 Fluid --
334 -- for peripheralName, wrappedTank in pairs(attachedTanks.meBridge) do
335 -- functions[i] = function()
336 -- local fluids = wrappedTank.listFluid()
337 -- if fluids then
338 -- for key, fluidInfo in ipairs(fluids) do
339 -- local fluidForgeName = fluidInfo.name
340 -- local fluidAmount = fluidInfo.amount
341 -- local fluidName = firstToUpper(joinString(strSplit("_", strSplit(":", fluidForgeName)[2])), true)
342 -- list[thisComputerID][peripheralName..":"..fluidName] = {}
343 -- list[thisComputerID][peripheralName..":"..fluidName]["peripheralType"] = "AE2 Bridge"
344 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidForgeName"] = fluidForgeName
345 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidName"] = fluidName
346 -- list[thisComputerID][peripheralName..":"..fluidName]["fluidAmount"] = fluidAmount
347 -- end
348 -- end
349 -- end
350 -- i = i +1
351 -- end
352 end
353 parallel.waitForAll(table.unpack(functions)) -- looks up fluid info on every tank at once! Was slow b4 with a couple tanks!
354 return list
355end
356
357---Returns a table of tanks missing max sizes.
358---@param list table list of fluid data, provided by getTankInfo
359---@return table
360local function getTanksMissing_fluidCapacity(list)
361 local toReturn = {}
362 local i = 1
363 for computer_ID, tanks in pairs(list) do
364 for tankName, tankData in pairs(tanks) do
365 if not tankData["fluidCapacity"] then
366 toReturn[i] = {}
367 toReturn[i]["tankName"] = tankName
368 toReturn[i]["computerID"] = computer_ID
369 for fluidData, value in pairs(tankData) do
370 toReturn[i][fluidData] = value
371 end
372 i = i +1
373 end
374 end
375 end
376 return toReturn
377end
378
379
380--[[ **************************************************************** ||--
381 Display Functions
382--|| **************************************************************** ]]--
383
384-- TODO WIP
385-- Need to import from 1st program still
386
387
388--[[ **************************************************************** ||--
389 Wireless Communication Functions
390--|| **************************************************************** ]]--
391
392-- TODO WIP --
393-- Will be replaced!
394-- Will use RedNet and their channels and filters!
395
396---Sends a payload to other computers
397---@param channel number port to send the message on
398---@param payload any the payload to send
399local function sendPayload(channel, payload)
400 modem.transmit(channel, 0, payload)
401end
402
403
404--[[ *************************************************************** ||--
405 Print Functions
406--|| *************************************************************** ]]--
407
408---Prints a string at yPos, justified to the Left, Center, or Right with offset
409---@param str string string to print
410---@param yPos number the y position to print at
411---@param justified string \"left" | "centered" | "right"
412---@param offset? number number to offset the text | nil = 0
413local function printJustified(str, yPos, justified, offset)
414 str = tostring(str)
415 if not offset then offset = 0 end
416 if justified == "left" then
417 term.setCursorPos(offset+1,yPos)
418 term.write(str)
419 elseif justified == "centered" then
420 term.setCursorPos((width/2 - #str/2)+offset+1, yPos)
421 term.write(str)
422 elseif justified == "centeredLeft" then
423 term.setCursorPos((width/2 - #str/2)+offset+1-(width/2), yPos)
424 term.write(str)
425 elseif justified == "right" then
426 term.setCursorPos((width - #str)-offset+1, yPos)
427 term.write(str)
428 end
429end
430
431---Clears the provided lines
432---@param ... number Lines to clear, can be more than one
433local function clearLines(...)
434 local arguments = {...}
435 for i = 1, #arguments do
436 term.setCursorPos(1, arguments[i])
437 term.clearLine()
438 end
439end
440
441---Prints out the menu options and their descriptions, excluding "Change Role" and "Quit"
442---@param menuOptions table menuOptions, defined in the menuOpt table.
443---@param selectedOption string selectedOption, name of the currently selected option.
444---@param start_yPos number the yPos to start the list of options at.
445---@param spacing number the space in-between each option.
446---@param justified string \"left" | "centered" | "right"
447---@param offset? number number to offset the text | nil = 0
448local function printMenuOptions(menuOptions, selectedOption, start_yPos, spacing, justified, offset)
449 if not offset then offset = 0 end
450 local yPos = start_yPos
451 local optionName, optionDescription, printName
452 for i = 1, #menuOptions do
453 -- Prevents "Quit" and "Change Role" from printing
454 -- Get optionName, optionDescription, currentSetting value
455 if menuOptions[i][1] == "Quit" or menuOptions[i][1] == "Change Role"then
456 optionName = ""
457 optionDescription = menuOptions[i][2]
458 currentSetting = A_F_M_S[optionName]
459 else
460 optionName = menuOptions[i][1]
461 optionDescription = menuOptions[i][2]
462 currentSetting = A_F_M_S[optionName]
463 end
464 if not optionDescription then optionDescription = "" end
465
466 -- Include setting values
467 if currentSetting then
468 currentSetting = ": "..currentSetting
469 printName = optionName..currentSetting
470 else
471 currentSetting = ""
472 printName = optionName
473 end
474
475 -- Print out the options, ignoring "Change Role", and "Quit"
476 printJustified(printName, yPos, justified, offset)
477
478 -- Indicate what is selected
479 if selectedOption == optionName and selectedOption ~= "Quit" and selectedOption ~= "Change Role" then
480 if justified == "left" then
481 printJustified("\26 ", yPos, justified, offset -2)
482 printJustified(string.rep("\175", #optionName), yPos +1, justified, offset)
483 elseif justified == "centered" and isEven(#optionName + #currentSetting) then
484 printJustified("\26", yPos, justified, offset - ((#optionName + #currentSetting)/2) -2)
485 printJustified(string.rep("\175", #optionName), yPos +1, justified, offset - (#currentSetting/2))
486 elseif justified == "centered" and not isEven(#optionName+#currentSetting) then
487 printJustified("\26", yPos, justified, offset - ((#optionName + #currentSetting)/2) -1)
488 printJustified(string.rep("\175", #optionName), yPos +1, justified, offset - (#currentSetting/2))
489 elseif justified == "right" then
490 printJustified(" \27", yPos, justified, offset -2)
491 printJustified(string.rep("\175", #optionName), yPos +1, justified, offset + #currentSetting)
492 end
493
494 -- Print the Option Description
495 printJustified(optionDescription, height -2, "centered")
496 end
497 yPos = yPos + spacing +2
498 end
499end
500
501---Prints out the menu options in a in columns with pages.
502---@param menuOptions table menuOptions, defined in the menuOpt table.
503---@param selectedOption string selectedOption, name of the currently selected option.
504---@param removeOptions number removes x number of options from the end of the list.
505---@param start_yPos number the yPos to start the list of options at.
506---@param spacing number the space in-between each option.
507---@param shortenRows number shortens the number of rows in each column.
508---@param columns number the number of columns to display.
509---@param justified string \"left" | "centered" ("right" NOT supported!)
510---@param offset? number number to offset the text | nil = 0
511local function printPagedOptions(menuOptions, selectedOption, removeOptions, start_yPos, spacing, shortenRows, columns, justified, offset)
512 -- Set the column width and the starting x offset for the 1st column --
513 local xPos_offset
514 local columnWith = math.floor(width / columns)
515 if justified == "left" then
516 xPos_offset = math.ceil(columnWith / (columns * 2))
517 elseif justified == "centered" then
518 xPos_offset = math.ceil((width/2 * -1) + (columnWith / columns ) +1 )
519 elseif justified == "right" then
520 xPos_offset = math.ceil(columnWith / (columns * 2))
521 end
522
523 -- calculate the rows that will fit on the page, and how many pages we need --
524 local rows = math.ceil((height - start_yPos) / (2 + spacing )) - shortenRows
525 local optionsPerPage = rows * columns
526 local numberOf_Pages = math.ceil((#menuOptions - removeOptions) / optionsPerPage)
527 if numberOf_Pages == 0 then numberOf_Pages = 1 end
528
529 -- calculate the range of options to display on this page --
530 local pageStartOption = (selectedPage * optionsPerPage) - optionsPerPage +1
531 local pageEndOption = selectedPage * optionsPerPage
532 -- fix the last page
533 if numberOf_Pages == selectedPage then
534 pageEndOption = #menuOptions - removeOptions
535 -- Change the number of rows to make the columns have equal options
536 rows = math.ceil((pageEndOption - pageStartOption + 1) / columns)
537 end
538
539 -- fill out pageInfo table to use in the RunMenu function --
540 pageInfo = {}
541 for i = 1, numberOf_Pages do
542 local page_StartOption = (i * optionsPerPage) - optionsPerPage +1
543 local page_EndOption = i * optionsPerPage
544 local page_Rows = rows
545 if i == numberOf_Pages then
546 page_EndOption = #menuOptions - removeOptions
547 page_Rows = math.ceil((pageEndOption - pageStartOption +1) / columns)
548 end
549
550 pageInfo[i] = {}
551 pageInfo[i].rows = page_Rows
552 pageInfo[i].columns = columns
553 pageInfo[i].pageStartOption = page_StartOption
554 pageInfo[i].pageEndOption = page_EndOption
555 end
556 pageInfo.numberOf_Pages = numberOf_Pages
557 pageInfo.optionsPerPage = optionsPerPage
558
559 -- Print the current page of options
560 local row = 1
561 -- local column = 1
562 local yPos = start_yPos
563 if not offset then offset = 0 end
564 for i = pageStartOption, pageEndOption do
565 local optionName = menuOptions[i][1] -- Will use later on to add check boxes | display option values
566
567 -- Print out the options --
568 printJustified(optionName, yPos, justified, xPos_offset + offset)
569
570 -- Indicate what is selected
571 if selectedOption == optionName and select == i then
572 if justified == "left" then
573 printJustified("\26 ", yPos, justified, (xPos_offset + offset) -2)
574 printJustified(string.rep("\175", #optionName), yPos +1, justified, (xPos_offset + offset))
575 elseif justified == "centered" then
576 printJustified("\26 ", yPos, justified, (xPos_offset + offset) - ((#optionName/2)) -1)
577 printJustified(string.rep("\175", #optionName), yPos +1, justified, (xPos_offset + offset))
578 elseif justified == "right" then -- does not work, will mess with the option order, don't use!
579 printJustified("\26 ", yPos, justified, (xPos_offset + offset) -2)
580 printJustified(string.rep("\175", #optionName), yPos +1, justified, (xPos_offset + offset))
581 end
582 end
583
584 yPos = yPos + spacing +2
585 row = row +1
586 if row > rows then
587 -- reset for the next column --
588 yPos = start_yPos
589 xPos_offset = (xPos_offset + columnWith)
590 row = 1
591 -- column = column +1
592 end
593 end
594end
595
596---Prints the "Change Role" option
597---@param selectedOption string selectedOption, name of the currently selected option.
598---@param yPos number the yPos to print the "Change Role" option at.
599---@param justified string \"left" | "centered" | "right"
600---@param offset? number number to offset the text | nil = 0
601local function printChangeRoleOption(selectedOption, yPos, justified, offset)
602 printJustified("Change Role", yPos, justified, offset)
603 if selectedOption == "Change Role" then
604 printJustified(" Change Role \27", yPos, justified, offset)
605 end
606end
607
608---Prints the "Quit" option
609---@param selectedOption string selectedOption, name of the currently selected option.
610---@param yPos number the yPos to print the "Quit" option at.
611---@param justified string \"left" | "centered" | "right"
612---@param offset? number number to offset the text | nil = 0
613local function printQuitOption(selectedOption, yPos, justified, offset)
614 printJustified("Quit", yPos, justified, offset)
615 if selectedOption == "Quit" then
616 printJustified("\26 Quit ", yPos, justified, offset)
617 end
618end
619
620---Prints the "Back" option
621---@param selectedOption string selectedOption, name of the currently selected option.
622---@param yPos number the yPos to print the "Quit" option at.
623---@param justified string \"left" | "centered" | "right"
624---@param offset? number number to offset the text | nil = 0
625local function printBackOption(selectedOption, yPos, justified, offset)
626 printJustified("Back", yPos, justified, offset)
627 if selectedOption == "Back" then
628 printJustified("\26 Back ", yPos, justified, offset)
629 end
630end
631
632
633--[[ *************************************************************** ||--
634 Draw Functions
635--|| *************************************************************** ]]--
636
637--- Draws the Headers and Footers for all Menus
638local function drawHeaderFooter(menuOpt, menuState, selectedOption)
639 if A_F_M_S.Debugging == false and menuState ~= "Configure Tanks" then
640 -- Draws the Header --
641 printJustified("\4"..string.rep("-", width -2).."\4", 1, "centered")
642 printJustified("\4 Advanced Fluid \4", 1, "centered")
643 printJustified("\166 Monitoring System \166", 2, "centered")
644 printJustified("\166 \166", 3, "centered")
645 printJustified("\4"..string.rep("-",23).."\4", 4, "centered")
646 printJustified("\17 "..menuState.." \16", 4, "centered")
647
648 --printJustified("By: PaintPauller & Ace DP Dragon", height, "left", 1) -- is too long to look good?
649 printJustified("By: PaintPauller & Ace", height, "left")
650 printJustified("Version: "..verNum, height, "right")
651
652 -- Draws the Footer --
653 printJustified("\4"..string.rep("-", width -2).."\4", height -1, "centered")
654 end
655
656 -- Draws the side Borders --
657 if menuState ~= "Configure Tanks" then
658 for i = 2, height -2 do
659 printJustified("\166", i, "left")
660 printJustified("\166", i, "right")
661 end
662 else
663 for i = 4, height -2 do
664 printJustified("\166", i, "left")
665 printJustified("\166", i, "right")
666 end
667 end
668
669 -- Draws the Debugging Header and Footer --
670 if A_F_M_S.Debugging then
671 -- Draw the Debugging Header, debugPrint box --
672 printJustified("\4"..string.rep("-", width -2).."\4", 1, "centered")
673 printJustified("\17 debugValue \16---\17 Any Key: Step Code \16", 1, "centered")
674
675 if menuState ~= "Configure Tanks" then -- Normal Menus
676 printJustified("\4"..string.rep("-", width -2).."\4", 4, "centered")
677
678 -- Draws the Footer --
679 clearLines(height)
680 printJustified("\4"..string.rep("-", width -2).."\4", height -1, "centered")
681 -- The Debugging Footer, Displays the below 3 variables --
682 -- selectedOption: select menuState --
683 printJustified(menuState, height, "right", 1)
684 printJustified(tostring(selectedOption)..": "..select, height, "left", 1)
685 else -- Configure Tanks Menu
686 printJustified(tostring(selectedOption)..": "..select, height -3, "left", 1)
687 end
688 end
689end
690
691---Draws the Edit Box Menu
692---@param yPos number where to draw the edit box menu
693---@param isValidFunction function a function that takes user input and checks if its valid, return (true & valid user input) or (string with error message).
694---@param inputLabel string the name of the setting or value we are editing
695---@param description string the description of what we are changing
696---@param defaultValue? any the default value of the setting or variable we are editing
697---@return boolean | any \returns false or userInput
698local function drawEditBox(yPos, isValidFunction, inputLabel, description, defaultValue)
699 if not defaultValue then defaultValue = "" end
700 defaultValue = tostring(defaultValue)
701 while true do
702 -- Draw the box --
703 printJustified("\166 \4"..string.rep("-", 41).."\4 \166", yPos, "centered")
704 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
705 printJustified("\166 \4"..string.rep("-", 41).."\4 \166", yPos +2, "centered")
706 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +3, "centered")
707 printJustified("\166 \4"..string.rep("-", 41).."\4 \166", yPos +4, "centered")
708
709 -- Draw the options--
710 printJustified("Cancel", yPos +3, "left", 10)
711 printJustified("Edit", yPos +3, "centered")
712 printJustified("Save", yPos +3, "right", 10)
713
714 -- Draw Labels --
715 printJustified("\17 "..description.." \16", yPos, "centered")
716 printJustified(inputLabel, yPos +1, "left", 6)
717
718 -- Get User Input --
719 term.setCursorPos(#inputLabel +7, yPos +1)
720 local isValid, userInput = isValidFunction(read(_, _, _, defaultValue))
721 if isValid ~= true then
722 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
723 printJustified("! "..isValid.." !", yPos +1, "centered")
724 end
725
726 -- Run the options --
727 local acted = false
728 local localSelect = 2
729 while not acted do
730 -- Draw the options--
731 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +3, "centered")
732 printJustified("Cancel", yPos +3, "left", 10)
733 printJustified("Edit", yPos +3, "centered")
734 printJustified("Save", yPos +3, "right", 10)
735
736 -- Indicate what is selected --
737 if localSelect == 1 then
738 printJustified("\26 Cancel", yPos +3, "left", 8)
739 elseif localSelect == 2 then
740 printJustified("\26 Edit", yPos +3, "centered", -1)
741 elseif localSelect == 3 then
742 printJustified("\26 Save", yPos +3, "right", 10)
743 end
744
745 -- User Pressed a key --
746 local id, key = os.pullEvent("key")
747
748 -- A movement key was pressed --
749 if key == keys.right or key == keys.d then
750 localSelect = localSelect +1
751 if localSelect > 3 then localSelect = 1 end
752 elseif key == keys.left or key == keys.a then
753 localSelect = localSelect -1
754 if localSelect < 1 then localSelect = 3 end
755
756 -- Enter or space was pressed lets do something --
757 elseif key == keys.space or key == keys.enter or key == keys.numPadEnter then
758 if localSelect == 1 then
759 return false
760 elseif localSelect == 2 then
761 acted = true
762 elseif localSelect == 3 then
763 if isValid == true then
764 return userInput
765 else -- Trying to save a invalid input, lets shake the error message! --
766 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
767 printJustified("! "..isValid.." !", yPos +1, "centered", -1)
768 sleep(.25)
769 for i = -2, 2 do
770 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
771 printJustified("! "..isValid.." !", yPos +1, "centered", i)
772 sleep(.25)
773 end
774 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
775 printJustified("! "..isValid.." !", yPos +1, "centered", 1)
776 sleep(.25)
777 printJustified("\166 \166"..string.rep(" ", 41).."\166 \166", yPos +1, "centered")
778 printJustified("! "..isValid.." !", yPos +1, "centered")
779 end
780 end
781 end
782 end
783 end
784end
785
786
787--[[ *************************************************************** ||--
788 Draw Menu Functions
789--|| *************************************************************** ]]--
790
791--- Draws the Change Role Menu ---
792local function drawChangeRole(options, selectedOption)
793 -- Draw Options --
794 printMenuOptions(options, selectedOption, 7, 1, "centered", 0) -- "left", 19
795 printQuitOption(selectedOption, height -4, "right", 2)
796
797 -- Draw Description box --
798 printJustified("\4"..string.rep("-", width -2).."\4", height -3, "centered")
799end
800
801--- Draws the Remote Prober Menu ---
802local function drawRemoteProber(options, selectedOption)
803 -- Draw Options --
804 if getTableLength(A_F_M_S.tanksMissingFluidCapacity) ~= 0 then
805 -- Move the menu over for the warning message!
806 printMenuOptions(options, selectedOption, 7, 0, "left", 7)
807 else
808 printMenuOptions(options, selectedOption, 6, 1, "centered", 0)
809 end
810 printQuitOption(selectedOption, height -4, "right", 2)
811 printChangeRoleOption(selectedOption, height -4, "left", 2)
812
813 -- Draw Description box --
814 printJustified("\4"..string.rep("-", width -2).."\4", height -3, "centered")
815end
816
817--- Draws the Display Server Menu ---
818local function drawDisplayServer(options, selectedOption)
819 -- Draw Options --
820 printMenuOptions(options, selectedOption, 8, 2, "centered")
821 printQuitOption(selectedOption, height -4, "right", 2)
822 printChangeRoleOption(selectedOption, height -4, "left", 2)
823
824 -- Draw Description box --
825 printJustified("\4"..string.rep("-", width -2).."\4", height -3, "centered")
826end
827
828--- Draws the Remote Display Menu ---
829local function drawRemoteDisplay(options, selectedOption)
830 -- Draw Options --
831 printMenuOptions(options, selectedOption, 8, 2, "centered")
832 printQuitOption(selectedOption, height -4, "right", 2)
833 printChangeRoleOption(selectedOption, height -4, "left", 2)
834
835 -- Draw Description box --
836 printJustified("\4"..string.rep("-", width -2).."\4", height -3, "centered")
837end
838
839-- Draws the Configure Tanks Menu --
840local function drawConfigureTanks(options, selectedOption)
841 -- Draw Options --
842 printPagedOptions(options, selectedOption, 1, 4, 0, 2, 2, "left", 0)
843 printBackOption(selectedOption, height -3, "right", 2)
844
845 -- Draw Header --
846 -- We use some of the normal Header space for this menu
847 -- Allow the Debug Header to display here if needed!
848 if not A_F_M_S.Debugging then
849 printJustified("Configure Tanks", 1, "centered")
850 printJustified("Page: "..selectedPage.."/"..pageInfo.numberOf_Pages, 2, "centered")
851 printJustified("Filter: ", 1, "left", 1)
852 if type(menuFilters["Configure Tanks"].filter) == "number" then
853 printJustified("Computer "..menuFilters["Configure Tanks"].filter, 2, "left", 1)
854 else
855 printJustified(menuFilters["Configure Tanks"].filter, 2, "left", 1)
856 end
857 end
858 printJustified("\4"..string.rep("-", width -2).."\4", 3, "centered")
859
860 -- Draw Footer Description box --
861 printJustified("\4"..string.rep("-", width -2).."\4", height -2, "centered")
862 if select ~= #options then
863 local tankCapacity
864 if options[select]["fluidCapacity"] then
865 tankCapacity = "/"..formatNum(options[select]["fluidCapacity"])
866 else tankCapacity = "" end
867 printJustified("\17 "..formatNum(options[select]["fluidAmount"])..tankCapacity.." mB \16", height -2, "centered")
868 printJustified("Computer: "..options[select]["computerID"], height -1, "left", 2)
869 printJustified("Tank Name: "..options[select]["tankName"], height -1, "left", 16)
870 if options[select]["fluidForgeName"] then
871 printJustified("Forge Name: "..tostring(options[select]["fluidForgeName"]), height, "left", 2)
872 else
873 printJustified("Forge Name: ", height, "left", 2)
874 end
875 end
876end
877
878-- Define the Menu Options --
879local menuState
880local lastMenuState
881local menuOpt = {
882 ["Change Role"] = {
883 draw = drawChangeRole,
884 paged = false,
885 options = {
886 [1] = {"Remote Prober", "Sends Probed fluid info to Master Server"},
887 [2] = {"Master Server", "Handles communications for Remote Computers"},
888 [3] = {"Remote Displays", "Displays fluid info on connected monitors"},
889 [4] = {"Quit", "Quits to the Terminal"},
890 },
891 },
892 ["Remote Prober"] = {
893 draw = drawRemoteProber,
894 paged = false,
895 options = {
896 [1] = {"Configure Tanks", "Set the max size for connected tanks"},
897 [2] = {"Probe Rate", "Probe Tanks for fluid info, in seconds"},
898 [3] = {"Channel", "Channel for this A.F.M.S network"},
899 [4] = {"Change Role", "Change the Role of this Computer"},
900 [5] = {"Quit", "Quits to the Terminal"},
901 },
902 },
903 ["Master Server"] = {
904 draw = drawDisplayServer,
905 paged = false,
906 options = {
907 [1] = {"Configure Monitors", ""},
908 [2] = {"Channel", "Channel for this A.F.M.S network"},
909 [3] = {"Change Role", "Change the Role of this Computer"},
910 [4] = {"Quit", "Quits to the Terminal"},
911 },
912 },
913 ["Remote Displays"] = {
914 draw = drawRemoteDisplay,
915 paged = false,
916 options = {
917 [1] = {"Configure Monitors", ""},
918 [2] = {"Channel", "Channel for this A.F.M.S network"},
919 [3] = {"Change Role", "Change the Role of this Computer"},
920 [4] = {"Quit", "Quits to the Terminal"},
921 },
922 },
923 ["Configure Tanks"] = {
924 draw = drawConfigureTanks,
925 paged = true,
926 options = { }, -- filled in later when draw menus.
927 },
928
929--[[ ["Config Monitors"] = { --Will be replaced!
930 -- options = { "Scan for New Display Slaves", "Configure Monitors", "Back" },
931 -- draw = drawSetupMonitors
932 }, ]]
933}
934
935--- fills the options for the Configure Tanks Menu
936local function updateConfigureTanksMenuOptions()
937 -- update filter options --
938 menuFilters["Configure Tanks"]["options"] = {"All Tanks", "Not Configured"}
939 for computer_ID, tanks in pairs(tankInfo) do
940 table.insert(menuFilters["Configure Tanks"]["options"], computer_ID)
941 end
942
943 -- set the menu options based on the filter --
944 menuOpt["Configure Tanks"].options = {}
945 local i = 1
946 local currentFilter = menuFilters["Configure Tanks"].filter
947 if currentFilter == "All Tanks" then
948 for computer_ID, tanks in pairs(tankInfo) do
949 for tankName, tankData in pairs(tanks) do
950 if tankData["fluidName"] then
951 table.insert(menuOpt["Configure Tanks"]["options"], {tankData["fluidName"]})
952 else
953 table.insert(menuOpt["Configure Tanks"]["options"], {tankName})
954 end
955 for fluidData, value in pairs(tankData)do
956 menuOpt["Configure Tanks"]["options"][i][fluidData] = value
957 end
958 menuOpt["Configure Tanks"]["options"][i]["computerID"] = computer_ID
959 menuOpt["Configure Tanks"]["options"][i]["tankName"] = tankName
960 i = i +1
961 end
962 end
963 elseif currentFilter == "Not Configured" then
964 for _, tankData in pairs(A_F_M_S.tanksMissingFluidCapacity) do
965 if tankData["fluidName"] then
966 table.insert(menuOpt["Configure Tanks"]["options"], {tankData["fluidName"]})
967 else
968 table.insert(menuOpt["Configure Tanks"]["options"], {tankData["tankName"]})
969 end
970 for fluidData, value in pairs(tankData)do
971 menuOpt["Configure Tanks"]["options"][i][fluidData] = value
972 end
973 i = i +1
974 end
975 elseif type(currentFilter) == "number" then
976 for tankName, tankData in pairs(tankInfo[currentFilter]) do
977 if tankData["fluidName"] then
978 table.insert(menuOpt["Configure Tanks"]["options"], {tankData["fluidName"]})
979 else
980 table.insert(menuOpt["Configure Tanks"]["options"], {tankName})
981 end
982 for fluidData, value in pairs(tankData)do
983 menuOpt["Configure Tanks"]["options"][i][fluidData] = value
984 end
985 menuOpt["Configure Tanks"]["options"][i]["computerID"] = currentFilter
986 menuOpt["Configure Tanks"]["options"][i]["tankName"] = tankName
987 i = i +1
988 end
989 end
990 table.insert(menuOpt["Configure Tanks"]["options"], {"Back", "Go Back to the Remote Prober Menu"})
991end
992
993--[[ *************************************************************** ||--
994 Run Functions
995--|| *************************************************************** ]]--
996
997--- Run Fluid Prober ---
998local function runFluidProber()
999 local attachedTanks = wrapAttachedTanks()
1000 local updateAttachedTanks_Timer = os.startTimer(3)
1001 local probeTanks_Timer = os.startTimer(0)
1002 while true do
1003 -- A timer went off, Lets do something! --
1004 local _, timerID = os.pullEvent("timer")
1005
1006 -- it was the attachedTanks timer
1007 if timerID == updateAttachedTanks_Timer then
1008 -- Check for any tanks that were removed or added --
1009 attachedTanks = wrapAttachedTanks()
1010 updateAttachedTanks_Timer = os.startTimer(5)
1011
1012 -- it was the probeTanks timer
1013 elseif timerID == probeTanks_Timer then
1014 -- Update the tankInfo table --
1015 local tankInfo_Updated = false
1016 local new_tankInfo = getTankInfo(attachedTanks)
1017 for computer_ID, tanks in pairs(new_tankInfo) do
1018 -- remove any tanks from the ist that are no-longer attached to the computer
1019 local delete = {}
1020 for tankName, tankData in pairs(tankInfo[computer_ID]) do
1021 if not tanks[tankName] then
1022 delete[tankName] = true
1023 end
1024 end
1025 for tankName in pairs(delete) do
1026 tankInfo[computer_ID][tankName] = nil
1027 tankInfo_Updated = true
1028 end
1029 -- /\ make it work with RS and AE2, needs to keep fluids with a capacity
1030
1031 -- Update any new tankInfo
1032 for tankName, tankData in pairs(tanks) do
1033 for fluidData, value in pairs(tankData)do
1034 if not tankInfo[computer_ID] then
1035 tankInfo[computer_ID] = {}
1036 tankInfo_Updated = true
1037 end
1038 if not tankInfo[computer_ID][tankName] then
1039 tankInfo[computer_ID][tankName] = {}
1040 tankInfo_Updated = true
1041 end
1042 if not tankInfo[computer_ID][tankName][fluidData] then
1043 tankInfo[computer_ID][tankName][fluidData] = value
1044 tankInfo_Updated = true
1045 end
1046 if tankInfo[computer_ID][tankName][fluidData] ~= value then
1047 tankInfo[computer_ID][tankName][fluidData] = value
1048 tankInfo_Updated = true
1049 end
1050 end
1051 end
1052 end
1053
1054 if not comparTables(A_F_M_S.tanksMissingFluidCapacity, getTanksMissing_fluidCapacity(tankInfo)) then
1055 updateSettings("tanksMissingFluidCapacity", getTanksMissing_fluidCapacity(tankInfo))
1056 if currentMenuState == "Remote Prober" then
1057 -- refresh the menu to move it over for the warning message
1058 os.queueEvent("key")
1059 end
1060 end
1061
1062 if tankInfo_Updated then
1063 -- Save the new tankInfo to the computer
1064 local f = fs.open('A_F_M_S/tankInfo', 'w')
1065 f.write(textutils.serialise(tankInfo))
1066 f.close()
1067 end
1068 probeTanks_Timer = os.startTimer(A_F_M_S["Probe Rate"])
1069 end
1070 -- WIP --
1071 -- need to add sending this table to other computers!
1072 end
1073end
1074
1075--- Displays Warning Message when tanks are missing fluidCapacity variable
1076local function runFluidWarning()
1077 local i = 1
1078 local char = {
1079 [1] = { ["h2"] = " -", ["h"] = " ", ["v"] = "|", ["v1"] = " ", ["i"] = "\19",["i1"] = " " },
1080 [2] = { ["h2"] = "- ", ["h"] = "-", ["v"] = " ", ["v1"] = "|", ["i"] = " ",["i1"] = "\19" }
1081 }
1082 local function printWarning(widthOffset, heightOffset)
1083 local numMissing = getTableLength(A_F_M_S.tanksMissingFluidCapacity)
1084 printJustified("\4"..string.rep(char[i].h2, 7)..char[i].h.."\4", heightOffset, "centered", widthOffset)
1085 printJustified(char[i].i1.." Alert "..char[i].i, heightOffset +1, "centered", widthOffset)
1086 printJustified(tostring(numMissing), heightOffset +3, "centered", widthOffset)
1087 printJustified("Tanks Require", heightOffset +5, "centered", widthOffset)
1088 printJustified("Configuring", heightOffset +6, "centered", widthOffset)
1089 printJustified("\4"..string.rep(char[i].h2, 7)..char[i].h.."\4", heightOffset +7, "centered", widthOffset)
1090 for ii = heightOffset +1, heightOffset +6, 2 do
1091 printJustified(char[i].v, ii, "left", widthOffset +17)
1092 printJustified(char[i].v1, ii +1, "left", widthOffset +17)
1093
1094 printJustified(char[i].v1, ii, "left", widthOffset +33)
1095 printJustified(char[i].v, ii +1, "left", widthOffset +33)
1096 end
1097 if i == 1 then i = 2 else i = 1 end
1098 end
1099
1100 while true do
1101 if getTableLength(A_F_M_S.tanksMissingFluidCapacity) ~= 0 then
1102 if currentMenuState == "Remote Prober" then
1103 printWarning(13, 6)
1104 end
1105 end
1106 sleep(.5)
1107 end
1108end
1109
1110--- Run Menu --
1111local function runMenu()
1112 while true do
1113 -- Update Options for Dynamic Menus --
1114 updateConfigureTanksMenuOptions()
1115
1116 -- Make some simpler variables to work with --
1117 local options = menuOpt[menuState].options
1118 local pagedMenu = menuOpt[menuState].paged
1119 local optionNames = {}
1120 for i = 1, #options do
1121 table.insert(optionNames, options[i][1])
1122 end
1123 local selectedOption = optionNames[select]
1124 currentMenuState = menuState
1125
1126 -- Draw the Screen --
1127 term.clear()
1128 drawHeaderFooter(menuOpt, menuState, selectedOption)
1129 menuOpt[menuState].draw(options, selectedOption) -- Draws the selected menu
1130
1131 -- The last 2 options are Quit and Change Role,
1132 -- Change how movement works so we can use right and left movements!
1133 local quitRole = (options[#options][1] == "Quit" and options[#options -1][1] == "Change Role")
1134 local quitRoleSelected = (selectedOption == "Quit" or selectedOption == "Change Role")
1135
1136 -- Make some files to look into important tables as they change --
1137 debugWrite(false, options, "options")
1138 debugWrite(false, menuFilters, "menuFilters")
1139 debugWrite(false, pageInfo, "pageInfo")
1140
1141 -- A Key was Pressed!! Lets do Something --
1142 local id, key
1143 parallel.waitForAny(
1144 function() id, key = os.pullEvent("key") end,
1145 runFluidWarning
1146 )
1147 if id == "key" then
1148 -- A movement Key was Pressed --
1149 if (key == keys.right or key == keys.d) then
1150 if pagedMenu and select ~= #optionNames then
1151 if select + pageInfo[selectedPage].rows > pageInfo[selectedPage].pageEndOption then
1152 -- Wrap around - ! breaks on last page if its not full !
1153 -- select = select - (pageInfo[selectedPage].rows * (pageInfo[selectedPage].columns -1))
1154 else
1155 -- Move Right
1156 select = select + pageInfo[selectedPage].rows
1157 end
1158 elseif select < #optionNames and quitRole and quitRoleSelected then
1159 -- Move Right
1160 select = select +1
1161 end
1162 elseif (key == keys.left or key == keys.a) then
1163 if pagedMenu and select ~= #optionNames then
1164 if select - pageInfo[selectedPage].rows < pageInfo[selectedPage].pageStartOption then
1165 -- Wrap around - ! breaks on last page if its not full !
1166 -- select = select + (pageInfo[selectedPage].rows * (pageInfo[selectedPage].columns -1))
1167 else
1168 -- Move Left
1169 select = select - pageInfo[selectedPage].rows
1170 end
1171 elseif select == #optionNames and quitRole and quitRoleSelected then
1172 -- Move Left
1173 select = select -1
1174 end
1175 elseif (key == keys.up or key == keys.w) then
1176 if pagedMenu then
1177 if select == pageInfo[selectedPage].pageStartOption then
1178 -- select back
1179 select = #optionNames
1180 elseif select == #optionNames then
1181 -- select the last option on the page
1182 select = pageInfo[selectedPage].pageEndOption
1183 else
1184 -- Move up
1185 select = select -1
1186 end
1187 elseif quitRole and select == 1 then
1188 -- Wrap around to the Change Role | Quit Options
1189 -- changed how it works while imTesting, to make it harder to accidentally quit, needs to be: select = #optionNames -1
1190 if imTesting then
1191 select = #optionNames
1192 else
1193 select = #optionNames -1
1194 end
1195 elseif quitRole and quitRoleSelected then
1196 -- Move up to normal options
1197 select = #optionNames -2
1198 elseif select > 1 then
1199 -- Move up in the menu
1200 select = select -1
1201 elseif select == 1 then
1202 -- Wrap around to the end of menu
1203 select = #optionNames
1204 end
1205 elseif (key == keys.down or key == keys.s) then
1206 if pagedMenu then
1207 if select == pageInfo[selectedPage].pageEndOption then
1208 -- select back
1209 select = #optionNames
1210 elseif select == #optionNames then
1211 -- select the last option on the page
1212 select = pageInfo[selectedPage].pageStartOption
1213 else
1214 -- Move down
1215 select = select +1
1216 end
1217 elseif quitRole and quitRoleSelected then
1218 -- Wrap around to normal options
1219 select = 1
1220 elseif select < #optionNames then
1221 -- Move down in the menu
1222 select = select +1
1223 elseif select == #optionNames then
1224 -- Wrap around to the start of menu
1225 select = 1
1226 end
1227
1228 -- Change the selected Page --
1229 elseif (key == keys.pageUp or key == keys.numPadAdd or key == keys.equals or key == keys.e) then
1230 if pagedMenu then
1231 if selectedPage == pageInfo.numberOf_Pages then
1232 -- Wrap around to the 1st page
1233 selectedPage = 1
1234 else
1235 -- Select the next page
1236 selectedPage = selectedPage +1
1237 end
1238 -- Select the 1st option on the page
1239 select = pageInfo[selectedPage].pageStartOption
1240 end
1241 elseif (key == keys.pageDown or key == keys.numPadSubtract or key == keys.minus or key == keys.q) then
1242 if pagedMenu then
1243 if selectedPage == 1 then
1244 -- Wrap around to the last page
1245 selectedPage = pageInfo.numberOf_Pages
1246 else
1247 -- Select the previous page
1248 selectedPage = selectedPage -1
1249 end
1250 -- Select the 1st option on the page
1251 select = pageInfo[selectedPage].pageStartOption
1252 end
1253
1254 -- Toggle the Debugging Mode on f1
1255 elseif key == keys.f1 then
1256 if A_F_M_S.Debugging then
1257 updateSettings("Debugging", false)
1258 elseif not A_F_M_S.Debugging then
1259 updateSettings("Debugging", true)
1260 end
1261
1262 -- Toggle the Menu Filter on f
1263 elseif key == keys.f and (menuState == "Configure Tanks") then
1264 local newFilter = menuFilters[menuState].filter
1265 for i = 1, #menuFilters[menuState].options do
1266 if menuFilters[menuState]["options"][i] == menuFilters[menuState].filter then
1267 if i == #menuFilters[menuState].options then
1268 newFilter = menuFilters[menuState]["options"][1]
1269 else
1270 newFilter = menuFilters[menuState]["options"][i +1]
1271 end
1272 end
1273 end
1274 selectedPage = 1
1275 select = pageInfo[selectedPage].pageStartOption
1276 menuFilters[menuState].filter = newFilter
1277
1278 -- Enter or Space was pressed lets do something --
1279 elseif key == keys.enter or key == keys.numPadEnter or key == keys.space then
1280 local interacted = false -- Make sure we only act once per key pressed!
1281
1282 -- Quit out of the while true do loop --
1283 -- Will exit to terminal after cleaning up the screen!
1284 if selectedOption == "Quit" and select == #options then break end
1285
1286 -- If we selected a menu then go to that menu --
1287 for name, info in pairs(menuOpt) do
1288 if selectedOption == name then
1289 -- If we are in the Change Role menu then Update the Role of this A.F.M.S Computer --
1290 if menuState == "Change Role" then
1291 updateSettings("Role", selectedOption)
1292 end
1293
1294 -- Change Menu --
1295 lastMenuState = menuState
1296 menuState = selectedOption
1297 select = 1
1298 selectedPage = 1
1299 interacted = true
1300
1301 -- Run this code when we open specific menu --
1302 if menuState == "Configure Tanks" then
1303 -- set the default filter for the options
1304 if getTableLength(A_F_M_S.tanksMissingFluidCapacity) ~= 0 then
1305 menuFilters["Configure Tanks"].filter = "Not Configured"
1306 else
1307 menuFilters["Configure Tanks"].filter = "All Tanks"
1308 end
1309 end
1310 elseif selectedOption == "Back" and select == #options then
1311 -- Go back to the last menu --
1312 menuState = lastMenuState
1313 select = 1
1314 selectedPage = 1
1315 interacted = true
1316 end
1317 end
1318
1319 -- Update the Probe Rate of this A.F.M.S Computer --
1320 if not interacted and menuState == "Remote Prober" and selectedOption == "Probe Rate" then
1321 interacted = true
1322
1323 -- Function to pass to the edit box to check for a valid input --
1324 local function isValid(input)
1325 input = tonumber(input)
1326 if type(input) == "number" then
1327 if input >= 0.1 and input <= 120 then
1328 return true, input
1329 else
1330 return "Invalid Number"
1331 end
1332 else
1333 return "Not a Number"
1334 end
1335 end
1336
1337 -- Draw the edit box and get any userInput --
1338 local userInput = drawEditBox(height -6, isValid, "Rate: ", "Seconds: 0.1 - 120", defaultSettings["Probe Rate"])
1339 if userInput then
1340 updateSettings("Probe Rate", userInput)
1341 end
1342 end
1343
1344 -- Update the A.F.M.S Channel of this Computer --
1345 if (menuState == "Remote Prober" or menuState == "Master Server" or menuState == "Remote Displays")
1346 and not interacted and selectedOption == "Channel" then
1347 interacted = true
1348
1349 -- Function to pass to the edit box to check for a valid input --
1350 local function isValid(input)
1351 input = tonumber(input)
1352 if type(input) == "number" then
1353 if input >= 1 and input <= 65535 then
1354 return true, input
1355 else
1356 return "Invalid Number"
1357 end
1358 else
1359 return "Not a Number"
1360 end
1361 end
1362
1363 -- Draw the edit box and get any userInput --
1364 local userInput = drawEditBox(height -6, isValid, "Channel: ", "1 - 65535", defaultSettings["Channel"])
1365 if userInput then
1366 updateSettings("Channel", userInput)
1367 end
1368 end
1369
1370 -- edit the tankCapacity in the Configure Tanks Menu
1371 if not interacted and menuState == "Configure Tanks" and select ~= #options then
1372 interacted = true
1373
1374 -- Function to pass to the edit box to check for a valid input --
1375 local function isValid(input)
1376 input = tonumber(input)
1377 if type(input) == "number" then
1378 if input >= options[select].fluidAmount then
1379 return true, input
1380 else
1381 return "Invalid Number"
1382 end
1383 else
1384 return "Not a Number"
1385 end
1386 end
1387
1388 -- Draw the edit box and get any userInput --
1389 local defaultValue = options[select].fluidCapacity or options[select].fluidAmount
1390 local userInput = drawEditBox(height -7, isValid, "Capacity: ", optionNames[select], defaultValue)
1391 if userInput then
1392 -- update tankInfo table --
1393 tankInfo[options[select].computerID][options[select].tankName]["fluidCapacity"] = userInput
1394
1395 if not comparTables(A_F_M_S.tanksMissingFluidCapacity, getTanksMissing_fluidCapacity(tankInfo)) then
1396 updateSettings("tanksMissingFluidCapacity", getTanksMissing_fluidCapacity(tankInfo))
1397 end
1398 if getTableLength(A_F_M_S.tanksMissingFluidCapacity) == 0
1399 and menuFilters["Configure Tanks"].filter == "Not Configured" then
1400 menuFilters["Configure Tanks"].filter = "All Tanks"
1401 end
1402
1403 -- Save the new tankInfo to the computer
1404 local f = fs.open('A_F_M_S/tankInfo', 'w')
1405 f.write(textutils.serialise(tankInfo))
1406 f.close()
1407 end
1408 end
1409
1410
1411 end
1412 end
1413 end
1414end
1415
1416---Creates needed directories and files
1417local function Initialize()
1418 if not fs.exists('A_F_M_S') then
1419 fs.makeDir('A_F_M_S')
1420 end
1421 if not fs.exists('A_F_M_S/config') then
1422 A_F_M_S = defaultSettings
1423 local f = fs.open('A_F_M_S/config', 'w')
1424 f.write(textutils.serialise(defaultSettings))
1425 f.close()
1426 elseif fs.exists('A_F_M_S/config') then
1427 local f = fs.open('A_F_M_S/config', 'r')
1428 local currentSettings = textutils.unserialise(f.readAll())
1429 f.close()
1430 for key, value in pairs(defaultSettings) do
1431 if currentSettings[key] == nil then
1432 currentSettings[key] = value
1433 end
1434 end
1435 local f2 = fs.open('A_F_M_S/config', 'w')
1436 f2.write(textutils.serialise(currentSettings))
1437 f2.close()
1438 A_F_M_S = currentSettings
1439 end
1440 if not fs.exists('A_F_M_S/tankInfo') then
1441 local attachedTanks = wrapAttachedTanks()
1442 tankInfo = getTankInfo(attachedTanks)
1443 local f = fs.open('A_F_M_S/tankInfo', 'w')
1444 f.write(textutils.serialise(tankInfo))
1445 f.close()
1446 elseif fs.exists('A_F_M_S/tankInfo') then
1447 local f = fs.open('A_F_M_S/tankInfo', 'r')
1448 local currentTankInfo = textutils.unserialise(f.readAll())
1449 f.close()
1450 local f2 = fs.open('A_F_M_S/tankInfo', 'w')
1451 f2.write(textutils.serialise(currentTankInfo))
1452 f2.close()
1453 tankInfo = currentTankInfo
1454 end
1455end
1456
1457
1458--[[ **************************************************************** ||--
1459 Run the Advanced Fluid Monitoring System!
1460--|| **************************************************************** ]]--
1461
1462-- Update Directories and Files --
1463Initialize()
1464
1465-- Set the MenuState to the current A.F.M.S Role --
1466if imTesting then
1467 -- Use to jump to the menu thats being worked on --
1468 menuState = "Configure Tanks"
1469 lastMenuState = "Remote Prober"
1470else
1471 if A_F_M_S.Role == "Master Server | Remote Prober | Remote Displays" then
1472 menuState = "Change Role"
1473 else
1474 menuState = A_F_M_S.Role
1475 end
1476 lastMenuState = menuState
1477end
1478
1479-- Run the A.F.M.S Program --
1480parallel.waitForAny( runFluidProber, runMenu )
1481
1482-- Lets cleanup before we Quit to Terminal --
1483term.clear()
1484term.setCursorPos(1, 1)