Compare commits
136 Commits
v1.8.15-be
...
v1.8.19-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f725eb0eef | ||
|
|
bcc55628cf | ||
|
|
9bffd6feee | ||
|
|
1500004481 | ||
|
|
2904621e81 | ||
|
|
08eee198c8 | ||
|
|
e750ffe69d | ||
|
|
de6d8a89ca | ||
|
|
f00751edeb | ||
|
|
d58a6a3369 | ||
|
|
340c6689a9 | ||
|
|
7cc088ca95 | ||
|
|
01f6b1e190 | ||
|
|
3ffc79b181 | ||
|
|
8e4bb583a8 | ||
|
|
ec107929bc | ||
|
|
3406d12681 | ||
|
|
03bbf8a891 | ||
|
|
b61867be3c | ||
|
|
1358d95269 | ||
|
|
fd06730e46 | ||
|
|
fb5f3b9474 | ||
|
|
3afc1e6cfa | ||
|
|
715765d442 | ||
|
|
3762e9dced | ||
|
|
022d1f9f49 | ||
|
|
b1da76c2f6 | ||
|
|
0364b4df7b | ||
|
|
e04bd032fe | ||
|
|
34fe5dc382 | ||
|
|
1f8ea56095 | ||
|
|
f2cd98c57a | ||
|
|
1e341af8a5 | ||
|
|
2fb3d9b515 | ||
|
|
220f9d152f | ||
|
|
604b4a1927 | ||
|
|
0ecaa42a7f | ||
|
|
9614407c37 | ||
|
|
f1c4f8c00a | ||
|
|
8a409f0313 | ||
|
|
da68398fa4 | ||
|
|
375e969161 | ||
|
|
c93cd4d0bd | ||
|
|
e69cbc8633 | ||
|
|
ea1bcbf81c | ||
|
|
f13f03fddc | ||
|
|
72a480e475 | ||
|
|
ec1b56b853 | ||
|
|
c5fb299f55 | ||
|
|
63c990a3cf | ||
|
|
89e84f9711 | ||
|
|
270aeb13ca | ||
|
|
df8c71f12e | ||
|
|
347f67c8ee | ||
|
|
8de2d7071e | ||
|
|
d424cf74d3 | ||
|
|
a1b571d7c0 | ||
|
|
8e0e4df3eb | ||
|
|
b025958173 | ||
|
|
f83eecf2e2 | ||
|
|
0b2f7b13a1 | ||
|
|
3ad3cbb4eb | ||
|
|
e868fd3397 | ||
|
|
a4add9370c | ||
|
|
4f48ba8abc | ||
|
|
3cc6781844 | ||
|
|
c05a45c29a | ||
|
|
f2937b47e9 | ||
|
|
8e14fa1591 | ||
|
|
aebb9f42be | ||
|
|
2de30ef064 | ||
|
|
8dedb092e7 | ||
|
|
807e575580 | ||
|
|
55dc203cdd | ||
|
|
a15cbadd32 | ||
|
|
bc76c01aa5 | ||
|
|
d2bc4f6bc0 | ||
|
|
4cdbe3b07f | ||
|
|
897a3ed22d | ||
|
|
2bc20ec312 | ||
|
|
fc42049aa0 | ||
|
|
4a7028f401 | ||
|
|
006c5e6adf | ||
|
|
f64db66448 | ||
|
|
a8e6bc0e35 | ||
|
|
4a39ed9d38 | ||
|
|
9fe0669fda | ||
|
|
219f02b188 | ||
|
|
ea8f62dea6 | ||
|
|
1c719ad67b | ||
|
|
87a91e309d | ||
|
|
c66ad44adb | ||
|
|
14736e414f | ||
|
|
fb1f85a626 | ||
|
|
697a3d6f6b | ||
|
|
00cacd6d0a | ||
|
|
0b97d4d4b0 | ||
|
|
def5b49327 | ||
|
|
38457cfbbc | ||
|
|
e851a5275f | ||
|
|
5848c2ac1a | ||
|
|
f8a5dd9c32 | ||
|
|
ff8ae5e609 | ||
|
|
95b93eb795 | ||
|
|
356caf7b4d | ||
|
|
09c44a6969 | ||
|
|
83ba6e3961 | ||
|
|
e88e1afcc4 | ||
|
|
b457edbc71 | ||
|
|
5e70e4131e | ||
|
|
375b7f680e | ||
|
|
e37b8758cd | ||
|
|
9404b50a8c | ||
|
|
58fb35e85b | ||
|
|
b9030d6bed | ||
|
|
25ebf2c8c7 | ||
|
|
4d87887709 | ||
|
|
b63a17bda0 | ||
|
|
39233dae8a | ||
|
|
a2af0d3829 | ||
|
|
db901129f9 | ||
|
|
c1c49ea3fb | ||
|
|
be6c3755a4 | ||
|
|
ac2d189c1a | ||
|
|
0fa0324940 | ||
|
|
3181ab96f1 | ||
|
|
30c9215658 | ||
|
|
946c28c929 | ||
|
|
e6d6353d05 | ||
|
|
0e81391144 | ||
|
|
b18cadb53e | ||
|
|
9a500d8f96 | ||
|
|
a268a770f2 | ||
|
|
b96eb7d89d | ||
|
|
9b8947fba2 | ||
|
|
43b44cc425 |
2
.github/workflows/check.yml
vendored
2
.github/workflows/check.yml
vendored
@@ -26,4 +26,4 @@ jobs:
|
|||||||
# --no-max-line-length = Disable warnings for long line lengths
|
# --no-max-line-length = Disable warnings for long line lengths
|
||||||
# --exclude-files ... = Exclude lockbox library (external) and config files
|
# --exclude-files ... = Exclude lockbox library (external) and config files
|
||||||
# --globals ... = Override all globals overridden in .vscode/settings.json AND 'os' since CraftOS 'os' differs from Lua's 'os'
|
# --globals ... = Override all globals overridden in .vscode/settings.json AND 'os' since CraftOS 'os' differs from Lua's 'os'
|
||||||
args: . --no-max-line-length -i 121 512 542 --exclude-files ./lockbox/* ./*/config.lua --globals os _HOST bit colors fs http keys parallel periphemu peripheral read rs settings shell term textutils window
|
args: . --no-max-line-length -i 121 512 542 --exclude-files ./lockbox/* --globals os _HOST bit colors fs http keys parallel periphemu peripheral read rs settings shell term textutils window
|
||||||
|
|||||||
4
.github/workflows/manifest.yml
vendored
4
.github/workflows/manifest.yml
vendored
@@ -46,7 +46,7 @@ jobs:
|
|||||||
- name: Generate manifest and shields for main branch
|
- name: Generate manifest and shields for main branch
|
||||||
id: manifest-main
|
id: manifest-main
|
||||||
if: ${{ (success() || failure()) && steps.checkout-main.outcome == 'success' }}
|
if: ${{ (success() || failure()) && steps.checkout-main.outcome == 'success' }}
|
||||||
run: python imgen.py shields
|
run: python build/imgen.py shields
|
||||||
- name: Save main's manifest
|
- name: Save main's manifest
|
||||||
if: ${{ (success() || failure()) && steps.manifest-main.outcome == 'success' }}
|
if: ${{ (success() || failure()) && steps.manifest-main.outcome == 'success' }}
|
||||||
run: mv install_manifest.json deploy/manifests/main
|
run: mv install_manifest.json deploy/manifests/main
|
||||||
@@ -61,7 +61,7 @@ jobs:
|
|||||||
- name: Generate manifest for devel
|
- name: Generate manifest for devel
|
||||||
id: manifest-devel
|
id: manifest-devel
|
||||||
if: ${{ (success() || failure()) && steps.checkout-devel.outcome == 'success' }}
|
if: ${{ (success() || failure()) && steps.checkout-devel.outcome == 'success' }}
|
||||||
run: python imgen.py
|
run: python build/imgen.py
|
||||||
- name: Save devel's manifest
|
- name: Save devel's manifest
|
||||||
if: ${{ (success() || failure()) && steps.manifest-devel.outcome == 'success' }}
|
if: ${{ (success() || failure()) && steps.manifest-devel.outcome == 'success' }}
|
||||||
run: mv install_manifest.json deploy/manifests/devel
|
run: mv install_manifest.json deploy/manifests/devel
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,2 @@
|
|||||||
_notes/
|
_*/
|
||||||
/*program.sh
|
/*program.sh
|
||||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright © 2022 - 2024 Mikayla Fischler
|
Copyright 2022 - 2024 Mikayla Fischler
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
118
build/_offline.lua
Normal file
118
build/_offline.lua
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
---@diagnostic disable: undefined-global
|
||||||
|
-- luacheck: push ignore install_manifest ccmsi_offline app_files dep_files lgray green white
|
||||||
|
|
||||||
|
local b64_lookup = {
|
||||||
|
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4, ['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9, ['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14, ['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19, ['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24, ['Z'] = 25,
|
||||||
|
['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29, ['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34, ['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39, ['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44, ['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49, ['y'] = 50, ['z'] = 51,
|
||||||
|
['0'] = 52, ['1'] = 53, ['2'] = 54, ['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59, ['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
|
||||||
|
}
|
||||||
|
|
||||||
|
local BYTE = 0xFF
|
||||||
|
local CHAR = string.char
|
||||||
|
local BOR = bit.bor ---@type function
|
||||||
|
local BAND = bit.band ---@type function
|
||||||
|
local LSHFT = bit.blshift ---@type function
|
||||||
|
local RSHFT = bit.blogic_rshift ---@type function
|
||||||
|
|
||||||
|
-- decode a base64 string
|
||||||
|
---@param input string
|
||||||
|
local function b64_decode(input)
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
local t_start = os.epoch("local")
|
||||||
|
|
||||||
|
local decoded = {}
|
||||||
|
|
||||||
|
local c_idx, idx = 1, 1
|
||||||
|
|
||||||
|
for _ = 1, input:len() / 4 do
|
||||||
|
local block = input:sub(idx, idx + 4)
|
||||||
|
local word = 0x0
|
||||||
|
|
||||||
|
-- build the 24-bit sequence from the 4 characters
|
||||||
|
for i = 1, 4 do
|
||||||
|
local num = b64_lookup[block:sub(i, i)]
|
||||||
|
|
||||||
|
if num then
|
||||||
|
word = BOR(word, LSHFT(b64_lookup[block:sub(i, i)], (4 - i) * 6))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- decode the 24-bit sequence as 8 bytes
|
||||||
|
for i = 1, 3 do
|
||||||
|
local char = BAND(BYTE, RSHFT(word, (3 - i) * 8))
|
||||||
|
|
||||||
|
if char ~= 0 then
|
||||||
|
decoded[c_idx] = CHAR(char)
|
||||||
|
c_idx = c_idx + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
idx = idx + 4
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
local elapsed = (os.epoch("local") - t_start)
|
||||||
|
local decoded_str = table.concat(decoded)
|
||||||
|
|
||||||
|
return decoded_str, elapsed
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write files recursively from base64 encodings in a table
|
||||||
|
---@param files table
|
||||||
|
---@param path string
|
||||||
|
local function write_files(files, path)
|
||||||
|
fs.makeDir(path)
|
||||||
|
|
||||||
|
for k, v in pairs(files) do
|
||||||
|
if type(v) == "table" then
|
||||||
|
if k == "system" then
|
||||||
|
-- write system files to root
|
||||||
|
write_files(v, "/")
|
||||||
|
else
|
||||||
|
-- descend into directories
|
||||||
|
write_files(v, path .. "/" .. k .. "/")
|
||||||
|
end
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
os.sleep(0.05)
|
||||||
|
else
|
||||||
|
local handle = fs.open(path .. k, "w")
|
||||||
|
local text, time = b64_decode(v)
|
||||||
|
|
||||||
|
print("decoded '" .. k .. "' in " .. time .. "ms")
|
||||||
|
|
||||||
|
handle.write(text)
|
||||||
|
handle.close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- write installation manifiest and offline install manager
|
||||||
|
local function write_install()
|
||||||
|
local handle = fs.open("install_manifest.json", "w")
|
||||||
|
handle.write(b64_decode(install_manifest))
|
||||||
|
handle.close()
|
||||||
|
|
||||||
|
handle = fs.open("ccmsim.lua", "w")
|
||||||
|
handle.write(b64_decode(ccmsi_offline))
|
||||||
|
handle.close()
|
||||||
|
end
|
||||||
|
|
||||||
|
lgray()
|
||||||
|
|
||||||
|
-- write both app and dependency files
|
||||||
|
write_files(app_files, "/")
|
||||||
|
write_files(dep_files, "/")
|
||||||
|
|
||||||
|
-- write an install manifest and the offline installer
|
||||||
|
write_install()
|
||||||
|
|
||||||
|
green()
|
||||||
|
print("Done!")
|
||||||
|
white()
|
||||||
|
print("All files have been installed. The app can be started with 'startup' and configured with 'configure'.")
|
||||||
|
lgray()
|
||||||
|
print("Hint: You can use 'ccmsim' to manage your off-line installation.")
|
||||||
|
white()
|
||||||
|
|
||||||
|
--luacheck: pop
|
||||||
214
build/bundle.py
Normal file
214
build/bundle.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
path_prefix = "./_minified/"
|
||||||
|
|
||||||
|
# get git build info
|
||||||
|
build = subprocess.check_output(["git", "describe", "--tags"]).strip().decode('UTF-8')
|
||||||
|
|
||||||
|
# list files in a directory
|
||||||
|
def list_files(path):
|
||||||
|
list = []
|
||||||
|
|
||||||
|
for (root, dirs, files) in os.walk(path):
|
||||||
|
for f in files:
|
||||||
|
list.append((root[2:] + "/" + f).replace('\\','/'))
|
||||||
|
|
||||||
|
return list
|
||||||
|
|
||||||
|
# recursively encode files with base64
|
||||||
|
def encode_recursive(path):
|
||||||
|
list = {}
|
||||||
|
|
||||||
|
for item in os.listdir(path):
|
||||||
|
item_path = path + '/' + item
|
||||||
|
|
||||||
|
if os.path.isfile(item_path):
|
||||||
|
handle = open(item_path, 'r')
|
||||||
|
list[item] = base64.b64encode(bytes(handle.read(), 'UTF-8')).decode('ASCII')
|
||||||
|
handle.close()
|
||||||
|
else:
|
||||||
|
list[item] = encode_recursive(item_path)
|
||||||
|
|
||||||
|
return list
|
||||||
|
|
||||||
|
# encode listed files with base64
|
||||||
|
def encode_files(files):
|
||||||
|
list = {}
|
||||||
|
|
||||||
|
for item in files:
|
||||||
|
item_path = path_prefix + './' + item
|
||||||
|
|
||||||
|
handle = open(item_path, 'r')
|
||||||
|
list[item] = base64.b64encode(bytes(handle.read(), 'UTF-8')).decode('ASCII')
|
||||||
|
handle.close()
|
||||||
|
|
||||||
|
return list
|
||||||
|
|
||||||
|
# get the version of an application at the provided path
|
||||||
|
def get_version(path, is_lib = False):
|
||||||
|
ver = ""
|
||||||
|
string = ".version = \""
|
||||||
|
|
||||||
|
if not is_lib:
|
||||||
|
string = "_VERSION = \""
|
||||||
|
|
||||||
|
f = open(path, "r")
|
||||||
|
|
||||||
|
for line in f:
|
||||||
|
pos = line.find(string)
|
||||||
|
if pos >= 0:
|
||||||
|
ver = line[(pos + len(string)):(len(line) - 2)]
|
||||||
|
break
|
||||||
|
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
return ver
|
||||||
|
|
||||||
|
# file manifest (reflects imgen.py)
|
||||||
|
manifest = {
|
||||||
|
"common_versions" : {
|
||||||
|
"bootloader" : get_version("./startup.lua"),
|
||||||
|
"common" : get_version("./scada-common/util.lua", True),
|
||||||
|
"comms" : get_version("./scada-common/comms.lua", True),
|
||||||
|
"graphics" : get_version("./graphics/core.lua", True),
|
||||||
|
"lockbox" : get_version("./lockbox/init.lua", True),
|
||||||
|
},
|
||||||
|
"app_versions" : {
|
||||||
|
"reactor-plc" : get_version("./reactor-plc/startup.lua"),
|
||||||
|
"rtu" : get_version("./rtu/startup.lua"),
|
||||||
|
"supervisor" : get_version("./supervisor/startup.lua"),
|
||||||
|
"coordinator" : get_version("./coordinator/startup.lua"),
|
||||||
|
"pocket" : get_version("./pocket/startup.lua")
|
||||||
|
},
|
||||||
|
"files" : {
|
||||||
|
# common files
|
||||||
|
"system" : encode_files([ "initenv.lua", "startup.lua", "configure.lua", "LICENSE" ]),
|
||||||
|
"scada-common" : encode_recursive(path_prefix + "./scada-common"),
|
||||||
|
"graphics" : encode_recursive(path_prefix + "./graphics"),
|
||||||
|
"lockbox" : encode_recursive(path_prefix + "./lockbox"),
|
||||||
|
# platform files
|
||||||
|
"reactor-plc" : encode_recursive(path_prefix + "./reactor-plc"),
|
||||||
|
"rtu" : encode_recursive(path_prefix + "./rtu"),
|
||||||
|
"supervisor" : encode_recursive(path_prefix + "./supervisor"),
|
||||||
|
"coordinator" : encode_recursive(path_prefix + "./coordinator"),
|
||||||
|
"pocket" : encode_recursive(path_prefix + "./pocket"),
|
||||||
|
},
|
||||||
|
"install_files" : {
|
||||||
|
# common files
|
||||||
|
"system" : [ "initenv.lua", "startup.lua", "configure.lua", "LICENSE" ],
|
||||||
|
"scada-common" : list_files("./scada-common"),
|
||||||
|
"graphics" : list_files("./graphics"),
|
||||||
|
"lockbox" : list_files("./lockbox"),
|
||||||
|
# platform files
|
||||||
|
"reactor-plc" : list_files("./reactor-plc"),
|
||||||
|
"rtu" : list_files("./rtu"),
|
||||||
|
"supervisor" : list_files("./supervisor"),
|
||||||
|
"coordinator" : list_files("./coordinator"),
|
||||||
|
"pocket" : list_files("./pocket"),
|
||||||
|
},
|
||||||
|
"depends" : [ "system", "scada-common", "graphics", "lockbox" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# write the application installation items as Lua tables
|
||||||
|
def write_items(body, items, indent):
|
||||||
|
indent_str = " " * indent
|
||||||
|
for key, value in items.items():
|
||||||
|
if isinstance(value, str):
|
||||||
|
body = body + f"{indent_str}['{key}'] = \"{value}\",\n"
|
||||||
|
else:
|
||||||
|
body = body + f"{indent_str}['{key}'] = {{\n"
|
||||||
|
body = write_items(body, value, indent + 4)
|
||||||
|
body = body + f"{indent_str}}},\n"
|
||||||
|
|
||||||
|
return body
|
||||||
|
|
||||||
|
# create output directory
|
||||||
|
if not os.path.exists("./BUNDLE"):
|
||||||
|
os.makedirs("./BUNDLE")
|
||||||
|
|
||||||
|
# get offline installer
|
||||||
|
ccmsim_file = open("./build/ccmsim.lua", "r")
|
||||||
|
ccmsim_script = ccmsim_file.read()
|
||||||
|
ccmsim_file.close()
|
||||||
|
|
||||||
|
# create dependency bundled file
|
||||||
|
dep_file = "common_" + build + ".lua"
|
||||||
|
f_d = open("./BUNDLE/" + dep_file, "w")
|
||||||
|
|
||||||
|
body_b = "local dep_files = {\n"
|
||||||
|
|
||||||
|
for depend in manifest["depends"]:
|
||||||
|
body_b = body_b + write_items("", { f"{depend}": manifest["files"][depend] }, 4)
|
||||||
|
body_b = body_b + "}\n"
|
||||||
|
|
||||||
|
body_b = body_b + f"""
|
||||||
|
if select("#", ...) == 0 then
|
||||||
|
term.setTextColor(colors.red)
|
||||||
|
print("You must run the other file you should have uploaded (it has the app in its name).")
|
||||||
|
term.setTextColor(colors.white)
|
||||||
|
end
|
||||||
|
|
||||||
|
return dep_files
|
||||||
|
"""
|
||||||
|
|
||||||
|
f_d.write(body_b)
|
||||||
|
f_d.close()
|
||||||
|
|
||||||
|
# application bundled files
|
||||||
|
for app in [ "reactor-plc", "rtu", "supervisor", "coordinator", "pocket" ]:
|
||||||
|
app_file = app + "_" + build + ".lua"
|
||||||
|
|
||||||
|
f_script = open("./build/_offline.lua", "r")
|
||||||
|
script = f_script.read()
|
||||||
|
f_script.close()
|
||||||
|
|
||||||
|
f_a = open("./BUNDLE/" + app_file, "w")
|
||||||
|
|
||||||
|
body_a = "local app_files = {\n"
|
||||||
|
|
||||||
|
body_a = body_a + write_items("", { f"{app}": manifest["files"][app] }, 4) + "}\n"
|
||||||
|
|
||||||
|
versions = manifest["common_versions"].copy()
|
||||||
|
versions[app] = manifest["app_versions"][app]
|
||||||
|
|
||||||
|
depends = manifest["depends"].copy()
|
||||||
|
depends.append(app)
|
||||||
|
|
||||||
|
install_manifest = json.dumps({ "versions" : versions, "files" : manifest["install_files"], "depends" : depends })
|
||||||
|
|
||||||
|
body_a = body_a + f"""
|
||||||
|
-- install manifest JSON and offline installer
|
||||||
|
local install_manifest = "{base64.b64encode(bytes(install_manifest, 'UTF-8')).decode('ASCII')}"
|
||||||
|
local ccmsi_offline = "{base64.b64encode(bytes(ccmsim_script, 'UTF-8')).decode('ASCII')}"
|
||||||
|
|
||||||
|
local function red() term.setTextColor(colors.red) end
|
||||||
|
local function green() term.setTextColor(colors.green) end
|
||||||
|
local function white() term.setTextColor(colors.white) end
|
||||||
|
local function lgray() term.setTextColor(colors.lightGray) end
|
||||||
|
|
||||||
|
if not fs.exists("{dep_file}") then
|
||||||
|
red()
|
||||||
|
print("Missing '{dep_file}'! Please upload it, then run this file again.")
|
||||||
|
white()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- rename the dependency file
|
||||||
|
fs.move("{dep_file}", "install_depends.lua")
|
||||||
|
|
||||||
|
-- load the other file
|
||||||
|
local dep_files = require("install_depends")
|
||||||
|
|
||||||
|
-- delete the uploaded files to free up space to actually install
|
||||||
|
fs.delete("{app_file}")
|
||||||
|
fs.delete("install_depends.lua")
|
||||||
|
|
||||||
|
-- get started installing
|
||||||
|
{script}"""
|
||||||
|
|
||||||
|
f_a.write(body_a)
|
||||||
|
f_a.close()
|
||||||
237
build/ccmsim.lua
Normal file
237
build/ccmsim.lua
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
local function println(message) print(tostring(message)) end
|
||||||
|
local function print(message) term.write(tostring(message)) end
|
||||||
|
|
||||||
|
local opts = { ... }
|
||||||
|
local mode, app
|
||||||
|
|
||||||
|
local function red() term.setTextColor(colors.red) end
|
||||||
|
local function orange() term.setTextColor(colors.orange) end
|
||||||
|
local function yellow() term.setTextColor(colors.yellow) end
|
||||||
|
local function green() term.setTextColor(colors.green) end
|
||||||
|
local function blue() term.setTextColor(colors.blue) end
|
||||||
|
local function white() term.setTextColor(colors.white) end
|
||||||
|
local function lgray() term.setTextColor(colors.lightGray) end
|
||||||
|
|
||||||
|
-- get command line option in list
|
||||||
|
local function get_opt(opt, options)
|
||||||
|
for _, v in pairs(options) do if opt == v then return v end end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- wait for any key to be pressed
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
local function any_key() os.pullEvent("key_up") end
|
||||||
|
|
||||||
|
-- ask the user yes or no
|
||||||
|
local function ask_y_n(question, default)
|
||||||
|
print(question)
|
||||||
|
if default == true then print(" (Y/n)? ") else print(" (y/N)? ") end
|
||||||
|
local response = read();any_key()
|
||||||
|
if response == "" then return default
|
||||||
|
elseif response == "Y" or response == "y" then return true
|
||||||
|
elseif response == "N" or response == "n" then return false
|
||||||
|
else return nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read the local manifest file
|
||||||
|
local function read_local_manifest()
|
||||||
|
local local_ok = false
|
||||||
|
local local_manifest = {}
|
||||||
|
local imfile = fs.open("install_manifest.json", "r")
|
||||||
|
if imfile ~= nil then
|
||||||
|
local_ok, local_manifest = pcall(function () return textutils.unserializeJSON(imfile.readAll()) end)
|
||||||
|
imfile.close()
|
||||||
|
end
|
||||||
|
return local_ok, local_manifest
|
||||||
|
end
|
||||||
|
|
||||||
|
-- recursively build a tree out of the file manifest
|
||||||
|
local function gen_tree(manifest, log)
|
||||||
|
local function _tree_add(tree, split)
|
||||||
|
if #split > 1 then
|
||||||
|
local name = table.remove(split, 1)
|
||||||
|
if tree[name] == nil then tree[name] = {} end
|
||||||
|
table.insert(tree[name], _tree_add(tree[name], split))
|
||||||
|
else return split[1] end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local list, tree = { log }, {}
|
||||||
|
|
||||||
|
-- make a list of each and every file
|
||||||
|
for _, files in pairs(manifest.files) do for i = 1, #files do table.insert(list, files[i]) end end
|
||||||
|
|
||||||
|
for i = 1, #list do
|
||||||
|
local split = {}
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
|
string.gsub(list[i], "([^/]+)", function(c) split[#split + 1] = c end)
|
||||||
|
if #split == 1 then table.insert(tree, list[i])
|
||||||
|
else table.insert(tree, _tree_add(tree, split)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
return tree
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _in_array(val, array)
|
||||||
|
for _, v in pairs(array) do if v == val then return true end end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function _clean_dir(dir, tree)
|
||||||
|
if tree == nil then tree = {} end
|
||||||
|
local ls = fs.list(dir)
|
||||||
|
for _, val in pairs(ls) do
|
||||||
|
local path = dir.."/"..val
|
||||||
|
if fs.isDir(path) then
|
||||||
|
_clean_dir(path, tree[val])
|
||||||
|
if #fs.list(path) == 0 then fs.delete(path);println("deleted "..path) end
|
||||||
|
elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then
|
||||||
|
fs.delete(path)
|
||||||
|
println("deleted "..path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- go through app/common directories to delete unused files
|
||||||
|
local function clean(manifest)
|
||||||
|
local log = nil
|
||||||
|
if fs.exists(app..".settings") and settings.load(app..".settings") then
|
||||||
|
log = settings.get("LogPath")
|
||||||
|
if log:sub(1, 1) == "/" then log = log:sub(2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local tree = gen_tree(manifest, log)
|
||||||
|
|
||||||
|
table.insert(tree, "install_manifest.json")
|
||||||
|
table.insert(tree, "ccmsim.lua")
|
||||||
|
|
||||||
|
local ls = fs.list("/")
|
||||||
|
for _, val in pairs(ls) do
|
||||||
|
if fs.isDriveRoot(val) then
|
||||||
|
yellow();println("skipped mount '"..val.."'")
|
||||||
|
elseif fs.isDir(val) then
|
||||||
|
if tree[val] ~= nil then lgray();_clean_dir("/"..val, tree[val])
|
||||||
|
else white(); if ask_y_n("delete the unused directory '"..val.."'") then lgray();_clean_dir("/"..val) end end
|
||||||
|
if #fs.list(val) == 0 then fs.delete(val);lgray();println("deleted empty directory '"..val.."'") end
|
||||||
|
elseif not _in_array(val, tree) and (string.find(val, ".settings") == nil) then
|
||||||
|
white();if ask_y_n("delete the unused file '"..val.."'") then fs.delete(val);lgray();println("deleted "..val) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
white()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get and validate command line options
|
||||||
|
|
||||||
|
println("-- CC Mekanism SCADA Install Manager (Off-Line) --")
|
||||||
|
|
||||||
|
if #opts == 0 or opts[1] == "help" then
|
||||||
|
println("usage: ccmsim <mode>")
|
||||||
|
println("<mode>")
|
||||||
|
lgray()
|
||||||
|
println(" check - check your installed versions")
|
||||||
|
println(" update-rm - delete everything except the config,")
|
||||||
|
println(" so that you can upload files for a")
|
||||||
|
println(" new two-file off-line update")
|
||||||
|
println(" uninstall - delete all app files and config")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
mode = get_opt(opts[1], { "check", "update-rm", "uninstall" })
|
||||||
|
if mode == nil then
|
||||||
|
red();println("Unrecognized mode.");white()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- run selected mode
|
||||||
|
if mode == "check" then
|
||||||
|
local local_ok, manifest = read_local_manifest()
|
||||||
|
if not local_ok then
|
||||||
|
yellow();println("failed to load local installation information");white()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- list all versions
|
||||||
|
for key, value in pairs(manifest.versions) do
|
||||||
|
term.setTextColor(colors.purple)
|
||||||
|
print(string.format("%-14s", "["..key.."]"))
|
||||||
|
blue();println(value);white()
|
||||||
|
end
|
||||||
|
elseif mode == "update-rm" or mode == "uninstall" then
|
||||||
|
local ok, manifest = read_local_manifest()
|
||||||
|
if not ok then
|
||||||
|
red();println("Error parsing local installation manifest.");white()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
app = manifest.depends[#manifest.depends]
|
||||||
|
|
||||||
|
if mode == "uninstall" then
|
||||||
|
orange();println("Uninstalling all app files...")
|
||||||
|
else
|
||||||
|
orange();println("Deleting all app files except for configuration...")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ask for confirmation
|
||||||
|
if not ask_y_n("Continue", false) then return end
|
||||||
|
|
||||||
|
-- delete unused files first
|
||||||
|
clean(manifest)
|
||||||
|
|
||||||
|
local file_list = manifest.files
|
||||||
|
local dependencies = manifest.depends
|
||||||
|
|
||||||
|
-- delete all installed files
|
||||||
|
lgray()
|
||||||
|
for _, dependency in pairs(dependencies) do
|
||||||
|
local files = file_list[dependency]
|
||||||
|
for _, file in pairs(files) do
|
||||||
|
if fs.exists(file) then fs.delete(file);println("deleted "..file) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local folder = files[1]
|
||||||
|
while true do
|
||||||
|
local dir = fs.getDir(folder)
|
||||||
|
if dir == "" or dir == ".." then break else folder = dir end
|
||||||
|
end
|
||||||
|
|
||||||
|
if fs.isDir(folder) then
|
||||||
|
fs.delete(folder)
|
||||||
|
println("deleted directory "..folder)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delete log file
|
||||||
|
local log_deleted = false
|
||||||
|
local settings_file = app..".settings"
|
||||||
|
|
||||||
|
if fs.exists(settings_file) and settings.load(settings_file) then
|
||||||
|
local log = settings.get("LogPath")
|
||||||
|
if log ~= nil then
|
||||||
|
log_deleted = true
|
||||||
|
if fs.exists(log) then
|
||||||
|
fs.delete(log)
|
||||||
|
println("deleted log file "..log)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not log_deleted then
|
||||||
|
red();println("Failed to delete log file (it may not exist).");lgray()
|
||||||
|
end
|
||||||
|
|
||||||
|
if mode == "uninstall" then
|
||||||
|
if fs.exists(settings_file) then
|
||||||
|
fs.delete(settings_file);println("deleted "..settings_file)
|
||||||
|
end
|
||||||
|
|
||||||
|
fs.delete("install_manifest.json")
|
||||||
|
println("deleted install_manifest.json")
|
||||||
|
|
||||||
|
fs.delete("ccmsim.lua")
|
||||||
|
println("deleted ccmsim.lua")
|
||||||
|
end
|
||||||
|
|
||||||
|
green();println("Done!")
|
||||||
|
end
|
||||||
|
|
||||||
|
white()
|
||||||
14
build/package.sh
Executable file
14
build/package.sh
Executable file
@@ -0,0 +1,14 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Create zips to attach to GitHub releases.
|
||||||
|
# These can be extracted onto a computer and will include all files CCMSI would otherwise install.
|
||||||
|
|
||||||
|
tag=$(git describe --tags)
|
||||||
|
apps=(coordinator pocket reactor-plc rtu supervisor)
|
||||||
|
|
||||||
|
for app in "${apps[@]}" do
|
||||||
|
mkdir ${tag}_${app}
|
||||||
|
cp -R $app scada-common graphics lockbox configure.lua initenv.lua startup.lua LICENSE ${tag}_${app}
|
||||||
|
zip -r ${tag}_${app}.zip ${tag}_${app}
|
||||||
|
rm -R ${tag}_${app}
|
||||||
|
done
|
||||||
80
build/safemin.py
Normal file
80
build/safemin.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
# minify files in a directory
|
||||||
|
def min_files(path):
|
||||||
|
start_sum, end_sum = 0, 0
|
||||||
|
|
||||||
|
for (root, _, files) in os.walk(path):
|
||||||
|
os.makedirs('_minified/' + root, exist_ok=True)
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
start, end = minify(root + "/" + f)
|
||||||
|
|
||||||
|
start_sum = start_sum + start
|
||||||
|
end_sum = end_sum + end
|
||||||
|
|
||||||
|
delta = start_sum - end_sum
|
||||||
|
|
||||||
|
print(f"> done with '{path}': shrunk from {start_sum} bytes to {end_sum} bytes (saved {delta} bytes, or {(100*delta/start_sum):.2f}%)")
|
||||||
|
|
||||||
|
return list
|
||||||
|
|
||||||
|
# minify a file
|
||||||
|
def minify(path: str):
|
||||||
|
size_start = os.stat(path).st_size
|
||||||
|
|
||||||
|
f = open(path, "r")
|
||||||
|
contents = f.read()
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
if re.search(r'--+\[+', contents) != None:
|
||||||
|
# absolutely not dealing with lua multiline comments
|
||||||
|
# - there are more important things to do
|
||||||
|
# - this minification is intended to be 100% safe, so working with multiline comments is asking for trouble
|
||||||
|
# - the project doesn't use them as of writing this (except in test/), and it might as well stay that way
|
||||||
|
raise Exception(f"no multiline comments allowed! (offending file: {path})")
|
||||||
|
|
||||||
|
if re.search(r'\\$', contents, flags=re.MULTILINE) != None:
|
||||||
|
# '\' allows for multiline strings, which would require reverting to processing syntax line by line to support them
|
||||||
|
raise Exception(f"no escaping newlines! (offending file: {path})")
|
||||||
|
|
||||||
|
# drop the comments, unless the line has quotes, because quotes are scary
|
||||||
|
# (quotes are scary since we could actually be inside a string: "-- ..." shouldn't get deleted)
|
||||||
|
# -> whitespace before '--' and anything after that, which includes '---' comments
|
||||||
|
minified = re.sub(r'\s*--+(?!.*[\'"]).*', '', contents)
|
||||||
|
|
||||||
|
# drop leading whitespace on each line
|
||||||
|
minified = re.sub(r'^ +', '', minified, flags=re.MULTILINE)
|
||||||
|
|
||||||
|
# drop blank lines
|
||||||
|
while minified != re.sub(r'\n\n', '\n', minified):
|
||||||
|
minified = re.sub(r'\n\n', '\n', minified)
|
||||||
|
|
||||||
|
# write the minified file
|
||||||
|
f_min = open(f"_minified/{path}", "w")
|
||||||
|
f_min.write(minified)
|
||||||
|
f_min.close()
|
||||||
|
|
||||||
|
size_end = os.stat(f"_minified/{path}").st_size
|
||||||
|
|
||||||
|
print(f">> shrunk '{path}' from {size_start} bytes to {size_end} bytes (saved {size_start-size_end} bytes)")
|
||||||
|
|
||||||
|
return size_start, size_end
|
||||||
|
|
||||||
|
# minify applications and libraries
|
||||||
|
dirs = [ 'scada-common', 'graphics', 'lockbox', 'reactor-plc', 'rtu', 'supervisor', 'coordinator', 'pocket' ]
|
||||||
|
for _, d in enumerate(dirs):
|
||||||
|
min_files(d)
|
||||||
|
|
||||||
|
# minify root files
|
||||||
|
minify("startup.lua")
|
||||||
|
minify("initenv.lua")
|
||||||
|
minify("configure.lua")
|
||||||
|
|
||||||
|
# copy in license for build usage
|
||||||
|
lic1 = open("LICENSE", "r")
|
||||||
|
lic2 = open("_minified/LICENSE", "w")
|
||||||
|
lic2.write(lic1.read())
|
||||||
|
lic1.close()
|
||||||
|
lic2.close()
|
||||||
147
ccmsi.lua
147
ccmsi.lua
@@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
local function println(message) print(tostring(message)) end
|
local function println(message) print(tostring(message)) end
|
||||||
local function print(message) term.write(tostring(message)) end
|
local function print(message) term.write(tostring(message)) end
|
||||||
|
|
||||||
local CCMSI_VERSION = "v1.14"
|
local CCMSI_VERSION = "v1.16"
|
||||||
|
|
||||||
local install_dir = "/.install-cache"
|
local install_dir = "/.install-cache"
|
||||||
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
||||||
@@ -120,8 +120,29 @@ local function write_install_manifest(manifest, dependencies)
|
|||||||
imfile.close()
|
imfile.close()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- try at most 3 times to download a file from the repository and write into w_path base directory
|
||||||
|
local function http_get_file(file, w_path)
|
||||||
|
local dl, err
|
||||||
|
for i = 1, 3 do
|
||||||
|
dl, err = http.get(repo_path..file)
|
||||||
|
if dl then
|
||||||
|
if i > 1 then green();println("success!");lgray() end
|
||||||
|
local f = fs.open(w_path..file, "w")
|
||||||
|
f.write(dl.readAll())
|
||||||
|
f.close()
|
||||||
|
break
|
||||||
|
else
|
||||||
|
red();println("HTTP Error: "..err)
|
||||||
|
if i < 3 then lgray();print("> retrying...") end
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
os.sleep(i/3.0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return dl ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
-- recursively build a tree out of the file manifest
|
-- recursively build a tree out of the file manifest
|
||||||
local function gen_tree(manifest)
|
local function gen_tree(manifest, log)
|
||||||
local function _tree_add(tree, split)
|
local function _tree_add(tree, split)
|
||||||
if #split > 1 then
|
if #split > 1 then
|
||||||
local name = table.remove(split, 1)
|
local name = table.remove(split, 1)
|
||||||
@@ -131,13 +152,14 @@ local function gen_tree(manifest)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local list, tree = {}, {}
|
local list, tree = { log }, {}
|
||||||
|
|
||||||
-- make a list of each and every file
|
-- make a list of each and every file
|
||||||
for _, files in pairs(manifest.files) do for i = 1, #files do table.insert(list, files[i]) end end
|
for _, files in pairs(manifest.files) do for i = 1, #files do table.insert(list, files[i]) end end
|
||||||
|
|
||||||
for i = 1, #list do
|
for i = 1, #list do
|
||||||
local split = {}
|
local split = {}
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
string.gsub(list[i], "([^/]+)", function(c) split[#split + 1] = c end)
|
string.gsub(list[i], "([^/]+)", function(c) split[#split + 1] = c end)
|
||||||
if #split == 1 then table.insert(tree, list[i])
|
if #split == 1 then table.insert(tree, list[i])
|
||||||
else table.insert(tree, _tree_add(tree, split)) end
|
else table.insert(tree, _tree_add(tree, split)) end
|
||||||
@@ -159,7 +181,7 @@ local function _clean_dir(dir, tree)
|
|||||||
if fs.isDir(path) then
|
if fs.isDir(path) then
|
||||||
_clean_dir(path, tree[val])
|
_clean_dir(path, tree[val])
|
||||||
if #fs.list(path) == 0 then fs.delete(path);println("deleted "..path) end
|
if #fs.list(path) == 0 then fs.delete(path);println("deleted "..path) end
|
||||||
elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then
|
elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then ---@todo remove config.lua on full release
|
||||||
fs.delete(path)
|
fs.delete(path)
|
||||||
println("deleted "..path)
|
println("deleted "..path)
|
||||||
end
|
end
|
||||||
@@ -168,11 +190,16 @@ end
|
|||||||
|
|
||||||
-- go through app/common directories to delete unused files
|
-- go through app/common directories to delete unused files
|
||||||
local function clean(manifest)
|
local function clean(manifest)
|
||||||
local tree = gen_tree(manifest)
|
local log = nil
|
||||||
|
if fs.exists(app..".settings") and settings.load(app..".settings") then
|
||||||
|
log = settings.get("LogPath")
|
||||||
|
if log:sub(1, 1) == "/" then log = log:sub(2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local tree = gen_tree(manifest, log)
|
||||||
|
|
||||||
table.insert(tree, "install_manifest.json")
|
table.insert(tree, "install_manifest.json")
|
||||||
table.insert(tree, "ccmsi.lua")
|
table.insert(tree, "ccmsi.lua")
|
||||||
table.insert(tree, "log.txt") ---@fixme fix after migration to settings files?
|
|
||||||
|
|
||||||
local ls = fs.list("/")
|
local ls = fs.list("/")
|
||||||
for _, val in pairs(ls) do
|
for _, val in pairs(ls) do
|
||||||
@@ -244,7 +271,6 @@ else
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- run selected mode
|
-- run selected mode
|
||||||
|
|
||||||
if mode == "check" then
|
if mode == "check" then
|
||||||
local ok, manifest = get_remote_manifest()
|
local ok, manifest = get_remote_manifest()
|
||||||
if not ok then return end
|
if not ok then return end
|
||||||
@@ -318,7 +344,7 @@ elseif mode == "install" or mode == "update" then
|
|||||||
local dl, err = http.get(repo_path.."ccmsi.lua")
|
local dl, err = http.get(repo_path.."ccmsi.lua")
|
||||||
|
|
||||||
if dl == nil then
|
if dl == nil then
|
||||||
red();println("HTTP Error "..err)
|
red();println("HTTP Error: "..err)
|
||||||
println("Installer download failed.");white()
|
println("Installer download failed.");white()
|
||||||
else
|
else
|
||||||
local handle = fs.open(debug.getinfo(1, "S").source:sub(2), "w") -- this file, regardless of name or location
|
local handle = fs.open(debug.getinfo(1, "S").source:sub(2), "w") -- this file, regardless of name or location
|
||||||
@@ -355,9 +381,6 @@ elseif mode == "install" or mode == "update" then
|
|||||||
ver.graphics.changed = show_pkg_change("graphics", ver.graphics)
|
ver.graphics.changed = show_pkg_change("graphics", ver.graphics)
|
||||||
ver.lockbox.changed = show_pkg_change("lockbox", ver.lockbox)
|
ver.lockbox.changed = show_pkg_change("lockbox", ver.lockbox)
|
||||||
|
|
||||||
-- ask for confirmation
|
|
||||||
if not ask_y_n("Continue", false) then return end
|
|
||||||
|
|
||||||
--------------------------
|
--------------------------
|
||||||
-- START INSTALL/UPDATE --
|
-- START INSTALL/UPDATE --
|
||||||
--------------------------
|
--------------------------
|
||||||
@@ -372,11 +395,32 @@ elseif mode == "install" or mode == "update" then
|
|||||||
|
|
||||||
table.insert(dependencies, app)
|
table.insert(dependencies, app)
|
||||||
|
|
||||||
|
-- helper function to check if a dependency is unchanged
|
||||||
|
local function unchanged(dependency)
|
||||||
|
if dependency == "system" then return not ver.boot.changed
|
||||||
|
elseif dependency == "graphics" then return not ver.graphics.changed
|
||||||
|
elseif dependency == "lockbox" then return not ver.lockbox.changed
|
||||||
|
elseif dependency == "common" then return not (ver.common.changed or ver.comms.changed)
|
||||||
|
elseif dependency == app then return not ver.app.changed
|
||||||
|
else return true end
|
||||||
|
end
|
||||||
|
|
||||||
|
local any_change = false
|
||||||
|
|
||||||
for _, dependency in pairs(dependencies) do
|
for _, dependency in pairs(dependencies) do
|
||||||
local size = size_list[dependency]
|
local size = size_list[dependency]
|
||||||
space_required = space_required + size
|
space_required = space_required + size
|
||||||
|
any_change = any_change or not unchanged(dependency)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if mode == "update" and not any_change then
|
||||||
|
yellow();println("Nothing to do, everything is already up-to-date!");white()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ask for confirmation
|
||||||
|
if not ask_y_n("Continue", false) then return end
|
||||||
|
|
||||||
-- check space constraints
|
-- check space constraints
|
||||||
if space_available < space_required then
|
if space_available < space_required then
|
||||||
single_file_mode = true
|
single_file_mode = true
|
||||||
@@ -392,16 +436,6 @@ elseif mode == "install" or mode == "update" then
|
|||||||
|
|
||||||
local success = true
|
local success = true
|
||||||
|
|
||||||
-- helper function to check if a dependency is unchanged
|
|
||||||
local function unchanged(dependency)
|
|
||||||
if dependency == "system" then return not ver.boot.changed
|
|
||||||
elseif dependency == "graphics" then return not ver.graphics.changed
|
|
||||||
elseif dependency == "lockbox" then return not ver.lockbox.changed
|
|
||||||
elseif dependency == "common" then return not (ver.common.changed or ver.comms.changed)
|
|
||||||
elseif dependency == app then return not ver.app.changed
|
|
||||||
else return true end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not single_file_mode then
|
if not single_file_mode then
|
||||||
if fs.exists(install_dir) then fs.delete(install_dir);fs.makeDir(install_dir) end
|
if fs.exists(install_dir) then fs.delete(install_dir);fs.makeDir(install_dir) end
|
||||||
|
|
||||||
@@ -416,19 +450,14 @@ elseif mode == "install" or mode == "update" then
|
|||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
println("GET "..file)
|
println("GET "..file)
|
||||||
local dl, err = http.get(repo_path..file)
|
if not http_get_file(file, install_dir.."/") then
|
||||||
|
red();println("failed to download "..file)
|
||||||
if dl == nil then
|
|
||||||
red();println("HTTP Error "..err)
|
|
||||||
success = false
|
success = false
|
||||||
break
|
break
|
||||||
else
|
|
||||||
local handle = fs.open(install_dir.."/"..file, "w")
|
|
||||||
handle.write(dl.readAll())
|
|
||||||
handle.close()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not success then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy in downloaded files (installation)
|
-- copy in downloaded files (installation)
|
||||||
@@ -478,19 +507,14 @@ elseif mode == "install" or mode == "update" then
|
|||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
println("GET "..file)
|
println("GET "..file)
|
||||||
local dl, err = http.get(repo_path..file)
|
if not http_get_file(file, "/") then
|
||||||
|
red();println("failed to download "..file)
|
||||||
if dl == nil then
|
|
||||||
red();println("HTTP Error "..err)
|
|
||||||
success = false
|
success = false
|
||||||
break
|
break
|
||||||
else
|
|
||||||
local handle = fs.open("/"..file, "w")
|
|
||||||
handle.write(dl.readAll())
|
|
||||||
handle.close()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not success then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
if success then
|
if success then
|
||||||
@@ -534,36 +558,8 @@ elseif mode == "uninstall" then
|
|||||||
|
|
||||||
table.insert(dependencies, app)
|
table.insert(dependencies, app)
|
||||||
|
|
||||||
-- delete log file
|
|
||||||
local log_deleted = false
|
|
||||||
local settings_file = app..".settings"
|
|
||||||
local legacy_config_file = app.."/config.lua"
|
|
||||||
|
|
||||||
lgray()
|
|
||||||
if fs.exists(legacy_config_file) then
|
|
||||||
log_deleted = pcall(function ()
|
|
||||||
local config = require(app..".config")
|
|
||||||
if fs.exists(config.LOG_PATH) then
|
|
||||||
fs.delete(config.LOG_PATH)
|
|
||||||
println("deleted log file "..config.LOG_PATH)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
elseif fs.exists(settings_file) and settings.load(settings_file) then
|
|
||||||
local log = settings.get("LogPath")
|
|
||||||
if log ~= nil and fs.exists(log) then
|
|
||||||
log_deleted = true
|
|
||||||
fs.delete(log)
|
|
||||||
println("deleted log file "..log)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not log_deleted then
|
|
||||||
red();println("Failed to delete log file.")
|
|
||||||
white();println("press any key to continue...")
|
|
||||||
any_key();lgray()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- delete all installed files
|
-- delete all installed files
|
||||||
|
lgray()
|
||||||
for _, dependency in pairs(dependencies) do
|
for _, dependency in pairs(dependencies) do
|
||||||
local files = file_list[dependency]
|
local files = file_list[dependency]
|
||||||
for _, file in pairs(files) do
|
for _, file in pairs(files) do
|
||||||
@@ -582,8 +578,23 @@ elseif mode == "uninstall" then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists(legacy_config_file) then
|
-- delete log file
|
||||||
fs.delete(legacy_config_file);println("deleted "..legacy_config_file)
|
local log_deleted = false
|
||||||
|
local settings_file = app..".settings"
|
||||||
|
|
||||||
|
if fs.exists(settings_file) and settings.load(settings_file) then
|
||||||
|
local log = settings.get("LogPath")
|
||||||
|
if log ~= nil then
|
||||||
|
log_deleted = true
|
||||||
|
if fs.exists(log) then
|
||||||
|
fs.delete(log)
|
||||||
|
println("deleted log file "..log)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not log_deleted then
|
||||||
|
red();println("Failed to delete log file (it may not exist).");lgray()
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists(settings_file) then
|
if fs.exists(settings_file) then
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ local log = require("scada-common.log")
|
|||||||
local network = require("scada-common.network")
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
local themes = require("graphics.themes")
|
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
@@ -45,7 +46,8 @@ local RIGHT = core.ALIGN.RIGHT
|
|||||||
local changes = {
|
local changes = {
|
||||||
{ "v1.2.4", { "Added temperature scale options" } },
|
{ "v1.2.4", { "Added temperature scale options" } },
|
||||||
{ "v1.2.12", { "Added main UI theme", "Added front panel UI theme", "Added color accessibility modes" } },
|
{ "v1.2.12", { "Added main UI theme", "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
{ "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
{ "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } },
|
||||||
|
{ "v1.5.1", { "Added energy scale options" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class crd_configurator
|
---@class crd_configurator
|
||||||
@@ -69,7 +71,7 @@ local tool_ctl = {
|
|||||||
nic = nil, ---@type nic
|
nic = nil, ---@type nic
|
||||||
net_listen = false,
|
net_listen = false,
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
sv_seq_num = 0,
|
sv_seq_num = util.time_ms() * 10,
|
||||||
sv_cool_conf = nil, ---@type table list of boiler & turbine counts
|
sv_cool_conf = nil, ---@type table list of boiler & turbine counts
|
||||||
show_sv_cfg = nil, ---@type function
|
show_sv_cfg = nil, ---@type function
|
||||||
|
|
||||||
@@ -118,6 +120,7 @@ local tmp_cfg = {
|
|||||||
SpeakerVolume = 1.0,
|
SpeakerVolume = 1.0,
|
||||||
Time24Hour = true,
|
Time24Hour = true,
|
||||||
TempScale = 1,
|
TempScale = 1,
|
||||||
|
EnergyScale = 1,
|
||||||
DisableFlowView = false,
|
DisableFlowView = false,
|
||||||
MainDisplay = nil, ---@type string
|
MainDisplay = nil, ---@type string
|
||||||
FlowDisplay = nil, ---@type string
|
FlowDisplay = nil, ---@type string
|
||||||
@@ -150,7 +153,8 @@ local fields = {
|
|||||||
{ "UnitDisplays", "Unit Monitors", {} },
|
{ "UnitDisplays", "Unit Monitors", {} },
|
||||||
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
||||||
{ "Time24Hour", "Use 24-hour Time Format", true },
|
{ "Time24Hour", "Use 24-hour Time Format", true },
|
||||||
{ "TempScale", "Temperature Scale", 1 },
|
{ "TempScale", "Temperature Scale", types.TEMP_SCALE.KELVIN },
|
||||||
|
{ "EnergyScale", "Energy Scale", types.ENERGY_SCALE.FE },
|
||||||
{ "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false },
|
{ "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "CRD_Channel", "CRD Channel", 16243 },
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
||||||
@@ -304,7 +308,7 @@ local function config_view(display)
|
|||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local function exit() os.queueEvent("terminate") end
|
local function exit() os.queueEvent("terminate") end
|
||||||
|
|
||||||
TextBox{parent=display,y=1,text="Coordinator Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=display,y=1,text="Coordinator Configurator",alignment=CENTER,fg_bg=style.header}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=display,x=1,y=2}
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
||||||
|
|
||||||
@@ -332,7 +336,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text=msg,fg_bg=cpair(colors.red,colors.lightGray)}
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text=msg,fg_bg=cpair(colors.red,colors.lightGray)}
|
||||||
y_start = y_start + 5
|
y_start = y_start + 5
|
||||||
elseif tool_ctl.start_fail > 0 then
|
elseif tool_ctl.start_fail > 0 then
|
||||||
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
||||||
y_start = y_start + 5
|
y_start = y_start + 5
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -376,24 +380,24 @@ local function config_view(display)
|
|||||||
|
|
||||||
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
||||||
|
|
||||||
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
TextBox{parent=net_c_1,x=1,y=1,text="Please set the network channels below."}
|
||||||
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 3 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 3 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"}
|
TextBox{parent=net_c_1,x=1,y=8,width=18,text="Supervisor Channel"}
|
||||||
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="Coordinator Channel"}
|
TextBox{parent=net_c_1,x=1,y=10,width=19,text="Coordinator Channel"}
|
||||||
local crd_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local crd_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"}
|
TextBox{parent=net_c_1,x=1,y=12,width=14,text="Pocket Channel"}
|
||||||
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_channels()
|
local function submit_channels()
|
||||||
local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
||||||
@@ -407,18 +411,18 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Please set the connection timeouts below."}
|
TextBox{parent=net_c_2,x=1,y=1,text="Please set the connection timeouts below."}
|
||||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=8,height=1,width=19,text="Supervisor Timeout"}
|
TextBox{parent=net_c_2,x=1,y=8,width=19,text="Supervisor Timeout"}
|
||||||
local svr_timeout = NumberField{parent=net_c_2,x=20,y=8,width=7,default=ini_cfg.SVR_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local svr_timeout = NumberField{parent=net_c_2,x=20,y=8,width=7,default=ini_cfg.SVR_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=10,height=1,width=14,text="Pocket Timeout"}
|
TextBox{parent=net_c_2,x=1,y=10,width=14,text="Pocket Timeout"}
|
||||||
local api_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.API_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local api_timeout = NumberField{parent=net_c_2,x=20,y=10,width=7,default=ini_cfg.API_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=28,y=8,height=4,width=7,text="seconds\n\nseconds",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local ct_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_timeouts()
|
local function submit_timeouts()
|
||||||
local svr_cto, api_cto = tonumber(svr_timeout.get_value()), tonumber(api_timeout.get_value())
|
local svr_cto, api_cto = tonumber(svr_timeout.get_value()), tonumber(api_timeout.get_value())
|
||||||
@@ -432,13 +436,13 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=1,height=1,text="Please set the trusted range below."}
|
TextBox{parent=net_c_3,x=1,y=1,text="Please set the trusted range below."}
|
||||||
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local tr_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local tr_err = TextBox{parent=net_c_3,x=8,y=14,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_tr()
|
local function submit_tr()
|
||||||
local range_val = tonumber(range.get_value())
|
local range_val = tonumber(range.get_value())
|
||||||
@@ -456,7 +460,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
||||||
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_4,x=1,y=11,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_4,x=1,y=11,text="Facility Auth Key"}
|
||||||
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||||
@@ -466,7 +470,7 @@ local function config_view(display)
|
|||||||
hide_key.set_value(true)
|
hide_key.set_value(true)
|
||||||
censor_key(true)
|
censor_key(true)
|
||||||
|
|
||||||
local key_err = TextBox{parent=net_c_4,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local key_err = TextBox{parent=net_c_4,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_auth()
|
local function submit_auth()
|
||||||
local v = key.get_value()
|
local v = key.get_value()
|
||||||
@@ -504,12 +508,12 @@ local function config_view(display)
|
|||||||
|
|
||||||
local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3}}
|
local fac_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3}}
|
||||||
|
|
||||||
TextBox{parent=fac_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=fac_cfg,x=1,y=2,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
TextBox{parent=fac_c_1,x=1,y=1,height=4,text="This tool can attempt to connect to your supervisor computer. This would load facility information in order to get the unit count and aid monitor setup."}
|
TextBox{parent=fac_c_1,x=1,y=1,height=4,text="This tool can attempt to connect to your supervisor computer. This would load facility information in order to get the unit count and aid monitor setup."}
|
||||||
TextBox{parent=fac_c_1,x=1,y=6,height=2,text="The supervisor startup app must be running and fully configured on your supervisor computer."}
|
TextBox{parent=fac_c_1,x=1,y=6,height=2,text="The supervisor startup app must be running and fully configured on your supervisor computer."}
|
||||||
|
|
||||||
tool_ctl.sv_conn_status = TextBox{parent=fac_c_1,x=11,y=9,height=1,text=""}
|
tool_ctl.sv_conn_status = TextBox{parent=fac_c_1,x=11,y=9,text=""}
|
||||||
tool_ctl.sv_conn_detail = TextBox{parent=fac_c_1,x=1,y=11,height=2,text=""}
|
tool_ctl.sv_conn_detail = TextBox{parent=fac_c_1,x=1,y=11,height=2,text=""}
|
||||||
|
|
||||||
tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
tool_ctl.sv_conn_button = PushButton{parent=fac_c_1,x=1,y=9,text="Connect",min_width=9,callback=function()tool_ctl.sv_connect()end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
||||||
@@ -533,11 +537,11 @@ local function config_view(display)
|
|||||||
|
|
||||||
TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
TextBox{parent=fac_c_2,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
||||||
local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
local num_units = NumberField{parent=fac_c_2,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=fac_c_2,x=7,y=5,height=1,text="reactors"}
|
TextBox{parent=fac_c_2,x=7,y=5,text="reactors"}
|
||||||
TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=fac_c_2,x=1,y=7,height=3,text="This will decide how many monitors you need. If this does not match the supervisor's number of reactor units, the coordinator will not connect.",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=fac_c_2,x=1,y=10,height=3,text="Since you skipped supervisor sync, the main monitor minimum height can't be determined precisely. It is marked with * on the next page.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=fac_c_2,x=1,y=10,height=3,text="Since you skipped supervisor sync, the main monitor minimum height can't be determined precisely. It is marked with * on the next page.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local nu_error = TextBox{parent=fac_c_2,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local nu_error = TextBox{parent=fac_c_2,x=8,y=14,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_num_units()
|
local function submit_num_units()
|
||||||
local count = tonumber(num_units.get_value())
|
local count = tonumber(num_units.get_value())
|
||||||
@@ -570,7 +574,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3,mon_c_4}}
|
local mon_pane = MultiPane{parent=mon_cfg,x=1,y=4,panes={mon_c_1,mon_c_2,mon_c_3,mon_c_4}}
|
||||||
|
|
||||||
TextBox{parent=mon_cfg,x=1,y=2,height=1,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)}
|
TextBox{parent=mon_cfg,x=1,y=2,text=" Monitor Configuration",fg_bg=cpair(colors.black,colors.blue)}
|
||||||
|
|
||||||
TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."}
|
TextBox{parent=mon_c_1,x=1,y=1,height=5,text="Your configuration requires the following monitors. The main and flow monitors' heights are dependent on your unit count and cooling setup. If you manually entered the unit count, a * will be shown on potentially inaccurate calculations."}
|
||||||
local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local mon_reqs = ListBox{parent=mon_c_1,x=1,y=7,height=6,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
@@ -591,7 +595,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local mon_list = ListBox{parent=mon_c_2,x=1,y=6,height=7,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
local assign_err = TextBox{parent=mon_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local assign_err = TextBox{parent=mon_c_2,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_monitors()
|
local function submit_monitors()
|
||||||
if tmp_cfg.MainDisplay == nil then
|
if tmp_cfg.MainDisplay == nil then
|
||||||
@@ -651,13 +655,13 @@ local function config_view(display)
|
|||||||
if value == "0" or value == nil then mon_unit.set_value(0) end
|
if value == "0" or value == nil then mon_unit.set_value(0) end
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=mon_c_3,x=1,y=6,width=10,height=1,text="Assignment"}
|
TextBox{parent=mon_c_3,x=1,y=6,width=10,text="Assignment"}
|
||||||
local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.blue}
|
local mon_assign = RadioButton{parent=mon_c_3,x=1,y=7,default=1,options={"Main Monitor","Flow Monitor","Unit Monitor"},callback=on_assign_mon,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.blue}
|
||||||
|
|
||||||
mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,height=1,text="Unit ID"}
|
mon_unit_l = TextBox{parent=mon_c_3,x=18,y=6,width=7,text="Unit ID"}
|
||||||
mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
mon_unit = NumberField{parent=mon_c_3,x=18,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local mon_u_err = TextBox{parent=mon_c_3,x=8,y=14,height=1,width=35,text="Please provide a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local mon_u_err = TextBox{parent=mon_c_3,x=8,y=14,width=35,text="Please provide a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
-- purge all assignments for a given monitor
|
-- purge all assignments for a given monitor
|
||||||
---@param iface string
|
---@param iface string
|
||||||
@@ -719,7 +723,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49}
|
local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=spkr_cfg,x=1,y=2,height=1,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)}
|
TextBox{parent=spkr_cfg,x=1,y=2,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)}
|
||||||
|
|
||||||
TextBox{parent=spkr_c,x=1,y=1,height=2,text="The coordinator uses a speaker to play alarm sounds."}
|
TextBox{parent=spkr_c,x=1,y=1,height=2,text="The coordinator uses a speaker to play alarm sounds."}
|
||||||
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
||||||
@@ -728,7 +732,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,height=1,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_vol()
|
local function submit_vol()
|
||||||
local vol = tonumber(s_vol.get_value())
|
local vol = tonumber(s_vol.get_value())
|
||||||
@@ -748,19 +752,23 @@ local function config_view(display)
|
|||||||
|
|
||||||
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
TextBox{parent=crd_cfg,x=1,y=2,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
||||||
|
|
||||||
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."}
|
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."}
|
||||||
|
|
||||||
TextBox{parent=crd_c_1,x=1,y=4,height=1,text="Clock Time Format"}
|
TextBox{parent=crd_c_1,x=1,y=4,text="Clock Time Format"}
|
||||||
local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
TextBox{parent=crd_c_1,x=1,y=8,height=1,text="Temperature Scale"}
|
TextBox{parent=crd_c_1,x=1,y=8,text="Temperature Scale"}
|
||||||
local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options={"Kelvin","Celsius","Fahrenheit","Rankine"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
|
TextBox{parent=crd_c_1,x=24,y=8,text="Energy Scale"}
|
||||||
|
local energy_scale = RadioButton{parent=crd_c_1,x=24,y=9,default=ini_cfg.EnergyScale,options=types.ENERGY_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
local function submit_ui_opts()
|
local function submit_ui_opts()
|
||||||
tmp_cfg.Time24Hour = clock_fmt.get_value() == 1
|
tmp_cfg.Time24Hour = clock_fmt.get_value() == 1
|
||||||
tmp_cfg.TempScale = temp_scale.get_value()
|
tmp_cfg.TempScale = temp_scale.get_value()
|
||||||
|
tmp_cfg.EnergyScale = energy_scale.get_value()
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(7)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -773,20 +781,20 @@ local function config_view(display)
|
|||||||
|
|
||||||
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
TextBox{parent=log_c_1,x=1,y=3,text="Log File Mode"}
|
||||||
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
TextBox{parent=log_c_1,x=1,y=7,text="Log File Path"}
|
||||||
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
||||||
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_log()
|
local function submit_log()
|
||||||
if path.get_value() ~= "" then
|
if path.get_value() ~= "" then
|
||||||
@@ -814,20 +822,20 @@ local function config_view(display)
|
|||||||
|
|
||||||
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color themes for the different UI displays."}
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color themes for the different UI displays."}
|
||||||
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Main UI Theme"}
|
TextBox{parent=clr_c_1,x=1,y=7,text="Main UI Theme"}
|
||||||
local main_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.MainTheme,options=themes.UI_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local main_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.MainTheme,options=themes.UI_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=18,y=7,height=1,text="Front Panel Theme"}
|
TextBox{parent=clr_c_1,x=18,y=7,text="Front Panel Theme"}
|
||||||
local fp_theme = RadioButton{parent=clr_c_1,x=18,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local fp_theme = RadioButton{parent=clr_c_1,x=18,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
TextBox{parent=clr_c_2,x=21,y=7,text="Preview"}
|
||||||
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
@@ -854,7 +862,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"}
|
||||||
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
@@ -910,7 +918,7 @@ local function config_view(display)
|
|||||||
clr_pane.set_value(1)
|
clr_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"}
|
||||||
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -929,7 +937,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
||||||
|
|
||||||
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
TextBox{parent=summary,x=1,y=2,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
||||||
|
|
||||||
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -968,6 +976,8 @@ local function config_view(display)
|
|||||||
try_set(dis_flow_view, ini_cfg.DisableFlowView)
|
try_set(dis_flow_view, ini_cfg.DisableFlowView)
|
||||||
try_set(s_vol, ini_cfg.SpeakerVolume)
|
try_set(s_vol, ini_cfg.SpeakerVolume)
|
||||||
try_set(clock_fmt, util.trinary(ini_cfg.Time24Hour, 1, 2))
|
try_set(clock_fmt, util.trinary(ini_cfg.Time24Hour, 1, 2))
|
||||||
|
try_set(temp_scale, ini_cfg.TempScale)
|
||||||
|
try_set(energy_scale, ini_cfg.EnergyScale)
|
||||||
try_set(mode, ini_cfg.LogMode)
|
try_set(mode, ini_cfg.LogMode)
|
||||||
try_set(path, ini_cfg.LogPath)
|
try_set(path, ini_cfg.LogPath)
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
@@ -997,7 +1007,7 @@ local function config_view(display)
|
|||||||
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
||||||
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=sum_c_2,x=1,y=1,text="Settings saved!"}
|
||||||
|
|
||||||
local function go_home()
|
local function go_home()
|
||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
@@ -1032,15 +1042,15 @@ local function config_view(display)
|
|||||||
|
|
||||||
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
TextBox{parent=changelog,x=1,y=2,text=" Config Change Log",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=c_log,text=change[1],fg_bg=bw_fg_bg}
|
||||||
for _, v in ipairs(change[2]) do
|
for _, v in ipairs(change[2]) do
|
||||||
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
||||||
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=1,text="- ",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1121,7 +1131,6 @@ local function config_view(display)
|
|||||||
tool_ctl.nic.open(tmp_cfg.CRD_Channel)
|
tool_ctl.nic.open(tmp_cfg.CRD_Channel)
|
||||||
|
|
||||||
tool_ctl.sv_addr = comms.BROADCAST
|
tool_ctl.sv_addr = comms.BROADCAST
|
||||||
tool_ctl.sv_seq_num = 0
|
|
||||||
tool_ctl.net_listen = true
|
tool_ctl.net_listen = true
|
||||||
|
|
||||||
send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD })
|
send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.CRD })
|
||||||
@@ -1136,12 +1145,12 @@ local function config_view(display)
|
|||||||
fac_config_list.remove_all()
|
fac_config_list.remove_all()
|
||||||
|
|
||||||
local str = util.sprintf("Facility has %d reactor unit%s:", #conf, util.trinary(#conf==1,"","s"))
|
local str = util.sprintf("Facility has %d reactor unit%s:", #conf, util.trinary(#conf==1,"","s"))
|
||||||
TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=fac_config_list,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
for i = 1, #conf do
|
for i = 1, #conf do
|
||||||
local num_b, num_t = conf[i][1], conf[i][2]
|
local num_b, num_t = conf[i][1], conf[i][2]
|
||||||
str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s"))
|
str = util.sprintf("\x07 Unit %d has %d boiler%s and %d turbine%s", i, num_b, util.trinary(num_b == 1, "", "s"), num_t, util.trinary(num_t == 1, "", "s"))
|
||||||
TextBox{parent=fac_config_list,height=1,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=fac_config_list,text=str,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1177,13 +1186,13 @@ local function config_view(display)
|
|||||||
|
|
||||||
mon_reqs.remove_all()
|
mon_reqs.remove_all()
|
||||||
|
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")}
|
TextBox{parent=mon_reqs,x=1,y=1,text="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")}
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=mon_reqs,x=1,y=1,text=" "..util.trinary(plural,"each ","").."must be 4 blocks wide by 4 tall",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Main View Monitor"}
|
TextBox{parent=mon_reqs,x=1,y=1,text="\x1a 1 Main View Monitor"}
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=mon_reqs,x=1,y=1,text=" must be 8 blocks wide by "..m_at_least..tool_ctl.main_mon_h..asterisk.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
if not tmp_cfg.DisableFlowView then
|
if not tmp_cfg.DisableFlowView then
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text="\x1a 1 Flow View Monitor"}
|
TextBox{parent=mon_reqs,x=1,y=1,text="\x1a 1 Flow View Monitor"}
|
||||||
TextBox{parent=mon_reqs,x=1,y=1,height=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=mon_reqs,x=1,y=1,text=" must be 8 blocks wide by "..f_at_least..tool_ctl.flow_mon_h.." tall",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1285,8 +1294,8 @@ local function config_view(display)
|
|||||||
|
|
||||||
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
||||||
|
|
||||||
TextBox{parent=line,x=1,y=1,width=6,height=1,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)}
|
TextBox{parent=line,x=1,y=1,width=6,text=assignment,fg_bg=cpair(util.trinary(assignment=="Unused",colors.red,colors.blue),colors.white)}
|
||||||
TextBox{parent=line,x=8,y=1,height=1,text=iface}
|
TextBox{parent=line,x=8,y=1,text=iface}
|
||||||
|
|
||||||
local w, h = ppm.monitor_block_size(dev.getSize())
|
local w, h = ppm.monitor_block_size(dev.getSize())
|
||||||
|
|
||||||
@@ -1295,7 +1304,7 @@ local function config_view(display)
|
|||||||
tool_ctl.gen_mon_list()
|
tool_ctl.gen_mon_list()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=line,x=33,y=1,width=4,height=1,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)}
|
TextBox{parent=line,x=33,y=1,width=4,text=w.."x"..h,fg_bg=cpair(colors.black,colors.white)}
|
||||||
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()tool_ctl.edit_monitor(iface,device)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
local unset = PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -1314,15 +1323,15 @@ local function config_view(display)
|
|||||||
for i = 1, #dc_list do
|
for i = 1, #dc_list do
|
||||||
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
||||||
|
|
||||||
TextBox{parent=line,x=1,y=1,width=6,height=1,text=dc_list[i][1],fg_bg=cpair(colors.blue,colors.white)}
|
TextBox{parent=line,x=1,y=1,width=6,text=dc_list[i][1],fg_bg=cpair(colors.blue,colors.white)}
|
||||||
TextBox{parent=line,x=8,y=1,height=1,text="disconnected",fg_bg=cpair(colors.red,colors.white)}
|
TextBox{parent=line,x=8,y=1,text="disconnected",fg_bg=cpair(colors.red,colors.white)}
|
||||||
|
|
||||||
local function unset_mon()
|
local function unset_mon()
|
||||||
purge_assignments(dc_list[i][2])
|
purge_assignments(dc_list[i][2])
|
||||||
tool_ctl.gen_mon_list()
|
tool_ctl.gen_mon_list()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=line,x=33,y=1,width=4,height=1,text="?x?",fg_bg=cpair(colors.black,colors.white)}
|
TextBox{parent=line,x=33,y=1,width=4,text="?x?",fg_bg=cpair(colors.black,colors.white)}
|
||||||
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()end,dis_fg_bg=cpair(colors.black,colors.gray)}.disable()
|
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()end,dis_fg_bg=cpair(colors.black,colors.gray)}.disable()
|
||||||
PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
||||||
end
|
end
|
||||||
@@ -1356,7 +1365,9 @@ local function config_view(display)
|
|||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
elseif f[1] == "TempScale" then
|
elseif f[1] == "TempScale" then
|
||||||
if raw == 1 then val = "Kelvin" elseif raw == 2 then val = "Celsius" elseif raw == 3 then val = "Fahrenheit" elseif raw == 4 then val = "Rankine" end
|
val = util.strval(types.TEMP_SCALE_NAMES[raw])
|
||||||
|
elseif f[1] == "EnergyScale" then
|
||||||
|
val = util.strval(types.ENERGY_SCALE_NAMES[raw])
|
||||||
elseif f[1] == "MainTheme" then
|
elseif f[1] == "MainTheme" then
|
||||||
val = util.strval(themes.ui_theme_name(raw))
|
val = util.strval(themes.ui_theme_name(raw))
|
||||||
elseif f[1] == "FrontPanelTheme" then
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ function coordinator.load_config()
|
|||||||
config.SpeakerVolume = settings.get("SpeakerVolume")
|
config.SpeakerVolume = settings.get("SpeakerVolume")
|
||||||
config.Time24Hour = settings.get("Time24Hour")
|
config.Time24Hour = settings.get("Time24Hour")
|
||||||
config.TempScale = settings.get("TempScale")
|
config.TempScale = settings.get("TempScale")
|
||||||
|
config.EnergyScale = settings.get("EnergyScale")
|
||||||
|
|
||||||
config.DisableFlowView = settings.get("DisableFlowView")
|
config.DisableFlowView = settings.get("DisableFlowView")
|
||||||
config.MainDisplay = settings.get("MainDisplay")
|
config.MainDisplay = settings.get("MainDisplay")
|
||||||
@@ -67,6 +68,8 @@ function coordinator.load_config()
|
|||||||
cfv.assert_type_bool(config.Time24Hour)
|
cfv.assert_type_bool(config.Time24Hour)
|
||||||
cfv.assert_type_int(config.TempScale)
|
cfv.assert_type_int(config.TempScale)
|
||||||
cfv.assert_range(config.TempScale, 1, 4)
|
cfv.assert_range(config.TempScale, 1, 4)
|
||||||
|
cfv.assert_type_int(config.EnergyScale)
|
||||||
|
cfv.assert_range(config.EnergyScale, 1, 3)
|
||||||
|
|
||||||
cfv.assert_type_bool(config.DisableFlowView)
|
cfv.assert_type_bool(config.DisableFlowView)
|
||||||
cfv.assert_type_table(config.UnitDisplays)
|
cfv.assert_type_table(config.UnitDisplays)
|
||||||
@@ -232,8 +235,8 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
local self = {
|
local self = {
|
||||||
sv_linked = false,
|
sv_linked = false,
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
sv_seq_num = 0,
|
sv_seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
sv_r_seq_num = nil,
|
sv_r_seq_num = nil, ---@type nil|integer
|
||||||
sv_config_err = false,
|
sv_config_err = false,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||||
last_api_est_acks = {},
|
last_api_est_acks = {},
|
||||||
@@ -492,7 +495,7 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
||||||
elseif dev_type == DEVICE_TYPE.PKT then
|
elseif dev_type == DEVICE_TYPE.PKT then
|
||||||
-- pocket linking request
|
-- pocket linking request
|
||||||
local id = apisessions.establish_session(src_addr, firmware_v)
|
local id = apisessions.establish_session(src_addr, packet.scada_frame.seq_num(), firmware_v)
|
||||||
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
||||||
|
|
||||||
local conf = iocontrol.get_db().facility.conf
|
local conf = iocontrol.get_db().facility.conf
|
||||||
@@ -515,15 +518,15 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
elseif r_chan == config.SVR_Channel then
|
elseif r_chan == config.SVR_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.sv_r_seq_num == nil then
|
if self.sv_r_seq_num == nil then
|
||||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.sv_linked and ((self.sv_r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.sv_r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return false
|
return false
|
||||||
elseif self.sv_linked and src_addr ~= self.sv_addr then
|
elseif self.sv_linked and src_addr ~= self.sv_addr then
|
||||||
log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?")
|
log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@@ -702,11 +705,10 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
if conf.num_units == config.UnitCount then
|
if conf.num_units == config.UnitCount then
|
||||||
-- init io controller
|
-- init io controller
|
||||||
iocontrol.init(conf, public, config.TempScale)
|
iocontrol.init(conf, public, config.TempScale, config.EnergyScale)
|
||||||
|
|
||||||
self.sv_addr = src_addr
|
self.sv_addr = src_addr
|
||||||
self.sv_linked = true
|
self.sv_linked = true
|
||||||
self.sv_r_seq_num = nil
|
|
||||||
self.sv_config_err = false
|
self.sv_config_err = false
|
||||||
|
|
||||||
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
|
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ local pgi = require("coordinator.ui.pgi")
|
|||||||
local ALARM_STATE = types.ALARM_STATE
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
local PROCESS = types.PROCESS
|
local PROCESS = types.PROCESS
|
||||||
|
|
||||||
|
local ENERGY_SCALE = types.ENERGY_SCALE
|
||||||
|
local ENERGY_UNITS = types.ENERGY_SCALE_UNITS
|
||||||
|
local TEMP_SCALE = types.TEMP_SCALE
|
||||||
|
local TEMP_UNITS = types.TEMP_SCALE_UNITS
|
||||||
|
|
||||||
-- nominal RTT is ping (0ms to 10ms usually) + 500ms for CRD main loop tick
|
-- nominal RTT is ping (0ms to 10ms usually) + 500ms for CRD main loop tick
|
||||||
local WARN_RTT = 1000 -- 2x as long as expected w/ 0 ping
|
local WARN_RTT = 1000 -- 2x as long as expected w/ 0 ping
|
||||||
local HIGH_RTT = 1500 -- 3.33x as long as expected w/ 0 ping
|
local HIGH_RTT = 1500 -- 3.33x as long as expected w/ 0 ping
|
||||||
@@ -47,23 +52,36 @@ end
|
|||||||
-- initialize the coordinator IO controller
|
-- initialize the coordinator IO controller
|
||||||
---@param conf facility_conf configuration
|
---@param conf facility_conf configuration
|
||||||
---@param comms coord_comms comms reference
|
---@param comms coord_comms comms reference
|
||||||
---@param temp_scale integer temperature unit (1 = K, 2 = C, 3 = F, 4 = R)
|
---@param temp_scale TEMP_SCALE temperature unit
|
||||||
function iocontrol.init(conf, comms, temp_scale)
|
---@param energy_scale ENERGY_SCALE energy unit
|
||||||
|
function iocontrol.init(conf, comms, temp_scale, energy_scale)
|
||||||
|
io.temp_label = TEMP_UNITS[temp_scale]
|
||||||
|
io.energy_label = ENERGY_UNITS[energy_scale]
|
||||||
|
|
||||||
-- temperature unit label and conversion function (from Kelvin)
|
-- temperature unit label and conversion function (from Kelvin)
|
||||||
if temp_scale == 2 then
|
if temp_scale == TEMP_SCALE.CELSIUS then
|
||||||
io.temp_label = "\xb0C"
|
|
||||||
io.temp_convert = function (t) return t - 273.15 end
|
io.temp_convert = function (t) return t - 273.15 end
|
||||||
elseif temp_scale == 3 then
|
elseif temp_scale == TEMP_SCALE.FAHRENHEIT then
|
||||||
io.temp_label = "\xb0F"
|
|
||||||
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
||||||
elseif temp_scale == 4 then
|
elseif temp_scale == TEMP_SCALE.RANKINE then
|
||||||
io.temp_label = "\xb0R"
|
|
||||||
io.temp_convert = function (t) return 1.8 * t end
|
io.temp_convert = function (t) return 1.8 * t end
|
||||||
else
|
else
|
||||||
io.temp_label = "K"
|
io.temp_label = "K"
|
||||||
io.temp_convert = function (t) return t end
|
io.temp_convert = function (t) return t end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- energy unit label and conversion function (from Joules unless otherwise specified)
|
||||||
|
if energy_scale == ENERGY_SCALE.FE or energy_scale == ENERGY_SCALE.RF then
|
||||||
|
io.energy_convert = util.joules_to_fe_rf
|
||||||
|
io.energy_convert_from_fe = function (t) return t end
|
||||||
|
io.energy_convert_to_fe = function (t) return t end
|
||||||
|
else
|
||||||
|
io.energy_label = "J"
|
||||||
|
io.energy_convert = function (t) return t end
|
||||||
|
io.energy_convert_from_fe = util.fe_rf_to_joules
|
||||||
|
io.energy_convert_to_fe = util.joules_to_fe_rf
|
||||||
|
end
|
||||||
|
|
||||||
-- facility data structure
|
-- facility data structure
|
||||||
---@class ioctl_facility
|
---@class ioctl_facility
|
||||||
io.facility = {
|
io.facility = {
|
||||||
@@ -247,6 +265,9 @@ function iocontrol.init(conf, comms, temp_scale)
|
|||||||
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
||||||
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
|
|
||||||
|
last_rate_change_ms = 0,
|
||||||
|
turbine_flow_stable = false,
|
||||||
|
|
||||||
-- auto control group
|
-- auto control group
|
||||||
a_group = 0,
|
a_group = 0,
|
||||||
|
|
||||||
@@ -688,7 +709,7 @@ function iocontrol.update_facility_status(status)
|
|||||||
ps.publish("is_discharging", out_f > in_f)
|
ps.publish("is_discharging", out_f > in_f)
|
||||||
|
|
||||||
if data and data.build then
|
if data and data.build then
|
||||||
local cap = util.joules_to_fe(data.build.transfer_cap)
|
local cap = util.joules_to_fe_rf(data.build.transfer_cap)
|
||||||
ps.publish("at_max_io", in_f >= cap or out_f >= cap)
|
ps.publish("at_max_io", in_f >= cap or out_f >= cap)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -1214,9 +1235,11 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local unit_state = status[5]
|
local unit_state = status[5]
|
||||||
|
|
||||||
if type(unit_state) == "table" then
|
if type(unit_state) == "table" then
|
||||||
if #unit_state == 6 then
|
if #unit_state == 8 then
|
||||||
unit.waste_mode = unit_state[5]
|
unit.waste_mode = unit_state[5]
|
||||||
unit.waste_product = unit_state[6]
|
unit.waste_product = unit_state[6]
|
||||||
|
unit.last_rate_change_ms = unit_state[7]
|
||||||
|
unit.turbine_flow_stable = unit_state[8]
|
||||||
|
|
||||||
unit.unit_ps.publish("U_StatusLine1", unit_state[1])
|
unit.unit_ps.publish("U_StatusLine1", unit_state[1])
|
||||||
unit.unit_ps.publish("U_StatusLine2", unit_state[2])
|
unit.unit_ps.publish("U_StatusLine2", unit_state[2])
|
||||||
|
|||||||
@@ -70,8 +70,8 @@ function process.init(iocontrol, coord_comms)
|
|||||||
|
|
||||||
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
||||||
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
||||||
self.io.facility.ps.publish("process_charge_target", ctl_proc.charge_target)
|
self.io.facility.ps.publish("process_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target))
|
||||||
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(ctl_proc.gen_target))
|
||||||
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
||||||
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
||||||
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
|
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
|
||||||
@@ -316,8 +316,8 @@ function process.start_ack_handle(response)
|
|||||||
|
|
||||||
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
||||||
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
||||||
self.io.facility.ps.publish("process_charge_target", ctl_proc.charge_target)
|
self.io.facility.ps.publish("process_charge_target", self.io.energy_convert_from_fe(ctl_proc.charge_target))
|
||||||
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
self.io.facility.ps.publish("process_gen_target", self.io.energy_convert_from_fe(ctl_proc.gen_target))
|
||||||
|
|
||||||
self.io.facility.start_ack(ack)
|
self.io.facility.start_ack(ack)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,10 +89,11 @@ end
|
|||||||
|
|
||||||
-- establish a new API session
|
-- establish a new API session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer pocket computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string pocket version
|
||||||
---@return integer session_id
|
---@return integer session_id
|
||||||
function apisessions.establish_session(source_addr, version)
|
function apisessions.establish_session(source_addr, i_seq_num, version)
|
||||||
---@class pkt_session_struct
|
---@class pkt_session_struct
|
||||||
local pkt_s = {
|
local pkt_s = {
|
||||||
open = true,
|
open = true,
|
||||||
@@ -105,7 +106,7 @@ function apisessions.establish_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_id
|
local id = self.next_id
|
||||||
|
|
||||||
pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, self.config.API_Timeout)
|
pkt_s.instance = pocket.new_session(id, source_addr, i_seq_num, pkt_s.in_queue, pkt_s.out_queue, self.config.API_Timeout)
|
||||||
table.insert(self.sessions, pkt_s)
|
table.insert(self.sessions, pkt_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
|
|||||||
@@ -32,16 +32,17 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
|
||||||
local log_header = "pkt_session(" .. id .. "): "
|
local log_header = "pkt_session(" .. id .. "): "
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@@ -104,13 +105,11 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
|||||||
---@param pkt mgmt_frame|crdn_frame
|
---@param pkt mgmt_frame|crdn_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
@@ -152,7 +151,9 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
|||||||
u.reactor_data,
|
u.reactor_data,
|
||||||
u.boiler_data_tbl,
|
u.boiler_data_tbl,
|
||||||
u.turbine_data_tbl,
|
u.turbine_data_tbl,
|
||||||
u.tank_data_tbl
|
u.tank_data_tbl,
|
||||||
|
u.last_rate_change_ms,
|
||||||
|
u.turbine_flow_stable
|
||||||
}
|
}
|
||||||
|
|
||||||
_send(CRDN_TYPE.API_GET_UNIT, data)
|
_send(CRDN_TYPE.API_GET_UNIT, data)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
|
|||||||
local sounder = require("coordinator.sounder")
|
local sounder = require("coordinator.sounder")
|
||||||
local threads = require("coordinator.threads")
|
local threads = require("coordinator.threads")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v1.4.6"
|
local COORDINATOR_VERSION = "v1.5.2"
|
||||||
|
|
||||||
local CHUNK_LOAD_DELAY_S = 30.0
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
@@ -151,8 +151,8 @@ local function main()
|
|||||||
|
|
||||||
-- core coordinator devices
|
-- core coordinator devices
|
||||||
crd_dev = {
|
crd_dev = {
|
||||||
speaker = ppm.get_device("speaker"),
|
modem = ppm.get_wireless_modem(),
|
||||||
modem = ppm.get_wireless_modem()
|
speaker = ppm.get_device("speaker")
|
||||||
},
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
|
|||||||
@@ -244,7 +244,7 @@ function threads.thread__main(smem)
|
|||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
-- coordinator renderer thread, tasked with long duration re-draws
|
-- coordinator renderer thread, tasked with long duration draws
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param smem crd_shared_memory
|
---@param smem crd_shared_memory
|
||||||
function threads.thread__render(smem)
|
function threads.thread__render(smem)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
local style = require("coordinator.ui.style")
|
|
||||||
|
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
|
local style = require("coordinator.ui.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Rectangle = require("graphics.elements.rectangle")
|
local Rectangle = require("graphics.elements.rectangle")
|
||||||
@@ -35,10 +35,10 @@ local function new_view(root, x, y, ps)
|
|||||||
temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end)
|
temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end)
|
||||||
boil_r.register(ps, "boil_rate", boil_r.update)
|
boil_r.register(ps, "boil_rate", boil_r.update)
|
||||||
|
|
||||||
TextBox{parent=boiler,text="H",x=2,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=boiler,text="H",x=2,y=5,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="W",x=3,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=boiler,text="W",x=3,y=5,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="S",x=27,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=boiler,text="S",x=27,y=5,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="C",x=28,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=boiler,text="C",x=28,y=5,width=1,fg_bg=text_fg}
|
||||||
|
|
||||||
local hcool = VerticalBar{parent=boiler,x=2,y=1,fg_bg=cpair(colors.orange,colors.gray),height=4,width=1}
|
local hcool = VerticalBar{parent=boiler,x=2,y=1,fg_bg=cpair(colors.orange,colors.gray),height=4,width=1}
|
||||||
local water = VerticalBar{parent=boiler,x=3,y=1,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
local water = VerticalBar{parent=boiler,x=3,y=1,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
@@ -34,6 +36,8 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
local ind_yel = style.ind_yel
|
local ind_yel = style.ind_yel
|
||||||
local ind_wht = style.ind_wht
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local title = "INDUCTION MATRIX"
|
local title = "INDUCTION MATRIX"
|
||||||
if type(id) == "number" then title = title .. id end
|
if type(id) == "number" then title = title .. id end
|
||||||
|
|
||||||
@@ -42,30 +46,30 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
-- black has low contrast with dark gray, so if background is black use white instead
|
-- black has low contrast with dark gray, so if background is black use white instead
|
||||||
local cutout_fg_bg = cpair(util.trinary(style.theme.bg == colors.black, colors.white, style.theme.bg), colors.gray)
|
local cutout_fg_bg = cpair(util.trinary(style.theme.bg == colors.black, colors.white, style.theme.bg), colors.gray)
|
||||||
|
|
||||||
TextBox{parent=matrix,text=" ",width=33,height=1,x=1,y=1,fg_bg=cutout_fg_bg}
|
TextBox{parent=matrix,text=" ",width=33,x=1,y=1,fg_bg=cutout_fg_bg}
|
||||||
TextBox{parent=matrix,text=title,alignment=ALIGN.CENTER,width=33,height=1,x=1,y=2,fg_bg=cutout_fg_bg}
|
TextBox{parent=matrix,text=title,alignment=ALIGN.CENTER,width=33,x=1,y=2,fg_bg=cutout_fg_bg}
|
||||||
|
|
||||||
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
||||||
|
|
||||||
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
||||||
local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",unit=db.energy_label,format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end)
|
capacity.register(ps, "max_energy", function (val) capacity.update(db.energy_convert(val)) end)
|
||||||
energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end)
|
energy.register(ps, "energy", function (val) energy.update(db.energy_convert(val)) end)
|
||||||
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
||||||
input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end)
|
input.register(ps, "last_input", function (val) input.update(db.energy_convert(val)) end)
|
||||||
avg_in.register(ps, "avg_inflow", avg_in.update)
|
avg_in.register(ps, "avg_inflow", avg_in.update)
|
||||||
output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end)
|
output.register(ps, "last_output", function (val) output.update(db.energy_convert(val)) end)
|
||||||
avg_out.register(ps, "avg_outflow", avg_out.update)
|
avg_out.register(ps, "avg_outflow", avg_out.update)
|
||||||
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end)
|
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(db.energy_convert(val)) end)
|
||||||
|
|
||||||
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill: ",format="%7.2f",unit="%",value=0,width=20,fg_bg=text_fg}
|
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill: ",format="%7.2f",unit="%",value=0,width=20,fg_bg=text_fg}
|
||||||
local cells = DataIndicator{parent=rect,x=11,y=13,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg}
|
local cells = DataIndicator{parent=rect,x=11,y=13,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg}
|
||||||
@@ -87,7 +91,7 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
||||||
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
||||||
|
|
||||||
TextBox{parent=rect,text="FILL I/O",x=2,y=20,height=1,width=8,fg_bg=label_fg}
|
TextBox{parent=rect,text="FILL I/O",x=2,y=20,width=8,fg_bg=label_fg}
|
||||||
|
|
||||||
local function calc_saturation(val)
|
local function calc_saturation(val)
|
||||||
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
||||||
@@ -99,7 +103,7 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
||||||
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
||||||
|
|
||||||
local eta = TextBox{parent=rect,x=11,y=20,width=20,height=1,text="ETA Unknown",alignment=ALIGN.CENTER,fg_bg=style.theme.field_box}
|
local eta = TextBox{parent=rect,x=11,y=20,width=20,text="ETA Unknown",alignment=ALIGN.CENTER,fg_bg=style.theme.field_box}
|
||||||
|
|
||||||
eta.register(ps, "eta_ms", function (eta_ms)
|
eta.register(ps, "eta_ms", function (eta_ms)
|
||||||
local str, pre = "", util.trinary(eta_ms >= 0, "Full in ", "Empty in ")
|
local str, pre = "", util.trinary(eta_ms >= 0, "Full in ", "Empty in ")
|
||||||
|
|||||||
@@ -34,18 +34,18 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local ps_prefix = "pkt_" .. id .. "_"
|
local ps_prefix = "pkt_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,fg_bg=s_hi_box}
|
||||||
local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,fg_bg=s_hi_box}
|
||||||
pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value)
|
pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=10,y=2,text="FW:",width=3}
|
||||||
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
||||||
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4}
|
||||||
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -56,8 +56,10 @@ local function new_view(root, x, y)
|
|||||||
local blk_brn = cpair(colors.black, colors.brown)
|
local blk_brn = cpair(colors.black, colors.brown)
|
||||||
local blk_pur = cpair(colors.black, colors.purple)
|
local blk_pur = cpair(colors.black, colors.purple)
|
||||||
|
|
||||||
local facility = iocontrol.get_db().facility
|
local db = iocontrol.get_db()
|
||||||
local units = iocontrol.get_db().units
|
|
||||||
|
local facility = db.facility
|
||||||
|
local units = db.units
|
||||||
|
|
||||||
local main = Div{parent=root,width=128,height=24,x=x,y=y}
|
local main = Div{parent=root,width=128,height=24,x=x,y=y}
|
||||||
|
|
||||||
@@ -105,11 +107,11 @@ local function new_view(root, x, y)
|
|||||||
fac_rad_h.register(facility.ps, "as_radiation", fac_rad_h.update)
|
fac_rad_h.register(facility.ps, "as_radiation", fac_rad_h.update)
|
||||||
gen_fault.register(facility.ps, "as_gen_fault", gen_fault.update)
|
gen_fault.register(facility.ps, "as_gen_fault", gen_fault.update)
|
||||||
|
|
||||||
TextBox{parent=main,y=23,text="Radiation",height=1,width=13,fg_bg=style.label}
|
TextBox{parent=main,y=23,text="Radiation",width=13,fg_bg=style.label}
|
||||||
local radiation = RadIndicator{parent=main,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
local radiation = RadIndicator{parent=main,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
radiation.register(facility.ps, "radiation", radiation.update)
|
radiation.register(facility.ps, "radiation", radiation.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=15,y=23,text="Linked RTUs",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=main,x=15,y=23,text="Linked RTUs",width=11,fg_bg=style.label}
|
||||||
local rtu_count = DataIndicator{parent=main,x=15,y=24,label="",format="%11d",value=0,lu_colors=lu_cpair,width=11,fg_bg=s_field}
|
local rtu_count = DataIndicator{parent=main,x=15,y=24,label="",format="%11d",value=0,lu_colors=lu_cpair,width=11,fg_bg=s_field}
|
||||||
rtu_count.register(facility.ps, "rtu_count", rtu_count.update)
|
rtu_count.register(facility.ps, "rtu_count", rtu_count.update)
|
||||||
|
|
||||||
@@ -141,22 +143,22 @@ local function new_view(root, x, y)
|
|||||||
|
|
||||||
local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=s_hi_box}
|
local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=s_hi_box}
|
||||||
local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=chg_target,x=18,y=2,text="MFE",fg_bg=style.theme.label_fg}
|
TextBox{parent=chg_target,x=18,y=2,text="M"..db.energy_label,fg_bg=style.theme.label_fg}
|
||||||
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="M"..db.energy_label,commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
||||||
cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(fe / 1000000) end)
|
cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(db.energy_convert_from_fe(fe) / 1000000) end)
|
||||||
|
|
||||||
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
||||||
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
||||||
|
|
||||||
local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=s_hi_box}
|
local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=s_hi_box}
|
||||||
local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=gen_target,x=18,y=2,text="kFE/t",fg_bg=style.theme.label_fg}
|
TextBox{parent=gen_target,x=18,y=2,text="k"..db.energy_label.."/t",fg_bg=style.theme.label_fg}
|
||||||
local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="k"..db.energy_label.."/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
g_target.register(facility.ps, "process_gen_target", g_target.set_value)
|
g_target.register(facility.ps, "process_gen_target", g_target.set_value)
|
||||||
cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(util.joules_to_fe(j) / 1000)) end)
|
cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(db.energy_convert(j) / 1000)) end)
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
-- unit limits --
|
-- unit limits --
|
||||||
@@ -190,7 +192,7 @@ local function new_view(root, x, y)
|
|||||||
|
|
||||||
local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=s_hi_box}
|
local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=s_hi_box}
|
||||||
local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled,fg_bg=lim_fg_bg}
|
local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled,fg_bg=lim_fg_bg}
|
||||||
TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,fg_bg=label_fg}
|
||||||
|
|
||||||
local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg}
|
local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg}
|
||||||
|
|
||||||
@@ -249,8 +251,8 @@ local function new_view(root, x, y)
|
|||||||
mode.register(facility.ps, "process_mode", mode.set_value)
|
mode.register(facility.ps, "process_mode", mode.set_value)
|
||||||
|
|
||||||
local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg}
|
local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg}
|
||||||
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
||||||
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.white)}
|
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=31,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value)
|
stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value)
|
||||||
stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
|
stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
|
||||||
@@ -262,7 +264,10 @@ local function new_view(root, x, y)
|
|||||||
local limits = {}
|
local limits = {}
|
||||||
for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end
|
for i = 1, #rate_limits do limits[i] = rate_limits[i].get_value() end
|
||||||
|
|
||||||
process.save(mode.get_value(), b_target.get_value(), c_target.get_value(), g_target.get_value(), limits)
|
process.save(mode.get_value(), b_target.get_value(),
|
||||||
|
db.energy_convert_to_fe(c_target.get_value()),
|
||||||
|
db.energy_convert_to_fe(g_target.get_value()),
|
||||||
|
limits)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- start automatic control after saving process control settings
|
-- start automatic control after saving process control settings
|
||||||
@@ -320,7 +325,7 @@ local function new_view(root, x, y)
|
|||||||
for i = 1, facility.num_units do
|
for i = 1, facility.num_units do
|
||||||
local unit = units[i] ---@type ioctl_unit
|
local unit = units[i] ---@type ioctl_unit
|
||||||
|
|
||||||
TextBox{parent=waste_status,y=i,text="U"..i.." Waste",width=8,height=1}
|
TextBox{parent=waste_status,y=i,text="U"..i.." Waste",width=8}
|
||||||
local a_waste = IndicatorLight{parent=waste_status,x=10,y=i,label="Auto",colors=ind_wht}
|
local a_waste = IndicatorLight{parent=waste_status,x=10,y=i,label="Auto",colors=ind_wht}
|
||||||
local waste_m = StateIndicator{parent=waste_status,x=17,y=i,states=style.waste.states_abbrv,value=1,min_width=6}
|
local waste_m = StateIndicator{parent=waste_status,x=17,y=i,states=style.waste.states_abbrv,value=1,min_width=6}
|
||||||
|
|
||||||
@@ -332,8 +337,8 @@ local function new_view(root, x, y)
|
|||||||
|
|
||||||
local cutout_fg_bg = cpair(style.theme.bg, colors.brown)
|
local cutout_fg_bg = cpair(style.theme.bg, colors.brown)
|
||||||
|
|
||||||
TextBox{parent=waste_sel,text=" ",width=21,height=1,x=1,y=1,fg_bg=cutout_fg_bg}
|
TextBox{parent=waste_sel,text=" ",width=21,x=1,y=1,fg_bg=cutout_fg_bg}
|
||||||
TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=ALIGN.CENTER,width=21,height=1,x=1,y=2,fg_bg=cutout_fg_bg}
|
TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=ALIGN.CENTER,width=21,x=1,y=2,fg_bg=cutout_fg_bg}
|
||||||
|
|
||||||
local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3}
|
local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3}
|
||||||
local status = StateIndicator{parent=rect,x=2,y=1,states=style.waste.states,value=1,min_width=17}
|
local status = StateIndicator{parent=rect,x=2,y=1,states=style.waste.states,value=1,min_width=17}
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ local function new_view(root, x, y, ps)
|
|||||||
|
|
||||||
local reactor_fills = Rectangle{parent=root,border=border(1, colors.gray, true),width=24,height=7,x=(x + 29),y=y}
|
local reactor_fills = Rectangle{parent=root,border=border(1, colors.gray, true),width=24,height=7,x=(x + 29),y=y}
|
||||||
|
|
||||||
TextBox{parent=reactor_fills,text="FUEL",x=2,y=1,height=1,fg_bg=text_fg}
|
TextBox{parent=reactor_fills,text="FUEL",x=2,y=1,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="COOL",x=2,y=2,height=1,fg_bg=text_fg}
|
TextBox{parent=reactor_fills,text="COOL",x=2,y=2,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="HCOOL",x=2,y=4,height=1,fg_bg=text_fg}
|
TextBox{parent=reactor_fills,text="HCOOL",x=2,y=4,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="WASTE",x=2,y=5,height=1,fg_bg=text_fg}
|
TextBox{parent=reactor_fills,text="WASTE",x=2,y=5,fg_bg=text_fg}
|
||||||
|
|
||||||
local fuel = HorizontalBar{parent=reactor_fills,x=8,y=1,show_percent=true,bar_fg_bg=cpair(style.theme.fuel_color,colors.gray),height=1,width=14}
|
local fuel = HorizontalBar{parent=reactor_fills,x=8,y=1,show_percent=true,bar_fg_bg=cpair(style.theme.fuel_color,colors.gray),height=1,width=14}
|
||||||
local ccool = HorizontalBar{parent=reactor_fills,x=8,y=2,show_percent=true,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=14}
|
local ccool = HorizontalBar{parent=reactor_fills,x=8,y=2,show_percent=true,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=14}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
local util = require("scada-common.util")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
|
|
||||||
@@ -24,21 +24,23 @@ local function new_view(root, x, y, ps)
|
|||||||
local text_fg = style.theme.text_fg
|
local text_fg = style.theme.text_fg
|
||||||
local lu_col = style.lu_colors
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y}
|
local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y}
|
||||||
|
|
||||||
local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12}
|
local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12}
|
||||||
local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg}
|
local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",unit=db.energy_label,format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg}
|
||||||
local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg}
|
local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end)
|
prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(db.energy_convert(val)) end)
|
||||||
flow_rate.register(ps, "steam_input_rate", flow_rate.update)
|
flow_rate.register(ps, "steam_input_rate", flow_rate.update)
|
||||||
|
|
||||||
local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
||||||
local energy = VerticalBar{parent=turbine,x=3,y=1,fg_bg=cpair(colors.green,colors.gray),height=4,width=1}
|
local energy = VerticalBar{parent=turbine,x=3,y=1,fg_bg=cpair(colors.green,colors.gray),height=4,width=1}
|
||||||
|
|
||||||
TextBox{parent=turbine,text="S",x=2,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=turbine,text="S",x=2,y=5,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=turbine,text="E",x=3,y=5,height=1,width=1,fg_bg=text_fg}
|
TextBox{parent=turbine,text="E",x=3,y=5,width=1,fg_bg=text_fg}
|
||||||
|
|
||||||
steam.register(ps, "steam_fill", steam.update)
|
steam.register(ps, "steam_fill", steam.update)
|
||||||
energy.register(ps, "energy_fill", energy.update)
|
energy.register(ps, "energy_fill", energy.update)
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ local function init(parent, id)
|
|||||||
local b_ps = unit.boiler_ps_tbl
|
local b_ps = unit.boiler_ps_tbl
|
||||||
local t_ps = unit.turbine_ps_tbl
|
local t_ps = unit.turbine_ps_tbl
|
||||||
|
|
||||||
TextBox{parent=main,text="Reactor Unit #" .. id,alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
TextBox{parent=main,text="Reactor Unit #" .. id,alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
-- main stats and core map --
|
-- main stats and core map --
|
||||||
@@ -81,20 +81,20 @@ local function init(parent, id)
|
|||||||
core_map.register(u_ps, "temp", core_map.update)
|
core_map.register(u_ps, "temp", core_map.update)
|
||||||
core_map.register(u_ps, "size", function (s) core_map.resize(s[1], s[2]) end)
|
core_map.register(u_ps, "size", function (s) core_map.resize(s[1], s[2]) end)
|
||||||
|
|
||||||
TextBox{parent=main,x=12,y=22,text="Heating Rate",height=1,width=12,fg_bg=style.label}
|
TextBox{parent=main,x=12,y=22,text="Heating Rate",width=12,fg_bg=style.label}
|
||||||
local heating_r = DataIndicator{parent=main,x=12,label="",format="%14.0f",value=0,unit="mB/t",commas=true,lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
local heating_r = DataIndicator{parent=main,x=12,label="",format="%14.0f",value=0,unit="mB/t",commas=true,lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
||||||
heating_r.register(u_ps, "heating_rate", heating_r.update)
|
heating_r.register(u_ps, "heating_rate", heating_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=12,y=25,text="Commanded Burn Rate",height=1,width=19,fg_bg=style.label}
|
TextBox{parent=main,x=12,y=25,text="Commanded Burn Rate",width=19,fg_bg=style.label}
|
||||||
local burn_r = DataIndicator{parent=main,x=12,label="",format="%14.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
local burn_r = DataIndicator{parent=main,x=12,label="",format="%14.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
||||||
burn_r.register(u_ps, "burn_rate", burn_r.update)
|
burn_r.register(u_ps, "burn_rate", burn_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,text="F",x=2,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="F",x=2,y=22,width=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="C",x=4,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="C",x=4,y=22,width=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="\x1a",x=6,y=24,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="\x1a",x=6,y=24,width=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="\x1a",x=6,y=25,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="\x1a",x=6,y=25,width=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="H",x=8,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="H",x=8,y=22,width=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="W",x=10,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="W",x=10,y=22,width=1,fg_bg=style.label}
|
||||||
|
|
||||||
local fuel = VerticalBar{parent=main,x=2,y=23,fg_bg=cpair(style.theme.fuel_color,colors.gray),height=4,width=1}
|
local fuel = VerticalBar{parent=main,x=2,y=23,fg_bg=cpair(style.theme.fuel_color,colors.gray),height=4,width=1}
|
||||||
local ccool = VerticalBar{parent=main,x=4,y=23,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
local ccool = VerticalBar{parent=main,x=4,y=23,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
||||||
@@ -122,20 +122,20 @@ local function init(parent, id)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=22,text="Core Temp",height=1,width=9,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=22,text="Core Temp",width=9,fg_bg=style.label}
|
||||||
local fmt = util.trinary(string.len(db.temp_label) == 2, "%10.2f", "%11.2f")
|
local fmt = util.trinary(string.len(db.temp_label) == 2, "%10.2f", "%11.2f")
|
||||||
local core_temp = DataIndicator{parent=main,x=32,label="",format=fmt,value=0,commas=true,unit=db.temp_label,lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
local core_temp = DataIndicator{parent=main,x=32,label="",format=fmt,value=0,commas=true,unit=db.temp_label,lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end)
|
core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=25,text="Burn Rate",height=1,width=9,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=25,text="Burn Rate",width=9,fg_bg=style.label}
|
||||||
local act_burn_r = DataIndicator{parent=main,x=32,label="",format="%8.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
local act_burn_r = DataIndicator{parent=main,x=32,label="",format="%8.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
act_burn_r.register(u_ps, "act_burn_rate", act_burn_r.update)
|
act_burn_r.register(u_ps, "act_burn_rate", act_burn_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=28,text="Damage",height=1,width=6,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=28,text="Damage",width=6,fg_bg=style.label}
|
||||||
local damage_p = DataIndicator{parent=main,x=32,label="",format="%11.0f",value=0,unit="%",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
local damage_p = DataIndicator{parent=main,x=32,label="",format="%11.0f",value=0,unit="%",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
damage_p.register(u_ps, "damage", damage_p.update)
|
damage_p.register(u_ps, "damage", damage_p.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=31,text="Radiation",height=1,width=21,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=31,text="Radiation",width=21,fg_bg=style.label}
|
||||||
local radiation = RadIndicator{parent=main,x=32,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
local radiation = RadIndicator{parent=main,x=32,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
radiation.register(u_ps, "radiation", radiation.update)
|
radiation.register(u_ps, "radiation", radiation.update)
|
||||||
|
|
||||||
@@ -144,8 +144,8 @@ local function init(parent, id)
|
|||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
local u_stat = Rectangle{parent=main,border=border(1,colors.gray,true),thin=true,width=33,height=4,x=46,y=3,fg_bg=bw_fg_bg}
|
local u_stat = Rectangle{parent=main,border=border(1,colors.gray,true),thin=true,width=33,height=4,x=46,y=3,fg_bg=bw_fg_bg}
|
||||||
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=33,height=1,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=33,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
||||||
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=33,height=1,alignment=ALIGN.CENTER,fg_bg=gry_wht}
|
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=33,alignment=ALIGN.CENTER,fg_bg=gry_wht}
|
||||||
|
|
||||||
stat_line_1.register(u_ps, "U_StatusLine1", stat_line_1.set_value)
|
stat_line_1.register(u_ps, "U_StatusLine1", stat_line_1.set_value)
|
||||||
stat_line_2.register(u_ps, "U_StatusLine2", stat_line_2.set_value)
|
stat_line_2.register(u_ps, "U_StatusLine2", stat_line_2.set_value)
|
||||||
@@ -205,7 +205,7 @@ local function init(parent, id)
|
|||||||
|
|
||||||
-- RPS annunciator panel
|
-- RPS annunciator panel
|
||||||
|
|
||||||
TextBox{parent=main,text="REACTOR PROTECTION SYSTEM",fg_bg=cpair(colors.black,colors.cyan),alignment=ALIGN.CENTER,width=33,height=1,x=46,y=8}
|
TextBox{parent=main,text="REACTOR PROTECTION SYSTEM",fg_bg=cpair(colors.black,colors.cyan),alignment=ALIGN.CENTER,width=33,x=46,y=8}
|
||||||
local rps = Rectangle{parent=main,border=border(1,colors.cyan,true),thin=true,width=33,height=12,x=46,y=9}
|
local rps = Rectangle{parent=main,border=border(1,colors.cyan,true),thin=true,width=33,height=12,x=46,y=9}
|
||||||
local rps_annunc = Div{parent=rps,width=31,height=10,x=2,y=1}
|
local rps_annunc = Div{parent=rps,width=31,height=10,x=2,y=1}
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ local function init(parent, id)
|
|||||||
|
|
||||||
-- cooling annunciator panel
|
-- cooling annunciator panel
|
||||||
|
|
||||||
TextBox{parent=main,text="REACTOR COOLANT SYSTEM",fg_bg=cpair(colors.black,colors.blue),alignment=ALIGN.CENTER,width=33,height=1,x=46,y=22}
|
TextBox{parent=main,text="REACTOR COOLANT SYSTEM",fg_bg=cpair(colors.black,colors.blue),alignment=ALIGN.CENTER,width=33,x=46,y=22}
|
||||||
local rcs = Rectangle{parent=main,border=border(1,colors.blue,true),thin=true,width=33,height=24,x=46,y=23}
|
local rcs = Rectangle{parent=main,border=border(1,colors.blue,true),thin=true,width=33,height=24,x=46,y=23}
|
||||||
local rcs_annunc = Div{parent=rcs,width=27,height=22,x=3,y=1}
|
local rcs_annunc = Div{parent=rcs,width=27,height=22,x=3,y=1}
|
||||||
local rcs_tags = Div{parent=rcs,width=2,height=16,x=1,y=7}
|
local rcs_tags = Div{parent=rcs,width=2,height=16,x=1,y=7}
|
||||||
@@ -265,11 +265,11 @@ local function init(parent, id)
|
|||||||
if unit.num_boilers > 0 then
|
if unit.num_boilers > 0 then
|
||||||
if available_space > 0 then _add_space() end
|
if available_space > 0 then _add_space() end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,x=1,text="B1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,x=1,text="B1",width=2,fg_bg=hc_text}
|
||||||
local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
||||||
b1_wll.register(b_ps[1], "WaterLevelLow", b1_wll.update)
|
b1_wll.register(b_ps[1], "WaterLevelLow", b1_wll.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="B1",width=2,fg_bg=hc_text}
|
||||||
local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
||||||
b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update)
|
b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update)
|
||||||
end
|
end
|
||||||
@@ -281,11 +281,11 @@ local function init(parent, id)
|
|||||||
_add_space()
|
_add_space()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="B2",width=2,fg_bg=hc_text}
|
||||||
local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
||||||
b2_wll.register(b_ps[2], "WaterLevelLow", b2_wll.update)
|
b2_wll.register(b_ps[2], "WaterLevelLow", b2_wll.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="B2",width=2,fg_bg=hc_text}
|
||||||
local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
||||||
b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update)
|
b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update)
|
||||||
end
|
end
|
||||||
@@ -294,19 +294,19 @@ local function init(parent, id)
|
|||||||
|
|
||||||
if available_space > 1 then _add_space() end
|
if available_space > 1 then _add_space() end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T1",width=2,fg_bg=hc_text}
|
||||||
local t1_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
local t1_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t1_sdo.register(t_ps[1], "SteamDumpOpen", t1_sdo.update)
|
t1_sdo.register(t_ps[1], "SteamDumpOpen", t1_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T1",width=2,fg_bg=hc_text}
|
||||||
local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t1_tos.register(t_ps[1], "TurbineOverSpeed", t1_tos.update)
|
t1_tos.register(t_ps[1], "TurbineOverSpeed", t1_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T1",width=2,fg_bg=hc_text}
|
||||||
local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t1_gtrp.register(t_ps[1], "GeneratorTrip", t1_gtrp.update)
|
t1_gtrp.register(t_ps[1], "GeneratorTrip", t1_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T1",width=2,fg_bg=hc_text}
|
||||||
local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update)
|
t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update)
|
||||||
|
|
||||||
@@ -315,19 +315,19 @@ local function init(parent, id)
|
|||||||
_add_space()
|
_add_space()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T2",width=2,fg_bg=hc_text}
|
||||||
local t2_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
local t2_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t2_sdo.register(t_ps[2], "SteamDumpOpen", t2_sdo.update)
|
t2_sdo.register(t_ps[2], "SteamDumpOpen", t2_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T2",width=2,fg_bg=hc_text}
|
||||||
local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t2_tos.register(t_ps[2], "TurbineOverSpeed", t2_tos.update)
|
t2_tos.register(t_ps[2], "TurbineOverSpeed", t2_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T2",width=2,fg_bg=hc_text}
|
||||||
local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t2_gtrp.register(t_ps[2], "GeneratorTrip", t2_gtrp.update)
|
t2_gtrp.register(t_ps[2], "GeneratorTrip", t2_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T2",width=2,fg_bg=hc_text}
|
||||||
local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update)
|
t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update)
|
||||||
end
|
end
|
||||||
@@ -335,19 +335,19 @@ local function init(parent, id)
|
|||||||
if unit.num_turbines > 2 then
|
if unit.num_turbines > 2 then
|
||||||
if available_space > 3 then _add_space() end
|
if available_space > 3 then _add_space() end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T3",width=2,fg_bg=hc_text}
|
||||||
local t3_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
local t3_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t3_sdo.register(t_ps[3], "SteamDumpOpen", t3_sdo.update)
|
t3_sdo.register(t_ps[3], "SteamDumpOpen", t3_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T3",width=2,fg_bg=hc_text}
|
||||||
local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t3_tos.register(t_ps[3], "TurbineOverSpeed", t3_tos.update)
|
t3_tos.register(t_ps[3], "TurbineOverSpeed", t3_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T3",width=2,fg_bg=hc_text}
|
||||||
local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t3_gtrp.register(t_ps[3], "GeneratorTrip", t3_gtrp.update)
|
t3_gtrp.register(t_ps[3], "GeneratorTrip", t3_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
TextBox{parent=rcs_tags,text="T3",width=2,fg_bg=hc_text}
|
||||||
local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update)
|
t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update)
|
||||||
end
|
end
|
||||||
@@ -394,7 +394,7 @@ local function init(parent, id)
|
|||||||
|
|
||||||
reset.register(u_ps, "rps_tripped", function (active) if active then reset.enable() else reset.disable() end end)
|
reset.register(u_ps, "rps_tripped", function (active) if active then reset.enable() else reset.disable() end end)
|
||||||
|
|
||||||
TextBox{parent=main,text="WASTE PROCESSING",fg_bg=cpair(colors.black,colors.brown),alignment=ALIGN.CENTER,width=33,height=1,x=46,y=48}
|
TextBox{parent=main,text="WASTE PROCESSING",fg_bg=cpair(colors.black,colors.brown),alignment=ALIGN.CENTER,width=33,x=46,y=48}
|
||||||
local waste_proc = Rectangle{parent=main,border=border(1,colors.brown,true),thin=true,width=33,height=3,x=46,y=49}
|
local waste_proc = Rectangle{parent=main,border=border(1,colors.brown,true),thin=true,width=33,height=3,x=46,y=49}
|
||||||
local waste_div = Div{parent=waste_proc,x=2,y=1,width=31,height=1}
|
local waste_div = Div{parent=waste_proc,x=2,y=1,width=31,height=1}
|
||||||
|
|
||||||
@@ -474,15 +474,15 @@ local function init(parent, id)
|
|||||||
|
|
||||||
-- color tags
|
-- color tags
|
||||||
|
|
||||||
TextBox{parent=alarm_panel,x=5,y=13,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.cyan)}
|
TextBox{parent=alarm_panel,x=5,y=13,text="\x95",width=1,fg_bg=cpair(s_hi_bright.bkg,colors.cyan)}
|
||||||
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
||||||
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
-- automatic control settings --
|
-- automatic control settings --
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
TextBox{parent=main,text="AUTO CTRL",fg_bg=cpair(colors.black,colors.purple),alignment=ALIGN.CENTER,width=13,height=1,x=32,y=36}
|
TextBox{parent=main,text="AUTO CTRL",fg_bg=cpair(colors.black,colors.purple),alignment=ALIGN.CENTER,width=13,x=32,y=36}
|
||||||
local auto_ctl = Rectangle{parent=main,border=border(1,colors.purple,true),thin=true,width=13,height=15,x=32,y=37}
|
local auto_ctl = Rectangle{parent=main,border=border(1,colors.purple,true),thin=true,width=13,height=15,x=32,y=37}
|
||||||
local auto_div = Div{parent=auto_ctl,width=13,height=15,x=1,y=1}
|
local auto_div = Div{parent=auto_ctl,width=13,height=15,x=1,y=1}
|
||||||
|
|
||||||
@@ -499,8 +499,8 @@ local function init(parent, id)
|
|||||||
|
|
||||||
auto_div.line_break()
|
auto_div.line_break()
|
||||||
|
|
||||||
TextBox{parent=auto_div,text="Prio. Group",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=auto_div,text="Prio. Group",width=11,fg_bg=style.label}
|
||||||
local auto_grp = TextBox{parent=auto_div,text="Manual",height=1,width=11,fg_bg=s_field}
|
local auto_grp = TextBox{parent=auto_div,text="Manual",width=11,fg_bg=s_field}
|
||||||
|
|
||||||
auto_grp.register(u_ps, "auto_group", auto_grp.set_value)
|
auto_grp.register(u_ps, "auto_group", auto_grp.set_value)
|
||||||
|
|
||||||
|
|||||||
@@ -73,10 +73,10 @@ local function make(parent, x, y, wide, unit)
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
local reactor = Rectangle{parent=root,x=1,y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
local reactor = Rectangle{parent=root,x=1,y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
||||||
TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
TextBox{parent=root,x=3,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray}
|
TextBox{parent=root,x=3,y=5,text="\x19",width=1,fg_bg=lg_gray}
|
||||||
|
|
||||||
local rc_pipes = {}
|
local rc_pipes = {}
|
||||||
|
|
||||||
@@ -113,8 +113,8 @@ local function make(parent, x, y, wide, unit)
|
|||||||
hc_rate.register(unit.unit_ps, "heating_rate", hc_rate.update)
|
hc_rate.register(unit.unit_ps, "heating_rate", hc_rate.update)
|
||||||
|
|
||||||
local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
||||||
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=boiler,y=3,text=util.trinary(unit.num_boilers>1,"BOILERS","BOILER"),alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=boiler,y=3,text=util.trinary(unit.num_boilers>1,"BOILERS","BOILER"),alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
|
|
||||||
@@ -132,13 +132,13 @@ local function make(parent, x, y, wide, unit)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
||||||
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=ALIGN.CENTER}
|
||||||
TextBox{parent=root,x=_wide(93,79),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(93,79),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
|
|
||||||
for i = 1, unit.num_turbines do
|
for i = 1, unit.num_turbines do
|
||||||
local ry = 1 + (2 * (i - 1)) + prv_yo
|
local ry = 1 + (2 * (i - 1)) + prv_yo
|
||||||
TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1}
|
TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3}
|
||||||
local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=style.ind_bkg,c2=style.ind_yel.fgd,c3=style.ind_red.fgd}
|
local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=style.ind_bkg,c2=style.ind_yel.fgd,c3=style.ind_red.fgd}
|
||||||
state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update)
|
state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update)
|
||||||
end
|
end
|
||||||
@@ -172,7 +172,7 @@ local function make(parent, x, y, wide, unit)
|
|||||||
PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=style.theme.bg}
|
PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=style.theme.bg}
|
||||||
|
|
||||||
local function _valve(vx, vy, n)
|
local function _valve(vx, vy, n)
|
||||||
TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1}
|
TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2}
|
||||||
local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=ind_grn}
|
local conn = IndicatorLight{parent=waste,x=vx-3,y=vy+1,label=v_names[n],colors=ind_grn}
|
||||||
local open = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="OPEN",colors=ind_wht}
|
local open = IndicatorLight{parent=waste,x=vx-3,y=vy+2,label="OPEN",colors=ind_wht}
|
||||||
conn.register(unit.unit_ps, util.c("V_", v_fields[n], "_conn"), conn.update)
|
conn.register(unit.unit_ps, util.c("V_", v_fields[n], "_conn"), conn.update)
|
||||||
@@ -181,8 +181,8 @@ local function make(parent, x, y, wide, unit)
|
|||||||
|
|
||||||
local function _machine(mx, my, name)
|
local function _machine(mx, my, name)
|
||||||
local l = string.len(name) + 2
|
local l = string.len(name) + 2
|
||||||
TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=ALIGN.CENTER,fg_bg=cpair(style.theme.bg,style.theme.header.bkg),width=l,height=1}
|
TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=ALIGN.CENTER,fg_bg=cpair(style.theme.bg,style.theme.header.bkg),width=l}
|
||||||
TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=ALIGN.CENTER,fg_bg=style.theme.header,width=l,height=1}
|
TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=ALIGN.CENTER,fg_bg=style.theme.header,width=l}
|
||||||
end
|
end
|
||||||
|
|
||||||
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
||||||
@@ -209,7 +209,7 @@ local function make(parent, x, y, wide, unit)
|
|||||||
_machine(_wide(97, 83), 4, "PRC [Po] \x1a");
|
_machine(_wide(97, 83), 4, "PRC [Po] \x1a");
|
||||||
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
||||||
|
|
||||||
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
||||||
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=style.theme.highlight_box_bright}
|
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=style.theme.highlight_box_bright}
|
||||||
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
||||||
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c_d,label="CNT",unit="",format="%2d",value=0,width=7}
|
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c_d,label="CNT",unit="",format="%2d",value=0,width=7}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ local function make(parent, x, y, unit)
|
|||||||
local root = Div{parent=parent,x=x,y=y,width=80,height=height}
|
local root = Div{parent=parent,x=x,y=y,width=80,height=height}
|
||||||
|
|
||||||
-- unit header message
|
-- unit header message
|
||||||
TextBox{parent=root,text="Unit #"..unit.unit_id,alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
TextBox{parent=root,text="Unit #"..unit.unit_id,alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
-- REACTOR --
|
-- REACTOR --
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ local function init(main)
|
|||||||
local tank_list = facility.tank_list
|
local tank_list = facility.tank_list
|
||||||
|
|
||||||
-- window header message
|
-- window header message
|
||||||
local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
||||||
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=style.theme.header}
|
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,fg_bg=style.theme.header}
|
||||||
|
|
||||||
datetime.register(facility.ps, "date_time", datetime.set_value)
|
datetime.register(facility.ps, "date_time", datetime.set_value)
|
||||||
|
|
||||||
@@ -265,7 +265,7 @@ local function init(main)
|
|||||||
if tank_defs[i] > 0 then
|
if tank_defs[i] > 0 then
|
||||||
local vy = 3 + y_ofs(i)
|
local vy = 3 + y_ofs(i)
|
||||||
|
|
||||||
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=text_col,width=2,height=1}
|
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=text_col,width=2}
|
||||||
|
|
||||||
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn}
|
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn}
|
||||||
local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht}
|
local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht}
|
||||||
@@ -292,21 +292,21 @@ local function init(main)
|
|||||||
|
|
||||||
local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14}
|
local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14}
|
||||||
|
|
||||||
TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=style.lg_gray}
|
TextBox{parent=tank,text=" ",x=1,y=1,fg_bg=style.lg_gray}
|
||||||
TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=ALIGN.CENTER,height=1,fg_bg=style.wh_gray}
|
TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=ALIGN.CENTER,fg_bg=style.wh_gray}
|
||||||
|
|
||||||
local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12}
|
local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12}
|
||||||
|
|
||||||
local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14}
|
local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14}
|
||||||
|
|
||||||
TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label}
|
TextBox{parent=tank_box,x=2,y=3,text="Fill",width=10,fg_bg=style.label}
|
||||||
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
|
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
|
||||||
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=16,fg_bg=s_field}
|
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=16,fg_bg=s_field}
|
||||||
|
|
||||||
TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=tank_box,x=2,y=6,text="Water Level",width=11,fg_bg=style.label}
|
||||||
local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
|
local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
|
||||||
|
|
||||||
TextBox{parent=tank_box,x=2,y=9,text="In/Out Mode",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=tank_box,x=2,y=9,text="In/Out Mode",width=11,fg_bg=style.label}
|
||||||
local can_fill = IndicatorLight{parent=tank_box,x=2,y=10,label="FILL",colors=style.ind_wht}
|
local can_fill = IndicatorLight{parent=tank_box,x=2,y=10,label="FILL",colors=style.ind_wht}
|
||||||
local can_empty = IndicatorLight{parent=tank_box,x=10,y=10,label="EMPTY",colors=style.ind_wht}
|
local can_empty = IndicatorLight{parent=tank_box,x=10,y=10,label="EMPTY",colors=style.ind_wht}
|
||||||
|
|
||||||
@@ -344,8 +344,8 @@ local function init(main)
|
|||||||
|
|
||||||
local sps = Div{parent=main,x=140,y=3,height=12}
|
local sps = Div{parent=main,x=140,y=3,height=12}
|
||||||
|
|
||||||
TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=style.lg_gray}
|
TextBox{parent=sps,text=" ",width=24,x=1,y=1,fg_bg=style.lg_gray}
|
||||||
TextBox{parent=sps,text="SPS",alignment=ALIGN.CENTER,width=24,height=1,fg_bg=wh_gray}
|
TextBox{parent=sps,text="SPS",alignment=ALIGN.CENTER,width=24,fg_bg=wh_gray}
|
||||||
|
|
||||||
local sps_box = Rectangle{parent=sps,border=border(1,colors.gray,true),width=24,height=10}
|
local sps_box = Rectangle{parent=sps,border=border(1,colors.gray,true),width=24,height=10}
|
||||||
|
|
||||||
@@ -353,12 +353,12 @@ local function init(main)
|
|||||||
|
|
||||||
status.register(facility.sps_ps_tbl[1], "computed_status", status.update)
|
status.register(facility.sps_ps_tbl[1], "computed_status", status.update)
|
||||||
|
|
||||||
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label}
|
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",width=10,fg_bg=style.label}
|
||||||
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
||||||
|
|
||||||
sps_in.register(facility.ps, "po_am_rate", sps_in.update)
|
sps_in.register(facility.ps, "po_am_rate", sps_in.update)
|
||||||
|
|
||||||
TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label}
|
TextBox{parent=sps_box,x=2,y=6,text="Production Rate",width=15,fg_bg=style.label}
|
||||||
local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
||||||
|
|
||||||
sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end)
|
sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end)
|
||||||
@@ -367,13 +367,13 @@ local function init(main)
|
|||||||
-- statistics --
|
-- statistics --
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=16,text="RAW WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=16,text="RAW WASTE",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
||||||
local raw_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
local raw_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
||||||
local sum_raw_waste = DataIndicator{parent=raw_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.2f",value=0,width=17}
|
local sum_raw_waste = DataIndicator{parent=raw_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.2f",value=0,width=17}
|
||||||
|
|
||||||
sum_raw_waste.register(facility.ps, "burn_sum", sum_raw_waste.update)
|
sum_raw_waste.register(facility.ps, "burn_sum", sum_raw_waste.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=21,text="PROC. WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=21,text="PROC. WASTE",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
||||||
local pr_waste = Rectangle{parent=main,x=145,y=22,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=s_hi_bright}
|
local pr_waste = Rectangle{parent=main,x=145,y=22,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=s_hi_bright}
|
||||||
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
|
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
|
||||||
local po = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Po",unit="mB/t",format="%9.2f",value=0,width=17}
|
local po = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Po",unit="mB/t",format="%9.2f",value=0,width=17}
|
||||||
@@ -383,7 +383,7 @@ local function init(main)
|
|||||||
po.register(facility.ps, "po_rate", po.update)
|
po.register(facility.ps, "po_rate", po.update)
|
||||||
popl.register(facility.ps, "po_pl_rate", popl.update)
|
popl.register(facility.ps, "po_pl_rate", popl.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=28,text="SPENT WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=28,text="SPENT WASTE",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
||||||
local sp_waste = Rectangle{parent=main,x=145,y=29,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
local sp_waste = Rectangle{parent=main,x=145,y=29,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
||||||
local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17}
|
local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17}
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ local led_grn = style.led_grn
|
|||||||
local function init(panel, num_units)
|
local function init(panel, num_units)
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.fp_theme.header}
|
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,fg_bg=style.fp_theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ local function init(panel, num_units)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp.disabled_fg}
|
TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=style.fp.disabled_fg}
|
||||||
|
|
||||||
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
|
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
|
||||||
|
|
||||||
@@ -132,8 +132,8 @@ local function init(panel, num_units)
|
|||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_v.register(ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ local function init(main)
|
|||||||
local units = iocontrol.get_db().units
|
local units = iocontrol.get_db().units
|
||||||
|
|
||||||
-- window header message
|
-- window header message
|
||||||
local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=ALIGN.CENTER,height=1,fg_bg=s_header}
|
local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=ALIGN.CENTER,fg_bg=s_header}
|
||||||
local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=style.lg_white,width=12,fg_bg=s_header}
|
local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=style.lg_white,width=12,fg_bg=s_header}
|
||||||
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
||||||
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=s_header}
|
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,fg_bg=s_header}
|
||||||
|
|
||||||
ping.register(facility.ps, "sv_ping", ping.update)
|
ping.register(facility.ps, "sv_ping", ping.update)
|
||||||
datetime.register(facility.ps, "date_time", datetime.set_value)
|
datetime.register(facility.ps, "date_time", datetime.set_value)
|
||||||
@@ -79,7 +79,7 @@ local function init(main)
|
|||||||
|
|
||||||
assert(cnc_bottom_align_start >= cnc_y_start, "main display not of sufficient vertical resolution (add an additional row of monitors)")
|
assert(cnc_bottom_align_start >= cnc_y_start, "main display not of sufficient vertical resolution (add an additional row of monitors)")
|
||||||
|
|
||||||
TextBox{parent=main,y=cnc_bottom_align_start,text=string.rep("\x8c", header.get_width()),alignment=ALIGN.CENTER,height=1,fg_bg=style.lg_gray}
|
TextBox{parent=main,y=cnc_bottom_align_start,text=string.rep("\x8c", header.get_width()),alignment=ALIGN.CENTER,fg_bg=style.lg_gray}
|
||||||
|
|
||||||
cnc_bottom_align_start = cnc_bottom_align_start + 2
|
cnc_bottom_align_start = cnc_bottom_align_start + 2
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
|
|||||||
|
|
||||||
local core = {}
|
local core = {}
|
||||||
|
|
||||||
core.version = "2.2.4"
|
core.version = "2.3.1"
|
||||||
|
|
||||||
core.flasher = flasher
|
core.flasher = flasher
|
||||||
core.events = events
|
core.events = events
|
||||||
|
|||||||
@@ -82,9 +82,10 @@ end
|
|||||||
-- a base graphics element, should not be created on its own
|
-- a base graphics element, should not be created on its own
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param args graphics_args arguments
|
---@param args graphics_args arguments
|
||||||
|
---@param constraint? function apply a dimensional constraint based on proposed dimensions function(frame) -> width, height
|
||||||
---@param child_offset_x? integer mouse event offset x
|
---@param child_offset_x? integer mouse event offset x
|
||||||
---@param child_offset_y? integer mouse event offset y
|
---@param child_offset_y? integer mouse event offset y
|
||||||
function element.new(args, child_offset_x, child_offset_y)
|
function element.new(args, constraint, child_offset_x, child_offset_y)
|
||||||
local self = {
|
local self = {
|
||||||
id = nil, ---@type element_id|nil
|
id = nil, ---@type element_id|nil
|
||||||
is_root = args.parent == nil,
|
is_root = args.parent == nil,
|
||||||
@@ -199,7 +200,7 @@ function element.new(args, child_offset_x, child_offset_y)
|
|||||||
---@param next_y integer next line if no y was provided
|
---@param next_y integer next line if no y was provided
|
||||||
function protected.prepare_template(offset_x, offset_y, next_y)
|
function protected.prepare_template(offset_x, offset_y, next_y)
|
||||||
-- don't auto incrememnt y if inheriting height, that would cause an assertion
|
-- don't auto incrememnt y if inheriting height, that would cause an assertion
|
||||||
next_y = util.trinary(args.height == nil, 1, next_y)
|
next_y = util.trinary(args.height == nil and constraint == nil, 1, next_y)
|
||||||
|
|
||||||
-- record offsets in case there is a reposition
|
-- record offsets in case there is a reposition
|
||||||
self.offset_x = offset_x
|
self.offset_x = offset_x
|
||||||
@@ -226,6 +227,13 @@ function element.new(args, child_offset_x, child_offset_y)
|
|||||||
local w, h = self.p_window.getSize()
|
local w, h = self.p_window.getSize()
|
||||||
f.w = math.min(f.w, w - (f.x - 1))
|
f.w = math.min(f.w, w - (f.x - 1))
|
||||||
f.h = math.min(f.h, h - (f.y - 1))
|
f.h = math.min(f.h, h - (f.y - 1))
|
||||||
|
|
||||||
|
if type(constraint) == "function" then
|
||||||
|
-- constrain per provided constraint function (can only get smaller than available space)
|
||||||
|
w, h = constraint(f)
|
||||||
|
f.w = math.min(f.w, w)
|
||||||
|
f.h = math.min(f.h, h)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check frame
|
-- check frame
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
-- Button Graphics Element
|
-- Button Graphics Element
|
||||||
|
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
local element = require("graphics.element")
|
local element = require("graphics.element")
|
||||||
@@ -21,7 +22,6 @@ local KEY_CLICK = core.events.KEY_CLICK
|
|||||||
---@field id? string element id
|
---@field id? string element id
|
||||||
---@field x? integer 1 if omitted
|
---@field x? integer 1 if omitted
|
||||||
---@field y? integer auto incremented if omitted
|
---@field y? integer auto incremented if omitted
|
||||||
---@field height? integer parent height if omitted
|
|
||||||
---@field fg_bg? cpair foreground/background colors
|
---@field fg_bg? cpair foreground/background colors
|
||||||
---@field hidden? boolean true to hide on initial draw
|
---@field hidden? boolean true to hide on initial draw
|
||||||
|
|
||||||
@@ -38,29 +38,40 @@ local function push_button(args)
|
|||||||
|
|
||||||
-- set automatic settings
|
-- set automatic settings
|
||||||
args.can_focus = true
|
args.can_focus = true
|
||||||
args.height = 1
|
|
||||||
args.min_width = args.min_width or 0
|
args.min_width = args.min_width or 0
|
||||||
args.width = math.max(text_width, args.min_width)
|
args.width = math.max(text_width, args.min_width)
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- provide a constraint condition to element creation to prefer a single line button
|
||||||
local e = element.new(args)
|
---@param frame graphics_frame
|
||||||
|
local function constrain(frame)
|
||||||
local h_pad = 1
|
return frame.w, math.max(1, #util.strwrap(args.text, frame.w))
|
||||||
local v_pad = math.floor(e.frame.h / 2) + 1
|
|
||||||
|
|
||||||
if alignment == ALIGN.CENTER then
|
|
||||||
h_pad = math.floor((e.frame.w - text_width) / 2) + 1
|
|
||||||
elseif alignment == ALIGN.RIGHT then
|
|
||||||
h_pad = (e.frame.w - text_width) + 1
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- create new graphics element base object
|
||||||
|
local e = element.new(args, constrain)
|
||||||
|
|
||||||
|
local text_lines = util.strwrap(args.text, e.frame.w)
|
||||||
|
|
||||||
-- draw the button
|
-- draw the button
|
||||||
function e.redraw()
|
function e.redraw()
|
||||||
e.window.clear()
|
e.window.clear()
|
||||||
|
|
||||||
-- write the button text
|
for i = 1, #text_lines do
|
||||||
e.w_set_cur(h_pad, v_pad)
|
if i > e.frame.h then break end
|
||||||
e.w_write(args.text)
|
|
||||||
|
local len = string.len(text_lines[i])
|
||||||
|
|
||||||
|
-- use cursor position to align this line
|
||||||
|
if alignment == ALIGN.CENTER then
|
||||||
|
e.w_set_cur(math.floor((e.frame.w - len) / 2) + 1, i)
|
||||||
|
elseif alignment == ALIGN.RIGHT then
|
||||||
|
e.w_set_cur((e.frame.w - len) + 1, i)
|
||||||
|
else
|
||||||
|
e.w_set_cur(1, i)
|
||||||
|
end
|
||||||
|
|
||||||
|
e.w_write(text_lines[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- draw the button as pressed (if active_fg_bg set)
|
-- draw the button as pressed (if active_fg_bg set)
|
||||||
@@ -109,7 +120,9 @@ local function push_button(args)
|
|||||||
if event.type == KEY_CLICK.DOWN then
|
if event.type == KEY_CLICK.DOWN then
|
||||||
if event.key == keys.space or event.key == keys.enter or event.key == keys.numPadEnter then
|
if event.key == keys.space or event.key == keys.enter or event.key == keys.numPadEnter then
|
||||||
args.callback()
|
args.callback()
|
||||||
e.defocus()
|
-- visualize click without unfocusing
|
||||||
|
show_unpressed()
|
||||||
|
if args.active_fg_bg ~= nil then tcd.dispatch(0.25, show_pressed) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ local element = require("graphics.element")
|
|||||||
|
|
||||||
---@class power_indicator_args
|
---@class power_indicator_args
|
||||||
---@field label string indicator label
|
---@field label string indicator label
|
||||||
|
---@field unit string energy unit
|
||||||
---@field format string power format override (lua string format)
|
---@field format string power format override (lua string format)
|
||||||
---@field rate boolean? whether to append /t to the end (power per tick)
|
---@field rate boolean? whether to append /t to the end (power per tick)
|
||||||
---@field lu_colors? cpair label foreground color (a), unit foreground color (b)
|
---@field lu_colors? cpair label foreground color (a), unit foreground color (b)
|
||||||
@@ -23,6 +24,8 @@ local element = require("graphics.element")
|
|||||||
---@param args power_indicator_args
|
---@param args power_indicator_args
|
||||||
---@return graphics_element element, element_id id
|
---@return graphics_element element, element_id id
|
||||||
local function power(args)
|
local function power(args)
|
||||||
|
element.assert(type(args.label) == "string", "label is a required field")
|
||||||
|
element.assert(type(args.unit) == "string", "unit is a required field")
|
||||||
element.assert(type(args.value) == "number", "value is a required field")
|
element.assert(type(args.value) == "number", "value is a required field")
|
||||||
element.assert(util.is_int(args.width), "width is a required field")
|
element.assert(util.is_int(args.width), "width is a required field")
|
||||||
|
|
||||||
@@ -40,7 +43,7 @@ local function power(args)
|
|||||||
function e.on_update(value)
|
function e.on_update(value)
|
||||||
e.value = value
|
e.value = value
|
||||||
|
|
||||||
local data_str, unit = util.power_format(value, false, args.format)
|
local data_str, unit = util.power_format(value, args.unit, false, args.format)
|
||||||
|
|
||||||
-- write data
|
-- write data
|
||||||
e.w_set_cur(data_start, 1)
|
e.w_set_cur(data_start, 1)
|
||||||
@@ -53,14 +56,13 @@ local function power(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- append per tick if rate is set
|
-- append per tick if rate is set
|
||||||
-- add space to FE so we don't end up with FEE (after having kFE for example)
|
|
||||||
if args.rate == true then
|
if args.rate == true then
|
||||||
unit = unit .. "/t"
|
unit = unit .. "/t"
|
||||||
if unit == "FE/t" then unit = "FE/t " end
|
|
||||||
else
|
|
||||||
if unit == "FE" then unit = "FE " end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- add space to unit so we don't end up with something like FEE after having kFE
|
||||||
|
unit = util.strminw(unit, 5)
|
||||||
|
|
||||||
e.w_write(" " .. unit)
|
e.w_write(" " .. unit)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ local function rectangle(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- create new graphics element base object
|
||||||
local e = element.new(args, offset_x, offset_y)
|
local e = element.new(args, nil, offset_x, offset_y)
|
||||||
|
|
||||||
-- create content window for child elements
|
-- create content window for child elements
|
||||||
e.content_window = window.create(e.window, 1 + offset_x, 1 + offset_y, e.frame.w - (2 * offset_x), e.frame.h - (2 * offset_y))
|
e.content_window = window.create(e.window, 1 + offset_x, 1 + offset_y, e.frame.w - (2 * offset_x), e.frame.h - (2 * offset_y))
|
||||||
|
|||||||
@@ -10,12 +10,13 @@ local ALIGN = core.ALIGN
|
|||||||
---@class textbox_args
|
---@class textbox_args
|
||||||
---@field text string text to show
|
---@field text string text to show
|
||||||
---@field alignment? ALIGN text alignment, left by default
|
---@field alignment? ALIGN text alignment, left by default
|
||||||
|
---@field anchor? boolean true to use this as an anchor, making it focusable
|
||||||
---@field parent graphics_element
|
---@field parent graphics_element
|
||||||
---@field id? string element id
|
---@field id? string element id
|
||||||
---@field x? integer 1 if omitted
|
---@field x? integer 1 if omitted
|
||||||
---@field y? integer auto incremented if omitted
|
---@field y? integer auto incremented if omitted
|
||||||
---@field width? integer parent width if omitted
|
---@field width? integer parent width if omitted
|
||||||
---@field height? integer parent height if omitted
|
---@field height? integer minimum necessary height for wrapped text if omitted
|
||||||
---@field gframe? graphics_frame frame instead of x/y/width/height
|
---@field gframe? graphics_frame frame instead of x/y/width/height
|
||||||
---@field fg_bg? cpair foreground/background colors
|
---@field fg_bg? cpair foreground/background colors
|
||||||
---@field hidden? boolean true to hide on initial draw
|
---@field hidden? boolean true to hide on initial draw
|
||||||
@@ -26,8 +27,22 @@ local ALIGN = core.ALIGN
|
|||||||
local function textbox(args)
|
local function textbox(args)
|
||||||
element.assert(type(args.text) == "string", "text is a required field")
|
element.assert(type(args.text) == "string", "text is a required field")
|
||||||
|
|
||||||
|
if args.anchor == true then args.can_focus = true end
|
||||||
|
|
||||||
|
-- provide a constraint condition to element creation to prevent an pointlessly tall text box
|
||||||
|
---@param frame graphics_frame
|
||||||
|
local function constrain(frame)
|
||||||
|
local new_height = math.max(1, #util.strwrap(args.text, frame.w))
|
||||||
|
|
||||||
|
if args.height then
|
||||||
|
new_height = math.max(frame.h, new_height)
|
||||||
|
end
|
||||||
|
|
||||||
|
return frame.w, new_height
|
||||||
|
end
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- create new graphics element base object
|
||||||
local e = element.new(args)
|
local e = element.new(args, constrain)
|
||||||
|
|
||||||
e.value = args.text
|
e.value = args.text
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local tcd = require("scada-common.tcd")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
@@ -32,7 +32,10 @@ local CENTER = core.ALIGN.CENTER
|
|||||||
local RIGHT = core.ALIGN.RIGHT
|
local RIGHT = core.ALIGN.RIGHT
|
||||||
|
|
||||||
-- changes to the config data/format to let the user know
|
-- changes to the config data/format to let the user know
|
||||||
local changes = {}
|
local changes = {
|
||||||
|
{ "v0.9.2", { "Added temperature scale options" } },
|
||||||
|
{ "v0.11.3", { "Added energy scale options" } }
|
||||||
|
}
|
||||||
|
|
||||||
---@class pkt_configurator
|
---@class pkt_configurator
|
||||||
local configurator = {}
|
local configurator = {}
|
||||||
@@ -73,6 +76,8 @@ local tool_ctl = {
|
|||||||
|
|
||||||
---@class pkt_config
|
---@class pkt_config
|
||||||
local tmp_cfg = {
|
local tmp_cfg = {
|
||||||
|
TempScale = 1,
|
||||||
|
EnergyScale = 1,
|
||||||
SVR_Channel = nil, ---@type integer
|
SVR_Channel = nil, ---@type integer
|
||||||
CRD_Channel = nil, ---@type integer
|
CRD_Channel = nil, ---@type integer
|
||||||
PKT_Channel = nil, ---@type integer
|
PKT_Channel = nil, ---@type integer
|
||||||
@@ -91,6 +96,8 @@ local settings_cfg = {}
|
|||||||
|
|
||||||
-- all settings fields, their nice names, and their default values
|
-- all settings fields, their nice names, and their default values
|
||||||
local fields = {
|
local fields = {
|
||||||
|
{ "TempScale", "Temperature Scale", types.TEMP_SCALE.KELVIN },
|
||||||
|
{ "EnergyScale", "Energy Scale", types.ENERGY_SCALE.FE },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "CRD_Channel", "CRD Channel", 16243 },
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
||||||
{ "PKT_Channel", "PKT Channel", 16244 },
|
{ "PKT_Channel", "PKT Channel", 16244 },
|
||||||
@@ -121,17 +128,18 @@ local function config_view(display)
|
|||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local function exit() os.queueEvent("terminate") end
|
local function exit() os.queueEvent("terminate") end
|
||||||
|
|
||||||
TextBox{parent=display,y=1,text="Pocket Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=display,y=1,text="Pocket Configurator",alignment=CENTER,fg_bg=style.header}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=display,x=1,y=2}
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
||||||
|
|
||||||
local main_page = Div{parent=root_pane_div,x=1,y=1}
|
local main_page = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
local ui_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local net_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local net_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local summary = Div{parent=root_pane_div,x=1,y=1}
|
local summary = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
|
||||||
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,log_cfg,summary,changelog}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,ui_cfg,net_cfg,log_cfg,summary,changelog}}
|
||||||
|
|
||||||
-- Main Page
|
-- Main Page
|
||||||
|
|
||||||
@@ -148,7 +156,7 @@ local function config_view(display)
|
|||||||
tool_ctl.viewing_config = true
|
tool_ctl.viewing_config = true
|
||||||
tool_ctl.gen_summary(settings_cfg)
|
tool_ctl.gen_summary(settings_cfg)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
main_pane.set_value(4)
|
main_pane.set_value(5)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists("/pocket/config.lua") then
|
if fs.exists("/pocket/config.lua") then
|
||||||
@@ -162,7 +170,32 @@ local function config_view(display)
|
|||||||
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
||||||
|
|
||||||
PushButton{parent=main_page,x=2,y=18,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=18,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=main_page,x=14,y=18,min_width=12,text="Change Log",callback=function()main_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=14,y=18,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#region Pocket UI
|
||||||
|
|
||||||
|
local ui_c_1 = Div{parent=ui_cfg,x=2,y=4,width=24}
|
||||||
|
|
||||||
|
TextBox{parent=ui_cfg,x=1,y=2,text=" Pocket UI",fg_bg=cpair(colors.black,colors.lime)}
|
||||||
|
|
||||||
|
TextBox{parent=ui_c_1,x=1,y=1,height=3,text="You may customize units below."}
|
||||||
|
|
||||||
|
TextBox{parent=ui_c_1,x=1,y=4,text="Temperature Scale"}
|
||||||
|
local temp_scale = RadioButton{parent=ui_c_1,x=1,y=5,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
|
TextBox{parent=ui_c_1,x=1,y=10,text="Energy Scale"}
|
||||||
|
local energy_scale = RadioButton{parent=ui_c_1,x=1,y=11,default=ini_cfg.EnergyScale,options=types.ENERGY_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
|
local function submit_ui_opts()
|
||||||
|
tmp_cfg.TempScale = temp_scale.get_value()
|
||||||
|
tmp_cfg.EnergyScale = energy_scale.get_value()
|
||||||
|
main_pane.set_value(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=ui_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=ui_c_1,x=19,y=15,text="Next \x1a",callback=submit_ui_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Network
|
--#region Network
|
||||||
|
|
||||||
@@ -173,24 +206,24 @@ local function config_view(display)
|
|||||||
|
|
||||||
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
||||||
|
|
||||||
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Set network channels."}
|
TextBox{parent=net_c_1,x=1,y=1,text="Set network channels."}
|
||||||
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the named channels must be the same within a particular SCADA network.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the named channels must be the same within a particular SCADA network.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"}
|
TextBox{parent=net_c_1,x=1,y=8,width=18,text="Supervisor Channel"}
|
||||||
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="Coordinator Channel"}
|
TextBox{parent=net_c_1,x=1,y=10,width=19,text="Coordinator Channel"}
|
||||||
local crd_chan = NumberField{parent=net_c_1,x=1,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local crd_chan = NumberField{parent=net_c_1,x=1,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=9,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=9,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"}
|
TextBox{parent=net_c_1,x=1,y=12,width=14,text="Pocket Channel"}
|
||||||
local pkt_chan = NumberField{parent=net_c_1,x=1,y=13,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local pkt_chan = NumberField{parent=net_c_1,x=1,y=13,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=9,y=13,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=9,y=13,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local chan_err = TextBox{parent=net_c_1,x=1,y=14,height=1,width=24,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local chan_err = TextBox{parent=net_c_1,x=1,y=14,width=24,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_channels()
|
local function submit_channels()
|
||||||
local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
local svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
||||||
@@ -201,18 +234,18 @@ local function config_view(display)
|
|||||||
else chan_err.show() end
|
else chan_err.show() end
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=net_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_1,x=19,y=15,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=19,y=15,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Set connection timeout."}
|
TextBox{parent=net_c_2,x=1,y=1,text="Set connection timeout."}
|
||||||
TextBox{parent=net_c_2,x=1,y=3,height=7,text="You generally should not need to modify this. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=1,y=3,height=7,text="You generally should not need to modify this. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=11,height=1,width=19,text="Connection Timeout"}
|
TextBox{parent=net_c_2,x=1,y=11,width=19,text="Connection Timeout"}
|
||||||
local timeout = NumberField{parent=net_c_2,x=1,y=12,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local timeout = NumberField{parent=net_c_2,x=1,y=12,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=9,y=12,height=2,text="seconds\n(default 5)",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=9,y=12,height=2,text="seconds\n(default 5)",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local ct_err = TextBox{parent=net_c_2,x=1,y=14,height=1,width=24,text="Please set timeout.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local ct_err = TextBox{parent=net_c_2,x=1,y=14,width=24,text="Please set timeout.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_timeouts()
|
local function submit_timeouts()
|
||||||
local timeout_val = tonumber(timeout.get_value())
|
local timeout_val = tonumber(timeout.get_value())
|
||||||
@@ -226,13 +259,13 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_2,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=1,y=15,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_2,x=19,y=15,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=19,y=15,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=1,height=1,text="Set the trusted range."}
|
TextBox{parent=net_c_3,x=1,y=1,text="Set the trusted range."}
|
||||||
TextBox{parent=net_c_3,x=1,y=3,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many blocks away.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=3,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many blocks away.",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=net_c_3,x=1,y=8,height=4,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=8,height=4,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local range = NumberField{parent=net_c_3,x=1,y=13,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
local range = NumberField{parent=net_c_3,x=1,y=13,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local tr_err = TextBox{parent=net_c_3,x=1,y=14,height=1,width=24,text="Set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local tr_err = TextBox{parent=net_c_3,x=1,y=14,width=24,text="Set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_tr()
|
local function submit_tr()
|
||||||
local range_val = tonumber(range.get_value())
|
local range_val = tonumber(range.get_value())
|
||||||
@@ -249,7 +282,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_4,x=1,y=1,height=4,text="Optionally, set the facility authentication key. Do NOT use one of your passwords."}
|
TextBox{parent=net_c_4,x=1,y=1,height=4,text="Optionally, set the facility authentication key. Do NOT use one of your passwords."}
|
||||||
TextBox{parent=net_c_4,x=1,y=6,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_4,x=1,y=6,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_4,x=1,y=12,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_4,x=1,y=12,text="Facility Auth Key"}
|
||||||
local key, _, censor = TextField{parent=net_c_4,x=1,y=13,max_len=64,value=ini_cfg.AuthKey,width=24,height=1,fg_bg=bw_fg_bg}
|
local key, _, censor = TextField{parent=net_c_4,x=1,y=13,max_len=64,value=ini_cfg.AuthKey,width=24,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||||
@@ -262,13 +295,13 @@ local function config_view(display)
|
|||||||
hide_key.set_value(true)
|
hide_key.set_value(true)
|
||||||
censor_key(true)
|
censor_key(true)
|
||||||
|
|
||||||
local key_err = TextBox{parent=net_c_4,x=1,y=14,height=1,width=24,text="Length must be > 7.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local key_err = TextBox{parent=net_c_4,x=1,y=14,width=24,text="Length must be > 7.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_auth()
|
local function submit_auth()
|
||||||
local v = key.get_value()
|
local v = key.get_value()
|
||||||
if string.len(v) == 0 or string.len(v) >= 8 then
|
if string.len(v) == 0 or string.len(v) >= 8 then
|
||||||
tmp_cfg.AuthKey = key.get_value()
|
tmp_cfg.AuthKey = key.get_value()
|
||||||
main_pane.set_value(3)
|
main_pane.set_value(4)
|
||||||
key_err.hide(true)
|
key_err.hide(true)
|
||||||
else key_err.show() end
|
else key_err.show() end
|
||||||
end
|
end
|
||||||
@@ -281,20 +314,20 @@ local function config_view(display)
|
|||||||
|
|
||||||
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=24}
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=24}
|
||||||
|
|
||||||
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Configure logging below."}
|
TextBox{parent=log_c_1,x=1,y=1,text="Configure logging below."}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
TextBox{parent=log_c_1,x=1,y=3,text="Log File Mode"}
|
||||||
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
TextBox{parent=log_c_1,x=1,y=7,text="Log File Path"}
|
||||||
local path = TextField{parent=log_c_1,x=1,y=8,width=24,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
local path = TextField{parent=log_c_1,x=1,y=8,width=24,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
||||||
TextBox{parent=log_c_1,x=3,y=11,height=4,text="This results in much larger log files. Use only as needed.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=log_c_1,x=3,y=11,height=4,text="This results in much larger log files. Use only as needed.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local path_err = TextBox{parent=log_c_1,x=1,y=14,height=1,width=24,text="Provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local path_err = TextBox{parent=log_c_1,x=1,y=14,width=24,text="Provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_log()
|
local function submit_log()
|
||||||
if path.get_value() ~= "" then
|
if path.get_value() ~= "" then
|
||||||
@@ -306,11 +339,11 @@ local function config_view(display)
|
|||||||
tool_ctl.viewing_config = false
|
tool_ctl.viewing_config = false
|
||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
tool_ctl.settings_apply.show()
|
tool_ctl.settings_apply.show()
|
||||||
main_pane.set_value(4)
|
main_pane.set_value(5)
|
||||||
else path_err.show() end
|
else path_err.show() end
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=log_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=log_c_1,x=1,y=15,text="\x1b Back",callback=function()main_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=log_c_1,x=19,y=15,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=log_c_1,x=19,y=15,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
@@ -324,7 +357,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
||||||
|
|
||||||
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
TextBox{parent=summary,x=1,y=2,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
||||||
|
|
||||||
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=11,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=11,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -335,7 +368,7 @@ local function config_view(display)
|
|||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
tool_ctl.settings_apply.show()
|
tool_ctl.settings_apply.show()
|
||||||
else
|
else
|
||||||
main_pane.set_value(3)
|
main_pane.set_value(4)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -352,6 +385,8 @@ local function config_view(display)
|
|||||||
load_settings(settings_cfg, true)
|
load_settings(settings_cfg, true)
|
||||||
load_settings(ini_cfg)
|
load_settings(ini_cfg)
|
||||||
|
|
||||||
|
try_set(temp_scale, ini_cfg.TempScale)
|
||||||
|
try_set(energy_scale, ini_cfg.EnergyScale)
|
||||||
try_set(svr_chan, ini_cfg.SVR_Channel)
|
try_set(svr_chan, ini_cfg.SVR_Channel)
|
||||||
try_set(crd_chan, ini_cfg.CRD_Channel)
|
try_set(crd_chan, ini_cfg.CRD_Channel)
|
||||||
try_set(pkt_chan, ini_cfg.PKT_Channel)
|
try_set(pkt_chan, ini_cfg.PKT_Channel)
|
||||||
@@ -379,7 +414,7 @@ local function config_view(display)
|
|||||||
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=1,y=13,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=1,y=13,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
||||||
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=18,y=15,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=18,y=15,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=sum_c_2,x=1,y=1,text="Settings saved!"}
|
||||||
|
|
||||||
local function go_home()
|
local function go_home()
|
||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
@@ -410,15 +445,15 @@ local function config_view(display)
|
|||||||
|
|
||||||
local cl = Div{parent=changelog,x=2,y=4,width=24}
|
local cl = Div{parent=changelog,x=2,y=4,width=24}
|
||||||
|
|
||||||
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
TextBox{parent=changelog,x=1,y=2,text=" Config Change Log",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local c_log = ListBox{parent=cl,x=1,y=1,height=13,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local c_log = ListBox{parent=cl,x=1,y=1,height=13,width=24,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=c_log,text=change[1],fg_bg=bw_fg_bg}
|
||||||
for _, v in ipairs(change[2]) do
|
for _, v in ipairs(change[2]) do
|
||||||
local e = Div{parent=c_log,height=#util.strwrap(v,21)}
|
local e = Div{parent=c_log,height=#util.strwrap(v,21)}
|
||||||
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=1,text="- ",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -444,7 +479,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
main_pane.set_value(4)
|
main_pane.set_value(5)
|
||||||
tool_ctl.importing_legacy = true
|
tool_ctl.importing_legacy = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -473,8 +508,15 @@ local function config_view(display)
|
|||||||
local raw = cfg[f[1]]
|
local raw = cfg[f[1]]
|
||||||
local val = util.strval(raw)
|
local val = util.strval(raw)
|
||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
if f[1] == "AuthKey" then
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") end
|
val = string.rep("*", string.len(val))
|
||||||
|
elseif f[1] == "LogMode" then
|
||||||
|
val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
|
elseif f[1] == "TempScale" then
|
||||||
|
val = util.strval(types.TEMP_SCALE_NAMES[raw])
|
||||||
|
elseif f[1] == "EnergyScale" then
|
||||||
|
val = util.strval(types.ENERGY_SCALE_NAMES[raw])
|
||||||
|
end
|
||||||
|
|
||||||
if val == "nil" then val = "<not set>" end
|
if val == "nil" then val = "<not set>" end
|
||||||
|
|
||||||
@@ -532,9 +574,7 @@ function configurator.configure(ask_config)
|
|||||||
local event, param1, param2, param3 = util.pull_event()
|
local event, param1, param2, param3 = util.pull_event()
|
||||||
|
|
||||||
-- handle event
|
-- handle event
|
||||||
if event == "timer" then
|
if event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
||||||
tcd.handle(param1)
|
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
|
||||||
local m_e = core.events.new_mouse_event(event, param1, param2, param3)
|
local m_e = core.events.new_mouse_event(event, param1, param2, param3)
|
||||||
if m_e then display.handle_mouse(m_e) end
|
if m_e then display.handle_mouse(m_e) end
|
||||||
elseif event == "char" or event == "key" or event == "key_up" then
|
elseif event == "char" or event == "key" or event == "key_up" then
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
-- I/O Control for Pocket Integration with Supervisor & Coordinator
|
-- I/O Control for Pocket Integration with Supervisor & Coordinator
|
||||||
--
|
--
|
||||||
|
|
||||||
local log = require("scada-common.log")
|
local const = require("scada-common.constants")
|
||||||
|
-- local log = require("scada-common.log")
|
||||||
local psil = require("scada-common.psil")
|
local psil = require("scada-common.psil")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@@ -10,6 +11,11 @@ local util = require("scada-common.util")
|
|||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
local ALARM_STATE = types.ALARM_STATE
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
|
|
||||||
|
local ENERGY_SCALE = types.ENERGY_SCALE
|
||||||
|
local ENERGY_UNITS = types.ENERGY_SCALE_UNITS
|
||||||
|
local TEMP_SCALE = types.TEMP_SCALE
|
||||||
|
local TEMP_UNITS = types.TEMP_SCALE_UNITS
|
||||||
|
|
||||||
---@todo nominal trip time is ping (0ms to 10ms usually)
|
---@todo nominal trip time is ping (0ms to 10ms usually)
|
||||||
local WARN_TT = 40
|
local WARN_TT = 40
|
||||||
local HIGH_TT = 80
|
local HIGH_TT = 80
|
||||||
@@ -26,200 +32,22 @@ local LINK_STATE = {
|
|||||||
|
|
||||||
iocontrol.LINK_STATE = LINK_STATE
|
iocontrol.LINK_STATE = LINK_STATE
|
||||||
|
|
||||||
---@enum POCKET_APP_ID
|
|
||||||
local APP_ID = {
|
|
||||||
ROOT = 1,
|
|
||||||
-- main app page
|
|
||||||
UNITS = 2,
|
|
||||||
ABOUT = 3,
|
|
||||||
-- diag app page
|
|
||||||
ALARMS = 4,
|
|
||||||
-- other
|
|
||||||
DUMMY = 5,
|
|
||||||
NUM_APPS = 5
|
|
||||||
}
|
|
||||||
|
|
||||||
iocontrol.APP_ID = APP_ID
|
|
||||||
|
|
||||||
---@class pocket_ioctl
|
---@class pocket_ioctl
|
||||||
local io = {
|
local io = {
|
||||||
version = "unknown",
|
version = "unknown",
|
||||||
ps = psil.create()
|
ps = psil.create()
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class nav_tree_page
|
local config = nil ---@type pkt_config
|
||||||
---@field _p nav_tree_page|nil page's parent
|
|
||||||
---@field _c table page's children
|
|
||||||
---@field nav_to function function to navigate to this page
|
|
||||||
---@field switcher function|nil function to switch between children
|
|
||||||
---@field tasks table tasks to run while viewing this page
|
|
||||||
|
|
||||||
-- allocate the page navigation system
|
|
||||||
function iocontrol.alloc_nav()
|
|
||||||
local self = {
|
|
||||||
pane = nil, ---@type graphics_element
|
|
||||||
apps = {},
|
|
||||||
containers = {},
|
|
||||||
cur_app = APP_ID.ROOT
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cur_page = self.root
|
|
||||||
|
|
||||||
---@class pocket_nav
|
|
||||||
io.nav = {}
|
|
||||||
|
|
||||||
-- set the root pane element to switch between apps with
|
|
||||||
---@param root_pane graphics_element
|
|
||||||
function io.nav.set_pane(root_pane)
|
|
||||||
self.pane = root_pane
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.nav.set_sidebar(sidebar)
|
|
||||||
self.sidebar = sidebar
|
|
||||||
end
|
|
||||||
|
|
||||||
-- register an app
|
|
||||||
---@param app_id POCKET_APP_ID app ID
|
|
||||||
---@param container graphics_element element that contains this app (usually a Div)
|
|
||||||
---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number
|
|
||||||
function io.nav.register_app(app_id, container, pane)
|
|
||||||
---@class pocket_app
|
|
||||||
local app = {
|
|
||||||
loaded = false,
|
|
||||||
load = nil,
|
|
||||||
cur_page = nil, ---@type nav_tree_page
|
|
||||||
pane = pane,
|
|
||||||
paned_pages = {},
|
|
||||||
sidebar_items = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
app.load = function () app.loaded = true end
|
|
||||||
|
|
||||||
-- delayed set of the pane if it wasn't ready at the start
|
|
||||||
---@param root_pane graphics_element multipane
|
|
||||||
function app.set_root_pane(root_pane)
|
|
||||||
app.pane = root_pane
|
|
||||||
end
|
|
||||||
|
|
||||||
function app.set_sidebar(items)
|
|
||||||
app.sidebar_items = items
|
|
||||||
if self.sidebar then self.sidebar.update(items) end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- function to run on initial load into memory
|
|
||||||
---@param on_load function callback
|
|
||||||
function app.set_on_load(on_load)
|
|
||||||
app.load = function ()
|
|
||||||
on_load()
|
|
||||||
app.loaded = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if a pane was provided, this will switch between numbered pages
|
|
||||||
---@param idx integer page index
|
|
||||||
function app.switcher(idx)
|
|
||||||
if app.paned_pages[idx] then
|
|
||||||
app.paned_pages[idx].nav_to()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- create a new page entry in the app's page navigation tree
|
|
||||||
---@param parent nav_tree_page? a parent page or nil to set this as the root
|
|
||||||
---@param nav_to function|integer function to navigate to this page or pane index
|
|
||||||
---@return nav_tree_page new_page this new page
|
|
||||||
function app.new_page(parent, nav_to)
|
|
||||||
---@type nav_tree_page
|
|
||||||
local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} }
|
|
||||||
|
|
||||||
if parent == nil and app.cur_page == nil then
|
|
||||||
app.cur_page = page
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(nav_to) == "number" then
|
|
||||||
app.paned_pages[nav_to] = page
|
|
||||||
|
|
||||||
function page.nav_to()
|
|
||||||
app.cur_page = page
|
|
||||||
if app.pane then app.pane.set_value(nav_to) end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
function page.nav_to()
|
|
||||||
app.cur_page = page
|
|
||||||
nav_to()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- switch between children
|
|
||||||
---@param id integer child ID
|
|
||||||
function page.switcher(id) if page._c[id] then page._c[id].nav_to() end end
|
|
||||||
|
|
||||||
if parent ~= nil then
|
|
||||||
table.insert(page._p._c, page)
|
|
||||||
end
|
|
||||||
|
|
||||||
return page
|
|
||||||
end
|
|
||||||
|
|
||||||
-- get the currently active page
|
|
||||||
function app.get_current_page() return app.cur_page end
|
|
||||||
|
|
||||||
-- attempt to navigate up the tree
|
|
||||||
---@return boolean success true if successfully navigated up
|
|
||||||
function app.nav_up()
|
|
||||||
local parent = app.cur_page._p
|
|
||||||
if parent then parent.nav_to() end
|
|
||||||
return parent ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
self.apps[app_id] = app
|
|
||||||
self.containers[app_id] = container
|
|
||||||
|
|
||||||
return app
|
|
||||||
end
|
|
||||||
|
|
||||||
-- get a list of the app containers (usually Div elements)
|
|
||||||
function io.nav.get_containers() return self.containers end
|
|
||||||
|
|
||||||
-- open a given app
|
|
||||||
---@param app_id POCKET_APP_ID
|
|
||||||
function io.nav.open_app(app_id)
|
|
||||||
local app = self.apps[app_id] ---@type pocket_app
|
|
||||||
if app then
|
|
||||||
if not app.loaded then app.load() end
|
|
||||||
|
|
||||||
self.cur_app = app_id
|
|
||||||
self.pane.set_value(app_id)
|
|
||||||
|
|
||||||
if #app.sidebar_items > 0 then
|
|
||||||
self.sidebar.update(app.sidebar_items)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug("tried to open unknown app")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- get the currently active page
|
|
||||||
---@return nav_tree_page
|
|
||||||
function io.nav.get_current_page()
|
|
||||||
return self.apps[self.cur_app].get_current_page()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- attempt to navigate up
|
|
||||||
function io.nav.nav_up()
|
|
||||||
local app = self.apps[self.cur_app] ---@type pocket_app
|
|
||||||
log.debug("attempting app nav up for app " .. self.cur_app)
|
|
||||||
|
|
||||||
if not app.nav_up() then
|
|
||||||
log.debug("internal app nav up failed, going to home screen")
|
|
||||||
io.nav.open_app(APP_ID.ROOT)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- initialize facility-independent components of pocket iocontrol
|
-- initialize facility-independent components of pocket iocontrol
|
||||||
---@param comms pocket_comms
|
---@param comms pocket_comms
|
||||||
function iocontrol.init_core(comms)
|
---@param nav pocket_nav
|
||||||
iocontrol.alloc_nav()
|
---@param cfg pkt_config
|
||||||
|
function iocontrol.init_core(comms, nav, cfg)
|
||||||
|
config = cfg
|
||||||
|
|
||||||
|
io.nav = nav
|
||||||
|
|
||||||
---@class pocket_ioctl_diag
|
---@class pocket_ioctl_diag
|
||||||
io.diag = {}
|
io.diag = {}
|
||||||
@@ -266,24 +94,36 @@ function iocontrol.init_core(comms)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- initialize facility-dependent components of pocket iocontrol
|
-- initialize facility-dependent components of pocket iocontrol
|
||||||
---@param conf facility_conf configuration
|
---@param conf facility_conf facility configuration
|
||||||
---@param temp_scale 1|2|3|4 temperature unit (1 = K, 2 = C, 3 = F, 4 = R)
|
function iocontrol.init_fac(conf)
|
||||||
function iocontrol.init_fac(conf, temp_scale)
|
local temp_scale, energy_scale = config.TempScale, config.EnergyScale
|
||||||
|
io.temp_label = TEMP_UNITS[temp_scale]
|
||||||
|
io.energy_label = ENERGY_UNITS[energy_scale]
|
||||||
|
|
||||||
-- temperature unit label and conversion function (from Kelvin)
|
-- temperature unit label and conversion function (from Kelvin)
|
||||||
if temp_scale == 2 then
|
if temp_scale == TEMP_SCALE.CELSIUS then
|
||||||
io.temp_label = "\xb0C"
|
|
||||||
io.temp_convert = function (t) return t - 273.15 end
|
io.temp_convert = function (t) return t - 273.15 end
|
||||||
elseif temp_scale == 3 then
|
elseif temp_scale == TEMP_SCALE.FAHRENHEIT then
|
||||||
io.temp_label = "\xb0F"
|
|
||||||
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
||||||
elseif temp_scale == 4 then
|
elseif temp_scale == TEMP_SCALE.RANKINE then
|
||||||
io.temp_label = "\xb0R"
|
|
||||||
io.temp_convert = function (t) return 1.8 * t end
|
io.temp_convert = function (t) return 1.8 * t end
|
||||||
else
|
else
|
||||||
io.temp_label = "K"
|
io.temp_label = "K"
|
||||||
io.temp_convert = function (t) return t end
|
io.temp_convert = function (t) return t end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- energy unit label and conversion function (from Joules unless otherwise specified)
|
||||||
|
if energy_scale == ENERGY_SCALE.FE or energy_scale == ENERGY_SCALE.RF then
|
||||||
|
io.energy_convert = util.joules_to_fe_rf
|
||||||
|
io.energy_convert_from_fe = function (t) return t end
|
||||||
|
io.energy_convert_to_fe = function (t) return t end
|
||||||
|
else
|
||||||
|
io.energy_label = "J"
|
||||||
|
io.energy_convert = function (t) return t end
|
||||||
|
io.energy_convert_from_fe = util.fe_rf_to_joules
|
||||||
|
io.energy_convert_to_fe = util.joules_to_fe_rf
|
||||||
|
end
|
||||||
|
|
||||||
-- facility data structure
|
-- facility data structure
|
||||||
---@class pioctl_facility
|
---@class pioctl_facility
|
||||||
io.facility = {
|
io.facility = {
|
||||||
@@ -454,6 +294,9 @@ function iocontrol.init_fac(conf, temp_scale)
|
|||||||
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
||||||
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
|
|
||||||
|
last_rate_change_ms = 0,
|
||||||
|
turbine_flow_stable = false,
|
||||||
|
|
||||||
-- auto control group
|
-- auto control group
|
||||||
a_group = 0,
|
a_group = 0,
|
||||||
|
|
||||||
@@ -507,8 +350,8 @@ end
|
|||||||
|
|
||||||
-- set network link state
|
-- set network link state
|
||||||
---@param state POCKET_LINK_STATE
|
---@param state POCKET_LINK_STATE
|
||||||
---@param sv_addr integer? supervisor address if linked
|
---@param sv_addr integer|false|nil supervisor address if linked, nil if unchanged, false if unlinked
|
||||||
---@param api_addr integer? coordinator address if linked
|
---@param api_addr integer|false|nil coordinator address if linked, nil if unchanged, false if unlinked
|
||||||
function iocontrol.report_link_state(state, sv_addr, api_addr)
|
function iocontrol.report_link_state(state, sv_addr, api_addr)
|
||||||
io.ps.publish("link_state", state)
|
io.ps.publish("link_state", state)
|
||||||
|
|
||||||
@@ -520,9 +363,16 @@ function iocontrol.report_link_state(state, sv_addr, api_addr)
|
|||||||
io.ps.publish("crd_conn_quality", 0)
|
io.ps.publish("crd_conn_quality", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
if state == LINK_STATE.LINKED then
|
if sv_addr then
|
||||||
io.ps.publish("sv_addr", sv_addr)
|
io.ps.publish("sv_addr", util.c(sv_addr, ":", config.SVR_Channel))
|
||||||
io.ps.publish("api_addr", api_addr)
|
elseif sv_addr == false then
|
||||||
|
io.ps.publish("sv_addr", "unknown (not linked)")
|
||||||
|
end
|
||||||
|
|
||||||
|
if api_addr then
|
||||||
|
io.ps.publish("api_addr", util.c(api_addr, ":", config.CRD_Channel))
|
||||||
|
elseif api_addr == false then
|
||||||
|
io.ps.publish("api_addr", "unknown (not linked)")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -585,186 +435,456 @@ function iocontrol.record_facility_data(data)
|
|||||||
return valid
|
return valid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function tripped(state) return state == ALARM_STATE.TRIPPED or state == ALARM_STATE.ACKED end
|
||||||
|
|
||||||
|
local function _record_multiblock_status(faulted, data, ps)
|
||||||
|
ps.publish("formed", data.formed)
|
||||||
|
ps.publish("faulted", faulted)
|
||||||
|
|
||||||
|
for key, val in pairs(data.state) do ps.publish(key, val) end
|
||||||
|
for key, val in pairs(data.tanks) do ps.publish(key, val) end
|
||||||
|
end
|
||||||
|
|
||||||
-- update unit status data from API_GET_UNIT
|
-- update unit status data from API_GET_UNIT
|
||||||
---@param data table
|
---@param data table
|
||||||
function iocontrol.record_unit_data(data)
|
function iocontrol.record_unit_data(data)
|
||||||
if type(data[1]) == "number" and io.units[data[1]] then
|
local unit = io.units[data[1]] ---@type pioctl_unit
|
||||||
local unit = io.units[data[1]] ---@type pioctl_unit
|
|
||||||
|
|
||||||
unit.connected = data[2]
|
unit.connected = data[2]
|
||||||
unit.rtu_hw = data[3]
|
unit.rtu_hw = data[3]
|
||||||
unit.alarms = data[4]
|
unit.alarms = data[4]
|
||||||
|
|
||||||
--#region Annunciator
|
--#region Annunciator
|
||||||
|
|
||||||
unit.annunciator = data[5]
|
unit.annunciator = data[5]
|
||||||
|
|
||||||
local rcs_disconn, rcs_warn, rcs_hazard = false, false, false
|
local rcs_disconn, rcs_warn, rcs_hazard = false, false, false
|
||||||
|
|
||||||
for key, val in pairs(unit.annunciator) do
|
for key, val in pairs(unit.annunciator) do
|
||||||
if key == "BoilerOnline" or key == "TurbineOnline" then
|
if key == "BoilerOnline" or key == "TurbineOnline" then
|
||||||
-- split up online arrays
|
local every = true
|
||||||
local every = true
|
|
||||||
for id = 1, #val do
|
-- split up online arrays
|
||||||
every = every and val[id]
|
for id = 1, #val do
|
||||||
|
every = every and val[id]
|
||||||
|
|
||||||
|
if key == "BoilerOnline" then
|
||||||
unit.boiler_ps_tbl[id].publish(key, val[id])
|
unit.boiler_ps_tbl[id].publish(key, val[id])
|
||||||
end
|
else
|
||||||
|
|
||||||
if not every then rcs_disconn = true end
|
|
||||||
|
|
||||||
unit.unit_ps.publish("U_" .. key, every)
|
|
||||||
elseif key == "HeatingRateLow" or key == "WaterLevelLow" then
|
|
||||||
-- split up array for all boilers
|
|
||||||
local any = false
|
|
||||||
for id = 1, #val do
|
|
||||||
any = any or val[id]
|
|
||||||
unit.boiler_ps_tbl[id].publish(key, val[id])
|
|
||||||
end
|
|
||||||
|
|
||||||
if key == "HeatingRateLow" and any then
|
|
||||||
rcs_warn = true
|
|
||||||
elseif key == "WaterLevelLow" and any then
|
|
||||||
rcs_hazard = true
|
|
||||||
end
|
|
||||||
|
|
||||||
unit.unit_ps.publish("U_" .. key, any)
|
|
||||||
elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then
|
|
||||||
-- split up array for all turbines
|
|
||||||
local any = false
|
|
||||||
for id = 1, #val do
|
|
||||||
any = any or val[id]
|
|
||||||
unit.turbine_ps_tbl[id].publish(key, val[id])
|
unit.turbine_ps_tbl[id].publish(key, val[id])
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if key == "GeneratorTrip" and any then
|
if not every then rcs_disconn = true end
|
||||||
rcs_warn = true
|
|
||||||
elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then
|
|
||||||
rcs_hazard = true
|
|
||||||
end
|
|
||||||
|
|
||||||
unit.unit_ps.publish("U_" .. key, any)
|
unit.unit_ps.publish("U_" .. key, every)
|
||||||
|
elseif key == "HeatingRateLow" or key == "WaterLevelLow" then
|
||||||
|
-- split up array for all boilers
|
||||||
|
local any = false
|
||||||
|
for id = 1, #val do
|
||||||
|
any = any or val[id]
|
||||||
|
unit.boiler_ps_tbl[id].publish(key, val[id])
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == "HeatingRateLow" and any then
|
||||||
|
rcs_warn = true
|
||||||
|
elseif key == "WaterLevelLow" and any then
|
||||||
|
rcs_hazard = true
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_" .. key, any)
|
||||||
|
elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then
|
||||||
|
-- split up array for all turbines
|
||||||
|
local any = false
|
||||||
|
for id = 1, #val do
|
||||||
|
any = any or val[id]
|
||||||
|
unit.turbine_ps_tbl[id].publish(key, val[id])
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == "GeneratorTrip" and any then
|
||||||
|
rcs_warn = true
|
||||||
|
elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then
|
||||||
|
rcs_hazard = true
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_" .. key, any)
|
||||||
|
else
|
||||||
|
-- non-table fields
|
||||||
|
unit.unit_ps.publish(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local anc = unit.annunciator
|
||||||
|
rcs_hazard = rcs_hazard or anc.RCPTrip
|
||||||
|
rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or
|
||||||
|
anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch
|
||||||
|
|
||||||
|
local rcs_status = 4
|
||||||
|
if rcs_hazard then
|
||||||
|
rcs_status = 2
|
||||||
|
elseif rcs_warn then
|
||||||
|
rcs_status = 3
|
||||||
|
elseif rcs_disconn then
|
||||||
|
rcs_status = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_RCS", rcs_status)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Reactor Data
|
||||||
|
|
||||||
|
unit.reactor_data = data[6]
|
||||||
|
|
||||||
|
local control_status = 1
|
||||||
|
local reactor_status = 1
|
||||||
|
local reactor_state = 1
|
||||||
|
local rps_status = 1
|
||||||
|
|
||||||
|
if unit.connected then
|
||||||
|
-- update RPS status
|
||||||
|
if unit.reactor_data.rps_tripped then
|
||||||
|
control_status = 2
|
||||||
|
|
||||||
|
if unit.reactor_data.rps_trip_cause == "manual" then
|
||||||
|
reactor_state = 4 -- disabled
|
||||||
|
rps_status = 3
|
||||||
else
|
else
|
||||||
-- non-table fields
|
reactor_state = 6 -- SCRAM
|
||||||
|
rps_status = 2
|
||||||
|
end
|
||||||
|
else
|
||||||
|
rps_status = 4
|
||||||
|
reactor_state = 4
|
||||||
|
end
|
||||||
|
|
||||||
|
-- update reactor/control status
|
||||||
|
if unit.reactor_data.mek_status.status then
|
||||||
|
reactor_status = 4
|
||||||
|
reactor_state = 5 -- running
|
||||||
|
control_status = util.trinary(unit.annunciator.AutoControl, 4, 3)
|
||||||
|
else
|
||||||
|
if unit.reactor_data.no_reactor then
|
||||||
|
reactor_status = 2
|
||||||
|
reactor_state = 3 -- faulted
|
||||||
|
elseif not unit.reactor_data.formed then
|
||||||
|
reactor_status = 3
|
||||||
|
reactor_state = 2 -- not formed
|
||||||
|
elseif unit.reactor_data.rps_status.force_dis then
|
||||||
|
reactor_status = 3
|
||||||
|
reactor_state = 7 -- force disabled
|
||||||
|
else
|
||||||
|
reactor_status = 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for key, val in pairs(unit.reactor_data) do
|
||||||
|
if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then
|
||||||
unit.unit_ps.publish(key, val)
|
unit.unit_ps.publish(key, val)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local anc = unit.annunciator
|
if type(unit.reactor_data.rps_status) == "table" then
|
||||||
rcs_hazard = rcs_hazard or anc.RCPTrip
|
for key, val in pairs(unit.reactor_data.rps_status) do
|
||||||
rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or
|
unit.unit_ps.publish(key, val)
|
||||||
anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch or anc.MaxWaterReturnFeed
|
|
||||||
|
|
||||||
local rcs_status = 4
|
|
||||||
if rcs_hazard then
|
|
||||||
rcs_status = 2
|
|
||||||
elseif rcs_warn then
|
|
||||||
rcs_status = 3
|
|
||||||
elseif rcs_disconn then
|
|
||||||
rcs_status = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
unit.unit_ps.publish("U_RCS", rcs_status)
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region Reactor Data
|
|
||||||
|
|
||||||
unit.reactor_data = data[6]
|
|
||||||
|
|
||||||
local control_status = 1
|
|
||||||
local reactor_status = 1
|
|
||||||
local rps_status = 1
|
|
||||||
|
|
||||||
if unit.connected then
|
|
||||||
-- update RPS status
|
|
||||||
if unit.reactor_data.rps_tripped then
|
|
||||||
control_status = 2
|
|
||||||
rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2)
|
|
||||||
else rps_status = 4 end
|
|
||||||
|
|
||||||
-- update reactor/control status
|
|
||||||
if unit.reactor_data.mek_status.status then
|
|
||||||
reactor_status = 4
|
|
||||||
control_status = util.trinary(unit.annunciator.AutoControl, 4, 3)
|
|
||||||
else
|
|
||||||
if unit.reactor_data.no_reactor then
|
|
||||||
reactor_status = 2
|
|
||||||
elseif not unit.reactor_data.formed or unit.reactor_data.rps_status.force_dis then
|
|
||||||
reactor_status = 3
|
|
||||||
else
|
|
||||||
reactor_status = 4
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for key, val in pairs(unit.reactor_data) do
|
|
||||||
if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then
|
|
||||||
unit.unit_ps.publish(key, val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(unit.reactor_data.rps_status) == "table" then
|
|
||||||
for key, val in pairs(unit.reactor_data.rps_status) do
|
|
||||||
unit.unit_ps.publish(key, val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(unit.reactor_data.mek_status) == "table" then
|
|
||||||
for key, val in pairs(unit.reactor_data.mek_status) do
|
|
||||||
unit.unit_ps.publish(key, val)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
unit.unit_ps.publish("U_ControlStatus", control_status)
|
if type(unit.reactor_data.mek_status) == "table" then
|
||||||
unit.unit_ps.publish("U_ReactorStatus", reactor_status)
|
for key, val in pairs(unit.reactor_data.mek_status) do
|
||||||
unit.unit_ps.publish("U_RPS", rps_status)
|
unit.unit_ps.publish(key, val)
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
unit.boiler_data_tbl = data[7]
|
|
||||||
|
|
||||||
for id = 1, #unit.boiler_data_tbl do
|
|
||||||
local boiler = unit.boiler_data_tbl[id] ---@type boilerv_session_db
|
|
||||||
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
|
||||||
|
|
||||||
local boiler_status = 1
|
|
||||||
|
|
||||||
if unit.rtu_hw.boilers[id].connected then
|
|
||||||
if unit.rtu_hw.boilers[id].faulted then
|
|
||||||
boiler_status = 3
|
|
||||||
elseif boiler.formed then
|
|
||||||
boiler_status = 4
|
|
||||||
else
|
|
||||||
boiler_status = 2
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
ps.publish("BoilerStatus", boiler_status)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unit.turbine_data_tbl = data[8]
|
|
||||||
|
|
||||||
for id = 1, #unit.turbine_data_tbl do
|
|
||||||
local turbine = unit.turbine_data_tbl[id] ---@type turbinev_session_db
|
|
||||||
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
|
||||||
|
|
||||||
local turbine_status = 1
|
|
||||||
|
|
||||||
if unit.rtu_hw.turbines[id].connected then
|
|
||||||
if unit.rtu_hw.turbines[id].faulted then
|
|
||||||
turbine_status = 3
|
|
||||||
elseif turbine.formed then
|
|
||||||
turbine_status = 4
|
|
||||||
else
|
|
||||||
turbine_status = 2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ps.publish("TurbineStatus", turbine_status)
|
|
||||||
end
|
|
||||||
|
|
||||||
unit.tank_data_tbl = data[9]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_ControlStatus", control_status)
|
||||||
|
unit.unit_ps.publish("U_ReactorStatus", reactor_status)
|
||||||
|
unit.unit_ps.publish("U_ReactorStateStatus", reactor_state)
|
||||||
|
unit.unit_ps.publish("U_RPS", rps_status)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region RTU Devices
|
||||||
|
|
||||||
|
unit.boiler_data_tbl = data[7]
|
||||||
|
|
||||||
|
for id = 1, #unit.boiler_data_tbl do
|
||||||
|
local boiler = unit.boiler_data_tbl[id] ---@type boilerv_session_db
|
||||||
|
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
|
local boiler_status = 1
|
||||||
|
local computed_status = 1
|
||||||
|
|
||||||
|
if unit.rtu_hw.boilers[id].connected then
|
||||||
|
if unit.rtu_hw.boilers[id].faulted then
|
||||||
|
boiler_status = 3
|
||||||
|
computed_status = 3
|
||||||
|
elseif boiler.formed then
|
||||||
|
boiler_status = 4
|
||||||
|
|
||||||
|
if boiler.state.boil_rate > 0 then
|
||||||
|
computed_status = 5
|
||||||
|
else
|
||||||
|
computed_status = 4
|
||||||
|
end
|
||||||
|
else
|
||||||
|
boiler_status = 2
|
||||||
|
computed_status = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
_record_multiblock_status(unit.rtu_hw.boilers[id].faulted, boiler, ps)
|
||||||
|
end
|
||||||
|
|
||||||
|
ps.publish("BoilerStatus", boiler_status)
|
||||||
|
ps.publish("BoilerStateStatus", computed_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.turbine_data_tbl = data[8]
|
||||||
|
|
||||||
|
for id = 1, #unit.turbine_data_tbl do
|
||||||
|
local turbine = unit.turbine_data_tbl[id] ---@type turbinev_session_db
|
||||||
|
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
|
local turbine_status = 1
|
||||||
|
local computed_status = 1
|
||||||
|
|
||||||
|
if unit.rtu_hw.turbines[id].connected then
|
||||||
|
if unit.rtu_hw.turbines[id].faulted then
|
||||||
|
turbine_status = 3
|
||||||
|
computed_status = 3
|
||||||
|
elseif turbine.formed then
|
||||||
|
turbine_status = 4
|
||||||
|
|
||||||
|
if turbine.tanks.energy_fill >= 0.99 then
|
||||||
|
computed_status = 6
|
||||||
|
elseif turbine.state.flow_rate < 100 then
|
||||||
|
computed_status = 4
|
||||||
|
else
|
||||||
|
computed_status = 5
|
||||||
|
end
|
||||||
|
else
|
||||||
|
turbine_status = 2
|
||||||
|
computed_status = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
_record_multiblock_status(unit.rtu_hw.turbines[id].faulted, turbine, ps)
|
||||||
|
end
|
||||||
|
|
||||||
|
ps.publish("TurbineStatus", turbine_status)
|
||||||
|
ps.publish("TurbineStateStatus", computed_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.tank_data_tbl = data[9]
|
||||||
|
|
||||||
|
unit.last_rate_change_ms = data[10]
|
||||||
|
unit.turbine_flow_stable = data[11]
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Status Information Display
|
||||||
|
|
||||||
|
local ecam = {} -- aviation reference :) back to VATSIM I go...
|
||||||
|
|
||||||
|
-- local function red(text) return { text = text, color = colors.red } end
|
||||||
|
local function white(text) return { text = text, color = colors.white } end
|
||||||
|
local function blue(text) return { text = text, color = colors.blue } end
|
||||||
|
|
||||||
|
-- unit.reactor_data.rps_status = {
|
||||||
|
-- high_dmg = false,
|
||||||
|
-- high_temp = false,
|
||||||
|
-- low_cool = false,
|
||||||
|
-- ex_waste = false,
|
||||||
|
-- ex_hcool = false,
|
||||||
|
-- no_fuel = false,
|
||||||
|
-- fault = false,
|
||||||
|
-- timeout = false,
|
||||||
|
-- manual = false,
|
||||||
|
-- automatic = false,
|
||||||
|
-- sys_fail = false,
|
||||||
|
-- force_dis = false
|
||||||
|
-- }
|
||||||
|
|
||||||
|
-- if unit.reactor_data.rps_status then
|
||||||
|
-- for k, v in pairs(unit.alarms) do
|
||||||
|
-- unit.alarms[k] = ALARM_STATE.TRIPPED
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ContainmentBreach]) then
|
||||||
|
local items = { white("REACTOR MELTDOWN"), blue("DON HAZMAT SUIT") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "CONTAINMENT BREACH", help = "ContainmentBreach", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ContainmentRadiation]) then
|
||||||
|
local items = {
|
||||||
|
white("RADIATION DETECTED"),
|
||||||
|
blue("DON HAZMAT SUIT"),
|
||||||
|
blue("RESOLVE LEAK"),
|
||||||
|
blue("AWAIT SAFE LEVELS")
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(ecam, { color = colors.red, text = "RADIATION LEAK", help = "ContainmentRadiation", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.CriticalDamage]) then
|
||||||
|
local items = { white("MELTDOWN IMMINENT"), blue("EVACUATE") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "RCT DAMAGE CRITICAL", help = "CriticalDamage", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorLost]) then
|
||||||
|
local items = { white("REACTOR OFF-LINE"), blue("CHECK PLC") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "REACTOR CONN LOST", help = "ReactorLost", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorDamage]) then
|
||||||
|
local items = { white("REACTOR DAMAGED"), blue("CHECK RCS"), blue("AWAIT DMG REDUCED") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "REACTOR DAMAGE", help = "ReactorDamage", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorOverTemp]) then
|
||||||
|
local items = { white("DAMAGING TEMP"), blue("CHECK RCS"), blue("AWAIT COOLDOWN") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "REACTOR OVER TEMP", help = "ReactorOverTemp", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorHighTemp]) then
|
||||||
|
local items = { white("OVER EXPECTED TEMP"), blue("CHECK RCS") }
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "REACTOR HIGH TEMP", help = "ReactorHighTemp", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorWasteLeak]) then
|
||||||
|
local items = { white("AT WASTE CAPACITY"), blue("CHECK WASTE OUTPUT"), blue("KEEP RCT DISABLED") }
|
||||||
|
table.insert(ecam, { color = colors.red, text = "REACTOR WASTE LEAK", help = "ReactorWasteLeak", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.ReactorHighWaste]) then
|
||||||
|
local items = { blue("CHECK WASTE OUTPUT") }
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "REACTOR WASTE HIGH", help = "ReactorHighWaste", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.RPSTransient]) then
|
||||||
|
local items = {}
|
||||||
|
local stat = unit.reactor_data.rps_status
|
||||||
|
|
||||||
|
-- for k, _ in pairs(stat) do stat[k] = true end
|
||||||
|
|
||||||
|
local function insert(cond, key, text, color) if cond[key] then table.insert(items, { text = text, help = key, color = color }) end end
|
||||||
|
|
||||||
|
table.insert(items, white("REACTOR SCRAMMED"))
|
||||||
|
insert(stat, "high_dmg", "HIGH DAMAGE", colors.red)
|
||||||
|
insert(stat, "high_temp", "HIGH TEMPERATURE", colors.red)
|
||||||
|
insert(stat, "low_cool", "CRIT LOW COOLANT")
|
||||||
|
insert(stat, "ex_waste", "EXCESS WASTE")
|
||||||
|
insert(stat, "ex_hcool", "EXCESS HEATED COOL")
|
||||||
|
insert(stat, "no_fuel", "NO FUEL")
|
||||||
|
insert(stat, "fault", "HARDWARE FAULT")
|
||||||
|
insert(stat, "timeout", "SUPERVISOR DISCONN")
|
||||||
|
insert(stat, "manual", "MANUAL SCRAM", colors.white)
|
||||||
|
insert(stat, "automatic", "AUTOMATIC SCRAM")
|
||||||
|
insert(stat, "sys_fail", "NOT FORMED", colors.red)
|
||||||
|
insert(stat, "force_dis", "FORCE DISABLED", colors.red)
|
||||||
|
table.insert(items, blue("RESOLVE PROBLEM"))
|
||||||
|
table.insert(items, blue("RESET RPS"))
|
||||||
|
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "RPS TRANSIENT", help = "RPSTransient", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.RCSTransient]) then
|
||||||
|
local items = {}
|
||||||
|
local annunc = unit.annunciator
|
||||||
|
|
||||||
|
-- for k, v in pairs(annunc) do
|
||||||
|
-- if type(v) == "boolean" then annunc[k] = true end
|
||||||
|
-- if type(v) == "table" then
|
||||||
|
-- for a, _ in pairs(v) do
|
||||||
|
-- v[a] = true
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
|
||||||
|
local function insert(cond, key, text, color)
|
||||||
|
if cond == true or (type(cond) == "table" and cond[key]) then table.insert(items, { text = text, help = key, color = color }) end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(items, white("COOLANT PROBLEM"))
|
||||||
|
|
||||||
|
insert(annunc, "RCPTrip", "RCP TRIP", colors.red)
|
||||||
|
insert(annunc, "CoolantLevelLow", "LOW COOLANT")
|
||||||
|
|
||||||
|
if unit.num_boilers == 0 then
|
||||||
|
if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
|
||||||
|
insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
|
||||||
|
end
|
||||||
|
|
||||||
|
if unit.turbine_flow_stable then
|
||||||
|
insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
|
||||||
|
insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
|
||||||
|
insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (util.time_ms() - unit.last_rate_change_ms) > const.FLOW_STABILITY_DELAY_MS then
|
||||||
|
insert(annunc, "RCSFlowLow", "RCS FLOW LOW")
|
||||||
|
insert(annunc, "BoilRateMismatch", "BOIL RATE MISMATCH")
|
||||||
|
insert(annunc, "CoolantFeedMismatch", "COOL FEED MISMATCH")
|
||||||
|
end
|
||||||
|
|
||||||
|
if unit.turbine_flow_stable then
|
||||||
|
insert(annunc, "SteamFeedMismatch", "STM FEED MISMATCH")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
insert(annunc, "MaxWaterReturnFeed", "MAX WTR RTRN FEED")
|
||||||
|
|
||||||
|
for k, v in ipairs(annunc.WaterLevelLow) do insert(v, "WaterLevelLow", "BOILER " .. k .. " WTR LOW", colors.red) end
|
||||||
|
for k, v in ipairs(annunc.HeatingRateLow) do insert(v, "HeatingRateLow", "BOILER " .. k .. " HEAT RATE") end
|
||||||
|
for k, v in ipairs(annunc.TurbineOverSpeed) do insert(v, "TurbineOverSpeed", "TURBINE " .. k .. " OVERSPD", colors.red) end
|
||||||
|
for k, v in ipairs(annunc.GeneratorTrip) do insert(v, "GeneratorTrip", "TURBINE " .. k .. " GEN TRIP") end
|
||||||
|
|
||||||
|
table.insert(items, blue("CHECK COOLING SYS"))
|
||||||
|
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "RCS TRANSIENT", help = "RCSTransient", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if tripped(unit.alarms[ALARM.TurbineTrip]) then
|
||||||
|
local items = {}
|
||||||
|
|
||||||
|
for k, v in ipairs(unit.annunciator.TurbineTrip) do
|
||||||
|
if v then table.insert(items, { text = "TURBINE " .. k .. " TRIP", help = "TurbineTrip" }) end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(items, blue("CHECK ENERGY OUT"))
|
||||||
|
table.insert(ecam, { color = colors.red, text = "TURBINE TRIP", help = "TurbineTripAlarm", items = items})
|
||||||
|
end
|
||||||
|
|
||||||
|
if not (tripped(unit.alarms[ALARM.ReactorLost]) or unit.connected) then
|
||||||
|
local items = { blue("CHECK PLC") }
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "REACTOR OFF-LINE", items = items })
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in ipairs(unit.annunciator.BoilerOnline) do
|
||||||
|
if not v then
|
||||||
|
local items = { blue("CHECK RTU") }
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "BOILER " .. k .. " OFF-LINE", items = items})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for k, v in ipairs(unit.annunciator.TurbineOnline) do
|
||||||
|
if not v then
|
||||||
|
local items = { blue("CHECK RTU") }
|
||||||
|
table.insert(ecam, { color = colors.yellow, text = "TURBINE " .. k .. " OFF-LINE", items = items})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if no alarms, put some basic status messages in
|
||||||
|
if #ecam == 0 then
|
||||||
|
table.insert(ecam, { color = colors.green, text = "REACTOR " .. util.trinary(unit.reactor_data.mek_status.status, "NOMINAL", "IDLE"), items = {}})
|
||||||
|
|
||||||
|
local plural = util.trinary(unit.num_turbines > 1, "S", "")
|
||||||
|
table.insert(ecam, { color = colors.green, text = "TURBINE" .. plural .. util.trinary(unit.turbine_flow_stable, " STABLE", " STABILIZING"), items = {}})
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_ECAM", textutils.serialize(ecam))
|
||||||
|
|
||||||
|
--#endregion
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the IO controller database
|
-- get the IO controller database
|
||||||
|
|||||||
@@ -14,6 +14,18 @@ local LINK_STATE = iocontrol.LINK_STATE
|
|||||||
|
|
||||||
local pocket = {}
|
local pocket = {}
|
||||||
|
|
||||||
|
local MQ__RENDER_CMD = {
|
||||||
|
UNLOAD_SV_APPS = 1,
|
||||||
|
UNLOAD_API_APPS = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
local MQ__RENDER_DATA = {
|
||||||
|
LOAD_APP = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pocket.MQ__RENDER_CMD = MQ__RENDER_CMD
|
||||||
|
pocket.MQ__RENDER_DATA = MQ__RENDER_DATA
|
||||||
|
|
||||||
---@type pkt_config
|
---@type pkt_config
|
||||||
local config = {}
|
local config = {}
|
||||||
|
|
||||||
@@ -23,6 +35,9 @@ pocket.config = config
|
|||||||
function pocket.load_config()
|
function pocket.load_config()
|
||||||
if not settings.load("/pocket.settings") then return false end
|
if not settings.load("/pocket.settings") then return false end
|
||||||
|
|
||||||
|
config.TempScale = settings.get("TempScale")
|
||||||
|
config.EnergyScale = settings.get("EnergyScale")
|
||||||
|
|
||||||
config.SVR_Channel = settings.get("SVR_Channel")
|
config.SVR_Channel = settings.get("SVR_Channel")
|
||||||
config.CRD_Channel = settings.get("CRD_Channel")
|
config.CRD_Channel = settings.get("CRD_Channel")
|
||||||
config.PKT_Channel = settings.get("PKT_Channel")
|
config.PKT_Channel = settings.get("PKT_Channel")
|
||||||
@@ -36,6 +51,11 @@ function pocket.load_config()
|
|||||||
|
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
|
cfv.assert_type_int(config.TempScale)
|
||||||
|
cfv.assert_range(config.TempScale, 1, 4)
|
||||||
|
cfv.assert_type_int(config.EnergyScale)
|
||||||
|
cfv.assert_range(config.EnergyScale, 1, 3)
|
||||||
|
|
||||||
cfv.assert_channel(config.SVR_Channel)
|
cfv.assert_channel(config.SVR_Channel)
|
||||||
cfv.assert_channel(config.CRD_Channel)
|
cfv.assert_channel(config.CRD_Channel)
|
||||||
cfv.assert_channel(config.PKT_Channel)
|
cfv.assert_channel(config.PKT_Channel)
|
||||||
@@ -58,26 +78,310 @@ function pocket.load_config()
|
|||||||
return cfv.valid()
|
return cfv.valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@enum POCKET_APP_ID
|
||||||
|
local APP_ID = {
|
||||||
|
ROOT = 1,
|
||||||
|
LOADER = 2,
|
||||||
|
-- main app pages
|
||||||
|
UNITS = 3,
|
||||||
|
GUIDE = 4,
|
||||||
|
ABOUT = 5,
|
||||||
|
-- diag app page
|
||||||
|
ALARMS = 6,
|
||||||
|
-- other
|
||||||
|
DUMMY = 7,
|
||||||
|
NUM_APPS = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
pocket.APP_ID = APP_ID
|
||||||
|
|
||||||
|
---@class nav_tree_page
|
||||||
|
---@field _p nav_tree_page|nil page's parent
|
||||||
|
---@field _c table page's children
|
||||||
|
---@field nav_to function function to navigate to this page
|
||||||
|
---@field switcher function|nil function to switch between children
|
||||||
|
---@field tasks table tasks to run while viewing this page
|
||||||
|
|
||||||
|
-- initialize the page navigation system
|
||||||
|
---@param smem pkt_shared_memory
|
||||||
|
function pocket.init_nav(smem)
|
||||||
|
local self = {
|
||||||
|
pane = nil, ---@type graphics_element
|
||||||
|
sidebar = nil, ---@type graphics_element
|
||||||
|
apps = {},
|
||||||
|
containers = {},
|
||||||
|
help_map = {},
|
||||||
|
help_return = nil,
|
||||||
|
loader_return = nil,
|
||||||
|
cur_app = APP_ID.ROOT
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cur_page = self.root
|
||||||
|
|
||||||
|
---@class pocket_nav
|
||||||
|
local nav = {}
|
||||||
|
|
||||||
|
-- set the root pane element to switch between apps with
|
||||||
|
---@param root_pane graphics_element
|
||||||
|
function nav.set_pane(root_pane) self.pane = root_pane end
|
||||||
|
|
||||||
|
-- link sidebar element
|
||||||
|
---@param sidebar graphics_element
|
||||||
|
function nav.set_sidebar(sidebar) self.sidebar = sidebar end
|
||||||
|
|
||||||
|
-- register an app
|
||||||
|
---@param app_id POCKET_APP_ID app ID
|
||||||
|
---@param container graphics_element element that contains this app (usually a Div)
|
||||||
|
---@param pane? graphics_element multipane if this is a simple paned app, then nav_to must be a number
|
||||||
|
---@param require_sv? boolean true to specifiy if this app should be unloaded when the supervisor connection is lost
|
||||||
|
---@param require_api? boolean true to specifiy if this app should be unloaded when the api connection is lost
|
||||||
|
function nav.register_app(app_id, container, pane, require_sv, require_api)
|
||||||
|
---@class pocket_app
|
||||||
|
local app = {
|
||||||
|
loaded = false,
|
||||||
|
cur_page = nil, ---@type nav_tree_page
|
||||||
|
pane = pane,
|
||||||
|
paned_pages = {},
|
||||||
|
sidebar_items = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.load = function () app.loaded = true end
|
||||||
|
app.unload = function () app.loaded = false end
|
||||||
|
|
||||||
|
-- check which connections this requires (for unload)
|
||||||
|
---@return boolean requires_sv, boolean requires_api
|
||||||
|
function app.check_requires() return require_sv or false, require_api or false end
|
||||||
|
|
||||||
|
-- check if any connection is required (for load)
|
||||||
|
function app.requires_conn() return require_sv or require_api or false end
|
||||||
|
|
||||||
|
-- delayed set of the pane if it wasn't ready at the start
|
||||||
|
---@param root_pane graphics_element multipane
|
||||||
|
function app.set_root_pane(root_pane)
|
||||||
|
app.pane = root_pane
|
||||||
|
end
|
||||||
|
|
||||||
|
-- configure the sidebar
|
||||||
|
---@param items table
|
||||||
|
function app.set_sidebar(items)
|
||||||
|
app.sidebar_items = items
|
||||||
|
if self.sidebar then self.sidebar.update(items) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- function to run on initial load into memory
|
||||||
|
---@param on_load function callback
|
||||||
|
function app.set_load(on_load)
|
||||||
|
app.load = function ()
|
||||||
|
on_load()
|
||||||
|
app.loaded = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- function to run to close out the app
|
||||||
|
---@param on_unload function callback
|
||||||
|
function app.set_unload(on_unload)
|
||||||
|
app.unload = function ()
|
||||||
|
on_unload()
|
||||||
|
app.loaded = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if a pane was provided, this will switch between numbered pages
|
||||||
|
---@param idx integer page index
|
||||||
|
function app.switcher(idx)
|
||||||
|
if app.paned_pages[idx] then
|
||||||
|
app.paned_pages[idx].nav_to()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create a new page entry in the app's page navigation tree
|
||||||
|
---@param parent nav_tree_page|nil a parent page or nil to set this as the root
|
||||||
|
---@param nav_to function|integer function to navigate to this page or pane index
|
||||||
|
---@return nav_tree_page new_page this new page
|
||||||
|
function app.new_page(parent, nav_to)
|
||||||
|
---@type nav_tree_page
|
||||||
|
local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} }
|
||||||
|
|
||||||
|
if parent == nil and app.cur_page == nil then
|
||||||
|
app.cur_page = page
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(nav_to) == "number" then
|
||||||
|
app.paned_pages[nav_to] = page
|
||||||
|
|
||||||
|
function page.nav_to()
|
||||||
|
app.cur_page = page
|
||||||
|
if app.pane then app.pane.set_value(nav_to) end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
function page.nav_to()
|
||||||
|
app.cur_page = page
|
||||||
|
nav_to()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- switch between children
|
||||||
|
---@param id integer child ID
|
||||||
|
function page.switcher(id) if page._c[id] then page._c[id].nav_to() end end
|
||||||
|
|
||||||
|
if parent ~= nil then
|
||||||
|
table.insert(page._p._c, page)
|
||||||
|
end
|
||||||
|
|
||||||
|
return page
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delete paned pages and clear the current page
|
||||||
|
function app.delete_pages()
|
||||||
|
app.paned_pages = {}
|
||||||
|
app.cur_page = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the currently active page
|
||||||
|
function app.get_current_page() return app.cur_page end
|
||||||
|
|
||||||
|
-- attempt to navigate up the tree
|
||||||
|
---@return boolean success true if successfully navigated up
|
||||||
|
function app.nav_up()
|
||||||
|
local parent = app.cur_page._p
|
||||||
|
if parent then parent.nav_to() end
|
||||||
|
return parent ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
self.apps[app_id] = app
|
||||||
|
self.containers[app_id] = container
|
||||||
|
|
||||||
|
return app
|
||||||
|
end
|
||||||
|
|
||||||
|
-- open an app
|
||||||
|
---@param app_id POCKET_APP_ID
|
||||||
|
function nav.open_app(app_id)
|
||||||
|
-- reset help return on navigating out of an app
|
||||||
|
if app_id == APP_ID.ROOT then self.help_return = nil end
|
||||||
|
|
||||||
|
local app = self.apps[app_id] ---@type pocket_app
|
||||||
|
if app then
|
||||||
|
if app.requires_conn() and not smem.pkt_sys.pocket_comms.is_linked() then
|
||||||
|
-- bring up the app loader
|
||||||
|
self.loader_return = app_id
|
||||||
|
app_id = APP_ID.LOADER
|
||||||
|
app = self.apps[app_id]
|
||||||
|
else self.loader_return = nil end
|
||||||
|
|
||||||
|
if not app.loaded then smem.q.mq_render.push_data(MQ__RENDER_DATA.LOAD_APP, app_id) end
|
||||||
|
|
||||||
|
self.cur_app = app_id
|
||||||
|
self.pane.set_value(app_id)
|
||||||
|
|
||||||
|
if #app.sidebar_items > 0 then
|
||||||
|
self.sidebar.update(app.sidebar_items)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("tried to open unknown app")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- open the app that was blocked on connecting
|
||||||
|
function nav.on_loader_connected()
|
||||||
|
if self.loader_return then
|
||||||
|
nav.open_app(self.loader_return)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- load a given app
|
||||||
|
---@param app_id POCKET_APP_ID
|
||||||
|
function nav.load_app(app_id)
|
||||||
|
self.apps[app_id].load()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- unload api-dependent apps
|
||||||
|
function nav.unload_api()
|
||||||
|
for id, app in pairs(self.apps) do
|
||||||
|
local _, api = app.check_requires()
|
||||||
|
if app.loaded and api then
|
||||||
|
if id == self.cur_app then nav.open_app(APP_ID.ROOT) end
|
||||||
|
app.unload()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- unload supervisor-dependent apps
|
||||||
|
function nav.unload_sv()
|
||||||
|
for id, app in pairs(self.apps) do
|
||||||
|
local sv, _ = app.check_requires()
|
||||||
|
if app.loaded and sv then
|
||||||
|
if id == self.cur_app then nav.open_app(APP_ID.ROOT) end
|
||||||
|
app.unload()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get a list of the app containers (usually Div elements)
|
||||||
|
function nav.get_containers() return self.containers end
|
||||||
|
|
||||||
|
-- get the currently active page
|
||||||
|
---@return nav_tree_page
|
||||||
|
function nav.get_current_page()
|
||||||
|
return self.apps[self.cur_app].get_current_page()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- attempt to navigate up within the active app, otherwise open home page<br>
|
||||||
|
-- except, this will go back to a prior app if leaving the help app after open_help was used
|
||||||
|
function nav.nav_up()
|
||||||
|
-- return out of help if opened with open_help
|
||||||
|
if self.help_return then
|
||||||
|
nav.open_app(self.help_return)
|
||||||
|
self.help_return = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local app = self.apps[self.cur_app] ---@type pocket_app
|
||||||
|
log.debug("attempting app nav up for app " .. self.cur_app)
|
||||||
|
|
||||||
|
if not app.nav_up() then
|
||||||
|
log.debug("internal app nav up failed, going to home screen")
|
||||||
|
nav.open_app(APP_ID.ROOT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- open the help app, to show the reference for a key
|
||||||
|
function nav.open_help(key)
|
||||||
|
self.help_return = self.cur_app
|
||||||
|
|
||||||
|
nav.open_app(APP_ID.GUIDE)
|
||||||
|
|
||||||
|
local load = self.help_map[key]
|
||||||
|
if load then load() end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- link the help map from the guide app
|
||||||
|
function nav.link_help(map) self.help_map = map end
|
||||||
|
|
||||||
|
return nav
|
||||||
|
end
|
||||||
|
|
||||||
-- pocket coordinator + supervisor communications
|
-- pocket coordinator + supervisor communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param version string pocket version
|
---@param version string pocket version
|
||||||
---@param nic nic network interface device
|
---@param nic nic network interface device
|
||||||
---@param sv_watchdog watchdog
|
---@param sv_watchdog watchdog
|
||||||
---@param api_watchdog watchdog
|
---@param api_watchdog watchdog
|
||||||
function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
---@param nav pocket_nav
|
||||||
|
function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||||
local self = {
|
local self = {
|
||||||
sv = {
|
sv = {
|
||||||
linked = false,
|
linked = false,
|
||||||
addr = comms.BROADCAST,
|
addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil, ---@type nil|integer
|
r_seq_num = nil, ---@type nil|integer
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
},
|
},
|
||||||
api = {
|
api = {
|
||||||
linked = false,
|
linked = false,
|
||||||
addr = comms.BROADCAST,
|
addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil, ---@type nil|integer
|
r_seq_num = nil, ---@type nil|integer
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
},
|
},
|
||||||
establish_delay_counter = 0
|
establish_delay_counter = 0
|
||||||
@@ -163,6 +467,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
-- close connection to the supervisor
|
-- close connection to the supervisor
|
||||||
function public.close_sv()
|
function public.close_sv()
|
||||||
sv_watchdog.cancel()
|
sv_watchdog.cancel()
|
||||||
|
nav.unload_sv()
|
||||||
self.sv.linked = false
|
self.sv.linked = false
|
||||||
self.sv.r_seq_num = nil
|
self.sv.r_seq_num = nil
|
||||||
self.sv.addr = comms.BROADCAST
|
self.sv.addr = comms.BROADCAST
|
||||||
@@ -172,6 +477,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
-- close connection to coordinator API server
|
-- close connection to coordinator API server
|
||||||
function public.close_api()
|
function public.close_api()
|
||||||
api_watchdog.cancel()
|
api_watchdog.cancel()
|
||||||
|
nav.unload_api()
|
||||||
self.api.linked = false
|
self.api.linked = false
|
||||||
self.api.r_seq_num = nil
|
self.api.r_seq_num = nil
|
||||||
self.api.addr = comms.BROADCAST
|
self.api.addr = comms.BROADCAST
|
||||||
@@ -187,7 +493,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
-- attempt to re-link if any of the dependent links aren't active
|
-- attempt to re-link if any of the dependent links aren't active
|
||||||
function public.link_update()
|
function public.link_update()
|
||||||
if not self.sv.linked then
|
if not self.sv.linked then
|
||||||
iocontrol.report_link_state(util.trinary(self.api.linked, LINK_STATE.API_LINK_ONLY, LINK_STATE.UNLINKED))
|
if self.api.linked then
|
||||||
|
iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY, false, nil)
|
||||||
|
else
|
||||||
|
iocontrol.report_link_state(LINK_STATE.UNLINKED, false, false)
|
||||||
|
end
|
||||||
|
|
||||||
if self.establish_delay_counter <= 0 then
|
if self.establish_delay_counter <= 0 then
|
||||||
_send_sv_establish()
|
_send_sv_establish()
|
||||||
@@ -196,7 +506,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.establish_delay_counter = self.establish_delay_counter - 1
|
self.establish_delay_counter = self.establish_delay_counter - 1
|
||||||
end
|
end
|
||||||
elseif not self.api.linked then
|
elseif not self.api.linked then
|
||||||
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY)
|
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY, nil, false)
|
||||||
|
|
||||||
if self.establish_delay_counter <= 0 then
|
if self.establish_delay_counter <= 0 then
|
||||||
_send_api_establish()
|
_send_api_establish()
|
||||||
@@ -204,9 +514,6 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
else
|
else
|
||||||
self.establish_delay_counter = self.establish_delay_counter - 1
|
self.establish_delay_counter = self.establish_delay_counter - 1
|
||||||
end
|
end
|
||||||
else
|
|
||||||
-- linked, all good!
|
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -274,7 +581,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
local ok = util.trinary(max == nil, packet.length == length, packet.length >= length and packet.length <= (max or 0))
|
local ok = util.trinary(max == nil, packet.length == length, packet.length >= length and packet.length <= (max or 0))
|
||||||
if not ok then
|
if not ok then
|
||||||
local fmt = "[comms] RX_PACKET{r_chan=%d,proto=%d,type=%d}: packet length mismatch -> expect %d != actual %d"
|
local fmt = "[comms] RX_PACKET{r_chan=%d,proto=%d,type=%d}: packet length mismatch -> expect %d != actual %d"
|
||||||
log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type))
|
log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type, length, packet.scada_frame.length()))
|
||||||
end
|
end
|
||||||
return ok
|
return ok
|
||||||
end
|
end
|
||||||
@@ -301,8 +608,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
elseif r_chan == config.CRD_Channel then
|
elseif r_chan == config.CRD_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.api.r_seq_num == nil then
|
if self.api.r_seq_num == nil then
|
||||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.api.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
||||||
@@ -310,7 +617,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@@ -324,7 +631,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
iocontrol.record_facility_data(packet.data)
|
iocontrol.record_facility_data(packet.data)
|
||||||
end
|
end
|
||||||
elseif packet.type == CRDN_TYPE.API_GET_UNIT then
|
elseif packet.type == CRDN_TYPE.API_GET_UNIT then
|
||||||
if _check_length(packet, 9) then
|
if _check_length(packet, 11) and type(packet.data[1]) == "number" and iocontrol.get_db().units[packet.data[1]] then
|
||||||
iocontrol.record_unit_data(packet.data)
|
iocontrol.record_unit_data(packet.data)
|
||||||
end
|
end
|
||||||
else _fail_type(packet) end
|
else _fail_type(packet) end
|
||||||
@@ -353,6 +660,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
elseif packet.type == MGMT_TYPE.CLOSE then
|
elseif packet.type == MGMT_TYPE.CLOSE then
|
||||||
-- handle session close
|
-- handle session close
|
||||||
api_watchdog.cancel()
|
api_watchdog.cancel()
|
||||||
|
nav.unload_api()
|
||||||
self.api.linked = false
|
self.api.linked = false
|
||||||
self.api.r_seq_num = nil
|
self.api.r_seq_num = nil
|
||||||
self.api.addr = comms.BROADCAST
|
self.api.addr = comms.BROADCAST
|
||||||
@@ -371,8 +679,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
-- get configuration
|
-- get configuration
|
||||||
local conf = { num_units = fac_config[1], cooling = fac_config[2] }
|
local conf = { num_units = fac_config[1], cooling = fac_config[2] }
|
||||||
|
|
||||||
---@todo unit options
|
iocontrol.init_fac(conf)
|
||||||
iocontrol.init_fac(conf, 1)
|
|
||||||
|
|
||||||
log.info("coordinator connection established")
|
log.info("coordinator connection established")
|
||||||
self.establish_delay_counter = 0
|
self.establish_delay_counter = 0
|
||||||
@@ -380,9 +687,9 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.api.addr = src_addr
|
self.api.addr = src_addr
|
||||||
|
|
||||||
if self.sv.linked then
|
if self.sv.linked then
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
iocontrol.report_link_state(LINK_STATE.LINKED, nil, self.api.addr)
|
||||||
else
|
else
|
||||||
iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY)
|
iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY, nil, self.api.addr)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("invalid facility configuration table received from coordinator, establish failed")
|
log.debug("invalid facility configuration table received from coordinator, establish failed")
|
||||||
@@ -421,8 +728,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
elseif r_chan == config.SVR_Channel then
|
elseif r_chan == config.SVR_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.sv.r_seq_num == nil then
|
if self.sv.r_seq_num == nil then
|
||||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.sv.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.sv.linked and (src_addr ~= self.sv.addr) then
|
elseif self.sv.linked and (src_addr ~= self.sv.addr) then
|
||||||
@@ -430,7 +737,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@@ -459,6 +766,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
elseif packet.type == MGMT_TYPE.CLOSE then
|
elseif packet.type == MGMT_TYPE.CLOSE then
|
||||||
-- handle session close
|
-- handle session close
|
||||||
sv_watchdog.cancel()
|
sv_watchdog.cancel()
|
||||||
|
nav.unload_sv()
|
||||||
self.sv.linked = false
|
self.sv.linked = false
|
||||||
self.sv.r_seq_num = nil
|
self.sv.r_seq_num = nil
|
||||||
self.sv.addr = comms.BROADCAST
|
self.sv.addr = comms.BROADCAST
|
||||||
@@ -519,9 +827,9 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.sv.addr = src_addr
|
self.sv.addr = src_addr
|
||||||
|
|
||||||
if self.api.linked then
|
if self.api.linked then
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, nil)
|
||||||
else
|
else
|
||||||
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY)
|
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY, self.sv.addr, nil)
|
||||||
end
|
end
|
||||||
elseif est_ack == ESTABLISH_ACK.DENY then
|
elseif est_ack == ESTABLISH_ACK.DENY then
|
||||||
if self.sv.last_est_ack ~= est_ack then
|
if self.sv.last_est_ack ~= est_ack then
|
||||||
@@ -559,6 +867,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_api_linked() return self.api.linked end
|
function public.is_api_linked() return self.api.linked end
|
||||||
|
|
||||||
|
-- check if we are still linked with the supervisor and coordinator
|
||||||
|
---@nodiscard
|
||||||
|
function public.is_linked() return self.sv.linked and self.api.linked end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -92,4 +92,20 @@ function renderer.handle_mouse(event)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- handle a keyboard event
|
||||||
|
---@param event key_interaction|nil
|
||||||
|
function renderer.handle_key(event)
|
||||||
|
if ui.display ~= nil and event ~= nil then
|
||||||
|
ui.display.handle_key(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle a paste event
|
||||||
|
---@param text string
|
||||||
|
function renderer.handle_paste(text)
|
||||||
|
if ui.display ~= nil then
|
||||||
|
ui.display.handle_paste(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return renderer
|
return renderer
|
||||||
|
|||||||
@@ -2,27 +2,35 @@
|
|||||||
-- SCADA System Access on a Pocket Computer
|
-- SCADA System Access on a Pocket Computer
|
||||||
--
|
--
|
||||||
|
|
||||||
|
---@diagnostic disable-next-line: undefined-global
|
||||||
|
local _is_pocket_env = pocket or periphemu -- luacheck: ignore pocket
|
||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local mqueue = require("scada-common.mqueue")
|
||||||
local network = require("scada-common.network")
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
|
||||||
|
|
||||||
local configure = require("pocket.configure")
|
local configure = require("pocket.configure")
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
local pocket = require("pocket.pocket")
|
local pocket = require("pocket.pocket")
|
||||||
local renderer = require("pocket.renderer")
|
local renderer = require("pocket.renderer")
|
||||||
|
local threads = require("pocket.threads")
|
||||||
|
|
||||||
local POCKET_VERSION = "v0.9.0-alpha"
|
local POCKET_VERSION = "v0.11.4-alpha"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
|
||||||
|
-- check environment (allows Pocket or CraftOS-PC)
|
||||||
|
if not _is_pocket_env then
|
||||||
|
println("You can only use this application on a pocket computer.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- get configuration
|
-- get configuration
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
@@ -72,9 +80,51 @@ local function main()
|
|||||||
iocontrol.get_db().version = POCKET_VERSION
|
iocontrol.get_db().version = POCKET_VERSION
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- setup communications & clocks
|
-- memory allocation
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
-- shared memory across threads
|
||||||
|
---@class pkt_shared_memory
|
||||||
|
local __shared_memory = {
|
||||||
|
-- pocket system state flags
|
||||||
|
---@class pkt_state
|
||||||
|
pkt_state = {
|
||||||
|
ui_ok = false,
|
||||||
|
ui_error = nil,
|
||||||
|
shutdown = false
|
||||||
|
},
|
||||||
|
|
||||||
|
-- core pocket devices
|
||||||
|
pkt_dev = {
|
||||||
|
modem = ppm.get_wireless_modem()
|
||||||
|
},
|
||||||
|
|
||||||
|
-- system objects
|
||||||
|
pkt_sys = {
|
||||||
|
nic = nil, ---@type nic
|
||||||
|
pocket_comms = nil, ---@type pocket_comms
|
||||||
|
sv_wd = nil, ---@type watchdog
|
||||||
|
api_wd = nil, ---@type watchdog
|
||||||
|
nav = nil ---@type pocket_nav
|
||||||
|
},
|
||||||
|
|
||||||
|
-- message queues
|
||||||
|
q = {
|
||||||
|
mq_render = mqueue.new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local smem_dev = __shared_memory.pkt_dev
|
||||||
|
local smem_sys = __shared_memory.pkt_sys
|
||||||
|
|
||||||
|
local pkt_state = __shared_memory.pkt_state
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- setup system
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
smem_sys.nav = pocket.init_nav(__shared_memory)
|
||||||
|
|
||||||
-- message authentication init
|
-- message authentication init
|
||||||
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
||||||
network.init_mac(config.AuthKey)
|
network.init_mac(config.AuthKey)
|
||||||
@@ -83,112 +133,59 @@ local function main()
|
|||||||
iocontrol.report_link_state(iocontrol.LINK_STATE.UNLINKED)
|
iocontrol.report_link_state(iocontrol.LINK_STATE.UNLINKED)
|
||||||
|
|
||||||
-- get the communications modem
|
-- get the communications modem
|
||||||
local modem = ppm.get_wireless_modem()
|
if smem_dev.modem == nil then
|
||||||
if modem == nil then
|
|
||||||
println("startup> wireless modem not found: please craft the pocket computer with a wireless modem")
|
println("startup> wireless modem not found: please craft the pocket computer with a wireless modem")
|
||||||
log.fatal("startup> no wireless modem on startup")
|
log.fatal("startup> no wireless modem on startup")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create connection watchdogs
|
-- create connection watchdogs
|
||||||
local conn_wd = {
|
smem_sys.sv_wd = util.new_watchdog(config.ConnTimeout)
|
||||||
sv = util.new_watchdog(config.ConnTimeout),
|
smem_sys.sv_wd.cancel()
|
||||||
api = util.new_watchdog(config.ConnTimeout)
|
smem_sys.api_wd = util.new_watchdog(config.ConnTimeout)
|
||||||
}
|
smem_sys.api_wd.cancel()
|
||||||
|
|
||||||
conn_wd.sv.cancel()
|
|
||||||
conn_wd.api.cancel()
|
|
||||||
|
|
||||||
log.debug("startup> conn watchdogs created")
|
log.debug("startup> conn watchdogs created")
|
||||||
|
|
||||||
-- create network interface then setup comms
|
-- create network interface then setup comms
|
||||||
local nic = network.nic(modem)
|
smem_sys.nic = network.nic(smem_dev.modem)
|
||||||
local pocket_comms = pocket.comms(POCKET_VERSION, nic, conn_wd.sv, conn_wd.api)
|
smem_sys.pocket_comms = pocket.comms(POCKET_VERSION, smem_sys.nic, smem_sys.sv_wd, smem_sys.api_wd, smem_sys.nav)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
|
|
||||||
-- base loop clock (2Hz, 10 ticks)
|
|
||||||
local MAIN_CLOCK = 0.5
|
|
||||||
local loop_clock = util.new_clock(MAIN_CLOCK)
|
|
||||||
|
|
||||||
-- init I/O control
|
-- init I/O control
|
||||||
iocontrol.init_core(pocket_comms)
|
iocontrol.init_core(smem_sys.pocket_comms, smem_sys.nav, config)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- start the UI
|
-- start the UI
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local ui_ok, message = renderer.try_start_ui()
|
local ui_message
|
||||||
if not ui_ok then
|
pkt_state.ui_ok, ui_message = renderer.try_start_ui()
|
||||||
println(util.c("UI error: ", message))
|
if not pkt_state.ui_ok then
|
||||||
log.error(util.c("startup> GUI render failed with error ", message))
|
println(util.c("UI error: ", ui_message))
|
||||||
else
|
log.error(util.c("startup> GUI render failed with error ", ui_message))
|
||||||
-- start clock
|
|
||||||
loop_clock.start()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main event loop
|
-- start system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
if ui_ok then
|
if pkt_state.ui_ok then
|
||||||
-- start connection watchdogs
|
-- init threads
|
||||||
conn_wd.sv.feed()
|
local main_thread = threads.thread__main(__shared_memory)
|
||||||
conn_wd.api.feed()
|
local render_thread = threads.thread__render(__shared_memory)
|
||||||
log.debug("startup> conn watchdogs started")
|
|
||||||
|
|
||||||
local io_db = iocontrol.get_db()
|
log.info("startup> completed")
|
||||||
local nav = io_db.nav
|
|
||||||
|
|
||||||
-- main event loop
|
-- run threads
|
||||||
while true do
|
parallel.waitForAll(main_thread.p_exec, render_thread.p_exec)
|
||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
|
||||||
|
|
||||||
-- handle event
|
|
||||||
if event == "timer" then
|
|
||||||
if loop_clock.is_clock(param1) then
|
|
||||||
-- main loop tick
|
|
||||||
|
|
||||||
-- relink if necessary
|
|
||||||
pocket_comms.link_update()
|
|
||||||
|
|
||||||
-- update any tasks for the active page
|
|
||||||
local page_tasks = nav.get_current_page().tasks
|
|
||||||
for i = 1, #page_tasks do page_tasks[i]() end
|
|
||||||
|
|
||||||
loop_clock.start()
|
|
||||||
elseif conn_wd.sv.is_timer(param1) then
|
|
||||||
-- supervisor watchdog timeout
|
|
||||||
log.info("supervisor server timeout")
|
|
||||||
pocket_comms.close_sv()
|
|
||||||
elseif conn_wd.api.is_timer(param1) then
|
|
||||||
-- coordinator watchdog timeout
|
|
||||||
log.info("coordinator api server timeout")
|
|
||||||
pocket_comms.close_api()
|
|
||||||
else
|
|
||||||
-- a non-clock/main watchdog timer event
|
|
||||||
-- notify timer callback dispatcher
|
|
||||||
tcd.handle(param1)
|
|
||||||
end
|
|
||||||
elseif event == "modem_message" then
|
|
||||||
-- got a packet
|
|
||||||
local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5)
|
|
||||||
pocket_comms.handle_packet(packet)
|
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
|
||||||
event == "double_click" then
|
|
||||||
-- handle a monitor touch event
|
|
||||||
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for termination request
|
|
||||||
if event == "terminate" or ppm.should_terminate() then
|
|
||||||
log.info("terminate requested, closing server connections...")
|
|
||||||
pocket_comms.close()
|
|
||||||
log.info("connections closed")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
renderer.close_ui()
|
renderer.close_ui()
|
||||||
|
|
||||||
|
if not pkt_state.ui_ok then
|
||||||
|
println(util.c("UI crashed with error: ", pkt_state.ui_error))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
println_ts("UI creation failed")
|
||||||
end
|
end
|
||||||
|
|
||||||
println_ts("exited")
|
println_ts("exited")
|
||||||
|
|||||||
219
pocket/threads.lua
Normal file
219
pocket/threads.lua
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
local log = require("scada-common.log")
|
||||||
|
local mqueue = require("scada-common.mqueue")
|
||||||
|
local ppm = require("scada-common.ppm")
|
||||||
|
local tcd = require("scada-common.tcd")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
local renderer = require("pocket.renderer")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local threads = {}
|
||||||
|
|
||||||
|
local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks)
|
||||||
|
local RENDER_SLEEP = 100 -- (100ms, 2 ticks)
|
||||||
|
|
||||||
|
local MQ__RENDER_CMD = pocket.MQ__RENDER_CMD
|
||||||
|
local MQ__RENDER_DATA = pocket.MQ__RENDER_DATA
|
||||||
|
|
||||||
|
-- main thread
|
||||||
|
---@nodiscard
|
||||||
|
---@param smem pkt_shared_memory
|
||||||
|
function threads.thread__main(smem)
|
||||||
|
---@class parallel_thread
|
||||||
|
local public = {}
|
||||||
|
|
||||||
|
-- execute thread
|
||||||
|
function public.exec()
|
||||||
|
log.debug("main thread start")
|
||||||
|
|
||||||
|
local loop_clock = util.new_clock(MAIN_CLOCK)
|
||||||
|
|
||||||
|
-- start clock
|
||||||
|
loop_clock.start()
|
||||||
|
|
||||||
|
-- load in from shared memory
|
||||||
|
local pkt_state = smem.pkt_state
|
||||||
|
local pocket_comms = smem.pkt_sys.pocket_comms
|
||||||
|
local sv_wd = smem.pkt_sys.sv_wd
|
||||||
|
local api_wd = smem.pkt_sys.api_wd
|
||||||
|
local nav = smem.pkt_sys.nav
|
||||||
|
|
||||||
|
-- start connection watchdogs
|
||||||
|
sv_wd.feed()
|
||||||
|
api_wd.feed()
|
||||||
|
log.debug("startup> conn watchdogs started")
|
||||||
|
|
||||||
|
-- event loop
|
||||||
|
while true do
|
||||||
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
|
-- handle event
|
||||||
|
if event == "timer" then
|
||||||
|
if loop_clock.is_clock(param1) then
|
||||||
|
-- main loop tick
|
||||||
|
|
||||||
|
-- relink if necessary
|
||||||
|
pocket_comms.link_update()
|
||||||
|
|
||||||
|
-- update any tasks for the active page
|
||||||
|
local page_tasks = nav.get_current_page().tasks
|
||||||
|
for i = 1, #page_tasks do page_tasks[i]() end
|
||||||
|
|
||||||
|
loop_clock.start()
|
||||||
|
elseif sv_wd.is_timer(param1) then
|
||||||
|
-- supervisor watchdog timeout
|
||||||
|
log.info("supervisor server timeout")
|
||||||
|
pocket_comms.close_sv()
|
||||||
|
elseif api_wd.is_timer(param1) then
|
||||||
|
-- coordinator watchdog timeout
|
||||||
|
log.info("coordinator api server timeout")
|
||||||
|
pocket_comms.close_api()
|
||||||
|
else
|
||||||
|
-- a non-clock/main watchdog timer event
|
||||||
|
-- notify timer callback dispatcher
|
||||||
|
tcd.handle(param1)
|
||||||
|
end
|
||||||
|
elseif event == "modem_message" then
|
||||||
|
-- got a packet
|
||||||
|
local packet = pocket_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
pocket_comms.handle_packet(packet)
|
||||||
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
||||||
|
event == "double_click" then
|
||||||
|
-- handle a mouse event
|
||||||
|
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
||||||
|
elseif event == "char" or event == "key" or event == "key_up" then
|
||||||
|
-- handle a keyboard event
|
||||||
|
renderer.handle_key(core.events.new_key_event(event, param1, param2))
|
||||||
|
elseif event == "paste" then
|
||||||
|
-- handle a paste event
|
||||||
|
renderer.handle_paste(param1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request or UI crash
|
||||||
|
if event == "terminate" or ppm.should_terminate() then
|
||||||
|
log.info("terminate requested, main thread exiting")
|
||||||
|
pkt_state.shutdown = true
|
||||||
|
elseif not pkt_state.ui_ok then
|
||||||
|
pkt_state.shutdown = true
|
||||||
|
log.info("terminating due to fatal UI error")
|
||||||
|
end
|
||||||
|
|
||||||
|
if pkt_state.shutdown then
|
||||||
|
log.info("closing server connections...")
|
||||||
|
pocket_comms.close()
|
||||||
|
log.info("connections closed")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute the thread in a protected mode, retrying it on return if not shutting down
|
||||||
|
function public.p_exec()
|
||||||
|
local pkt_state = smem.pkt_state
|
||||||
|
|
||||||
|
while not pkt_state.shutdown do
|
||||||
|
local status, result = pcall(public.exec)
|
||||||
|
if status == false then
|
||||||
|
log.fatal(util.strval(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if status is true, then we are probably exiting, so this won't matter
|
||||||
|
-- this thread cannot be slept because it will miss events (namely "terminate")
|
||||||
|
if not pkt_state.shutdown then
|
||||||
|
log.info("main thread restarting now...")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return public
|
||||||
|
end
|
||||||
|
|
||||||
|
-- pocket renderer thread, tasked with long duration draws
|
||||||
|
---@nodiscard
|
||||||
|
---@param smem pkt_shared_memory
|
||||||
|
function threads.thread__render(smem)
|
||||||
|
---@class parallel_thread
|
||||||
|
local public = {}
|
||||||
|
|
||||||
|
-- execute thread
|
||||||
|
function public.exec()
|
||||||
|
log.debug("render thread start")
|
||||||
|
|
||||||
|
-- load in from shared memory
|
||||||
|
local pkt_state = smem.pkt_state
|
||||||
|
local nav = smem.pkt_sys.nav
|
||||||
|
local render_queue = smem.q.mq_render
|
||||||
|
|
||||||
|
local last_update = util.time()
|
||||||
|
|
||||||
|
-- thread loop
|
||||||
|
while true do
|
||||||
|
-- check for messages in the message queue
|
||||||
|
while render_queue.ready() and not pkt_state.shutdown do
|
||||||
|
local msg = render_queue.pop()
|
||||||
|
|
||||||
|
if msg ~= nil then
|
||||||
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
|
-- received a command
|
||||||
|
if msg.message == MQ__RENDER_CMD.UNLOAD_SV_APPS then
|
||||||
|
elseif msg.message == MQ__RENDER_CMD.UNLOAD_API_APPS then
|
||||||
|
end
|
||||||
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
|
-- received data
|
||||||
|
local cmd = msg.message ---@type queue_data
|
||||||
|
|
||||||
|
if cmd.key == MQ__RENDER_DATA.LOAD_APP then
|
||||||
|
log.debug("RENDER: load app " .. cmd.val)
|
||||||
|
|
||||||
|
local draw_start = util.time_ms()
|
||||||
|
|
||||||
|
pkt_state.ui_ok, pkt_state.ui_error = pcall(function () nav.load_app(cmd.val) end)
|
||||||
|
if not pkt_state.ui_ok then
|
||||||
|
log.fatal(util.c("RENDER: app load failed with error ", pkt_state.ui_error))
|
||||||
|
else
|
||||||
|
log.debug("RENDER: app loaded in " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif msg.qtype == mqueue.TYPE.PACKET then
|
||||||
|
-- received a packet
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- quick yield
|
||||||
|
util.nop()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request
|
||||||
|
if pkt_state.shutdown then
|
||||||
|
log.info("render thread exiting")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delay before next check
|
||||||
|
last_update = util.adaptive_delay(RENDER_SLEEP, last_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute the thread in a protected mode, retrying it on return if not shutting down
|
||||||
|
function public.p_exec()
|
||||||
|
local pkt_state = smem.pkt_state
|
||||||
|
|
||||||
|
while not pkt_state.shutdown do
|
||||||
|
local status, result = pcall(public.exec)
|
||||||
|
if status == false then
|
||||||
|
log.fatal(util.strval(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
if not pkt_state.shutdown then
|
||||||
|
log.info("render thread restarting in 5 seconds...")
|
||||||
|
util.psleep(5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return public
|
||||||
|
end
|
||||||
|
|
||||||
|
return threads
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
@@ -15,9 +16,10 @@ local Checkbox = require("graphics.elements.controls.checkbox")
|
|||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
local SwitchButton = require("graphics.elements.controls.switch_button")
|
local SwitchButton = require("graphics.elements.controls.switch_button")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
-- create diagnostic app pages
|
-- create diagnostic app pages
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
@@ -30,7 +32,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local alarm_test = Div{parent=root,x=1,y=1}
|
local alarm_test = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
local alarm_app = db.nav.register_app(iocontrol.APP_ID.ALARMS, alarm_test)
|
local alarm_app = db.nav.register_app(APP_ID.ALARMS, alarm_test, nil, true)
|
||||||
|
|
||||||
local page = alarm_app.new_page(nil, function () end)
|
local page = alarm_app.new_page(nil, function () end)
|
||||||
page.tasks = { db.diag.tone_test.get_tone_states }
|
page.tasks = { db.diag.tone_test.get_tone_states }
|
||||||
@@ -44,13 +46,13 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local audio = Div{parent=alarm_test,x=1,y=1}
|
local audio = Div{parent=alarm_test,x=1,y=1}
|
||||||
|
|
||||||
TextBox{parent=audio,y=1,text="Alarm Sounder Tests",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=audio,y=1,text="Alarm Sounder Tests",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
ttest.ready_warn = TextBox{parent=audio,y=2,text="",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.yellow,colors.black)}
|
ttest.ready_warn = TextBox{parent=audio,y=2,text="",alignment=ALIGN.CENTER,fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
local tones = Div{parent=audio,x=2,y=3,height=10,width=8,fg_bg=cpair(colors.black,colors.yellow)}
|
local tones = Div{parent=audio,x=2,y=3,height=10,width=8,fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
TextBox{parent=tones,text="Tones",height=1,alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
TextBox{parent=tones,text="Tones",alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
||||||
|
|
||||||
local test_btns = {}
|
local test_btns = {}
|
||||||
test_btns[1] = SwitchButton{parent=tones,text="TEST 1",min_width=8,active_fg_bg=c_wht_gray,callback=ttest.test_1}
|
test_btns[1] = SwitchButton{parent=tones,text="TEST 1",min_width=8,active_fg_bg=c_wht_gray,callback=ttest.test_1}
|
||||||
@@ -73,7 +75,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local alarms = Div{parent=audio,x=11,y=3,height=15,fg_bg=cpair(colors.lightGray,colors.black)}
|
local alarms = Div{parent=audio,x=11,y=3,height=15,fg_bg=cpair(colors.lightGray,colors.black)}
|
||||||
|
|
||||||
TextBox{parent=alarms,text="Alarms (\x13)",height=1,alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
TextBox{parent=alarms,text="Alarms (\x13)",alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
||||||
|
|
||||||
local alarm_btns = {}
|
local alarm_btns = {}
|
||||||
alarm_btns[1] = Checkbox{parent=alarms,label="BREACH",min_width=15,box_fg_bg=c_red_gray,callback=ttest.test_breach}
|
alarm_btns[1] = Checkbox{parent=alarms,label="BREACH",min_width=15,box_fg_bg=c_red_gray,callback=ttest.test_breach}
|
||||||
@@ -100,7 +102,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local states = Div{parent=audio,x=2,y=14,height=5,width=8}
|
local states = Div{parent=audio,x=2,y=14,height=5,width=8}
|
||||||
|
|
||||||
TextBox{parent=states,text="States",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=states,text="States",alignment=ALIGN.CENTER}
|
||||||
local t_1 = IndicatorLight{parent=states,label="1",colors=c_blue_gray}
|
local t_1 = IndicatorLight{parent=states,label="1",colors=c_blue_gray}
|
||||||
local t_2 = IndicatorLight{parent=states,label="2",colors=c_blue_gray}
|
local t_2 = IndicatorLight{parent=states,label="2",colors=c_blue_gray}
|
||||||
local t_3 = IndicatorLight{parent=states,label="3",colors=c_blue_gray}
|
local t_3 = IndicatorLight{parent=states,label="3",colors=c_blue_gray}
|
||||||
|
|||||||
@@ -3,12 +3,15 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
-- create placeholder app page
|
-- create placeholder app page
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
local function create_pages(root)
|
local function create_pages(root)
|
||||||
@@ -16,7 +19,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
local main = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
db.nav.register_app(iocontrol.APP_ID.DUMMY, main).new_page(nil, function () end)
|
db.nav.register_app(APP_ID.DUMMY, main).new_page(nil, function () end)
|
||||||
|
|
||||||
TextBox{parent=main,text="This app is not implemented yet.",x=1,y=2,alignment=core.ALIGN.CENTER}
|
TextBox{parent=main,text="This app is not implemented yet.",x=1,y=2,alignment=core.ALIGN.CENTER}
|
||||||
|
|
||||||
|
|||||||
248
pocket/ui/apps/guide.lua
Normal file
248
pocket/ui/apps/guide.lua
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
--
|
||||||
|
-- System Guide
|
||||||
|
--
|
||||||
|
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
|
local docs = require("pocket.ui.docs")
|
||||||
|
-- local style = require("pocket.ui.style")
|
||||||
|
|
||||||
|
local guide_section = require("pocket.ui.pages.guide_section")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local WaitingAnim = require("graphics.elements.animations.waiting")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
|
-- local label = style.label
|
||||||
|
-- local lu_col = style.label_unit_pair
|
||||||
|
-- local text_fg = style.text_fg
|
||||||
|
|
||||||
|
-- new system guide view
|
||||||
|
---@param root graphics_element parent
|
||||||
|
local function new_view(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local frame = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
local app = db.nav.register_app(APP_ID.GUIDE, frame)
|
||||||
|
|
||||||
|
local load_div = Div{parent=frame,x=1,y=1}
|
||||||
|
local main = Div{parent=frame,x=1,y=1}
|
||||||
|
|
||||||
|
TextBox{parent=load_div,y=12,text="Loading...",alignment=ALIGN.CENTER}
|
||||||
|
WaitingAnim{parent=load_div,x=math.floor(main.get_width()/2)-1,y=8,fg_bg=cpair(colors.cyan,colors._INHERIT)}
|
||||||
|
|
||||||
|
local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}}
|
||||||
|
|
||||||
|
local btn_fg_bg = cpair(colors.cyan, colors.black)
|
||||||
|
local btn_active = cpair(colors.white, colors.black)
|
||||||
|
local btn_disable = cpair(colors.gray, colors.black)
|
||||||
|
|
||||||
|
app.set_sidebar({{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }})
|
||||||
|
|
||||||
|
local page_div = nil ---@type nil|graphics_element
|
||||||
|
|
||||||
|
-- load the app (create the elements)
|
||||||
|
local function load()
|
||||||
|
local list = {
|
||||||
|
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end },
|
||||||
|
{ label = " \x14 ", color = core.cpair(colors.black, colors.cyan), callback = function () app.switcher(1) end },
|
||||||
|
{ label = "__?", color = core.cpair(colors.black, colors.lightGray), callback = function () app.switcher(2) end }
|
||||||
|
}
|
||||||
|
|
||||||
|
app.set_sidebar(list)
|
||||||
|
|
||||||
|
page_div = Div{parent=main,y=2}
|
||||||
|
local p_width = page_div.get_width() - 2
|
||||||
|
|
||||||
|
local main_page = app.new_page(nil, 1)
|
||||||
|
local search_page = app.new_page(main_page, 2)
|
||||||
|
local use_page = app.new_page(main_page, 3)
|
||||||
|
local uis_page = app.new_page(main_page, 4)
|
||||||
|
local fps_page = app.new_page(main_page, 5)
|
||||||
|
local gls_page = app.new_page(main_page, 6)
|
||||||
|
|
||||||
|
local home = Div{parent=page_div,x=2}
|
||||||
|
local search = Div{parent=page_div,x=2}
|
||||||
|
local use = Div{parent=page_div,x=2,width=p_width}
|
||||||
|
local uis = Div{parent=page_div,x=2,width=p_width}
|
||||||
|
local fps = Div{parent=page_div,x=2,width=p_width}
|
||||||
|
local gls = Div{parent=page_div,x=2,width=p_width}
|
||||||
|
local panes = { home, search, use, uis, fps, gls }
|
||||||
|
|
||||||
|
local doc_map = {}
|
||||||
|
local search_db = {}
|
||||||
|
|
||||||
|
---@class _guide_section_constructor_data
|
||||||
|
local sect_construct_data = { app, page_div, panes, doc_map, search_db, btn_fg_bg, btn_active }
|
||||||
|
|
||||||
|
TextBox{parent=home,y=1,text="cc-mek-scada Guide",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
PushButton{parent=home,y=3,text="Search >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=search_page.nav_to}
|
||||||
|
PushButton{parent=home,y=5,text="System Usage >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=use_page.nav_to}
|
||||||
|
PushButton{parent=home,text="Operator UIs >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to}
|
||||||
|
PushButton{parent=home,text="Front Panels >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fps_page.nav_to}
|
||||||
|
PushButton{parent=home,text="Glossary >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_page.nav_to}
|
||||||
|
|
||||||
|
TextBox{parent=search,y=1,text="Search",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local query_field = TextField{parent=search,x=1,y=3,width=18,fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
local func_ref = {}
|
||||||
|
|
||||||
|
PushButton{parent=search,x=20,y=3,text="GO",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()func_ref.run_search()end}
|
||||||
|
|
||||||
|
local search_results = ListBox{parent=search,x=1,y=5,scroll_height=200,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
function func_ref.run_search()
|
||||||
|
local query = string.lower(query_field.get_value())
|
||||||
|
local s_results = { {}, {}, {} }
|
||||||
|
|
||||||
|
search_results.remove_all()
|
||||||
|
|
||||||
|
if string.len(query) < 3 then
|
||||||
|
TextBox{parent=search_results,text="Search requires at least 3 characters."}
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, entry in ipairs(search_db) do
|
||||||
|
local s_start, _ = string.find(entry[1], query, 1, true)
|
||||||
|
|
||||||
|
if s_start == nil then
|
||||||
|
elseif s_start == 1 then
|
||||||
|
-- best match, start of key
|
||||||
|
table.insert(s_results[1], entry)
|
||||||
|
elseif string.sub(query, s_start - 1, s_start) == " " then
|
||||||
|
-- start of word, good match
|
||||||
|
table.insert(s_results[2], entry)
|
||||||
|
else
|
||||||
|
-- basic match in content
|
||||||
|
table.insert(s_results[3], entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local empty = true
|
||||||
|
|
||||||
|
for tier = 1, 3 do
|
||||||
|
for idx = 1, #s_results[tier] do
|
||||||
|
local entry = s_results[tier][idx]
|
||||||
|
TextBox{parent=search_results,text=entry[3].." >",fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
PushButton{parent=search_results,text=entry[2],alignment=ALIGN.LEFT,fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=entry[4]}
|
||||||
|
|
||||||
|
empty = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if empty then
|
||||||
|
TextBox{parent=search_results,text="No results found."}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=search_results,text="Click 'GO' to search..."}
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
|
TextBox{parent=use,y=1,text="System Usage",alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=use,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to}
|
||||||
|
|
||||||
|
PushButton{parent=use,y=3,text="Configuring Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=use,text="Connecting Devices >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=use,text="Manual Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=use,text="Automatic Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=use,text="Waste Control >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
|
||||||
|
TextBox{parent=uis,y=1,text="Operator UIs",alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=uis,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to}
|
||||||
|
|
||||||
|
local annunc_page = app.new_page(uis_page, #panes + 1)
|
||||||
|
local annunc_div = Div{parent=page_div,x=2}
|
||||||
|
table.insert(panes, annunc_div)
|
||||||
|
|
||||||
|
local alarms_page = guide_section(sect_construct_data, uis_page, "Alarms", docs.alarms, 100)
|
||||||
|
|
||||||
|
PushButton{parent=uis,y=3,text="Alarms >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=alarms_page.nav_to}
|
||||||
|
PushButton{parent=uis,text="Annunciators >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=annunc_page.nav_to}
|
||||||
|
PushButton{parent=uis,text="Pocket UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=uis,text="Coordinator UI >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
|
||||||
|
TextBox{parent=annunc_div,y=1,text="Annunciators",alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=annunc_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=uis_page.nav_to}
|
||||||
|
|
||||||
|
local unit_gen_page = guide_section(sect_construct_data, annunc_page, "Unit General", docs.annunc.unit.main_section, 170)
|
||||||
|
local unit_rps_page = guide_section(sect_construct_data, annunc_page, "Unit RPS", docs.annunc.unit.rps_section, 100)
|
||||||
|
local unit_rcs_page = guide_section(sect_construct_data, annunc_page, "Unit RCS", docs.annunc.unit.rcs_section, 170)
|
||||||
|
local fac_annunc_page = guide_section(sect_construct_data, annunc_page, "Facility", docs.annunc.unit.fac_section, 100)
|
||||||
|
|
||||||
|
PushButton{parent=annunc_div,y=3,text="Unit General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_gen_page.nav_to}
|
||||||
|
PushButton{parent=annunc_div,text="Unit RPS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rps_page.nav_to}
|
||||||
|
PushButton{parent=annunc_div,text="Unit RCS >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=unit_rcs_page.nav_to}
|
||||||
|
PushButton{parent=annunc_div,text="Facility General >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fac_annunc_page.nav_to}
|
||||||
|
PushButton{parent=annunc_div,text="Waste & Valves >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
|
||||||
|
TextBox{parent=fps,y=1,text="Front Panels",alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=fps,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to}
|
||||||
|
|
||||||
|
PushButton{parent=fps,y=3,text="Common Items >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=fps,text="Reactor PLC >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=fps,text="RTU Gateway >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=fps,text="Supervisor >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
PushButton{parent=fps,text="Coordinator >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||||
|
|
||||||
|
TextBox{parent=gls,y=1,text="Glossary",alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=gls,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=main_page.nav_to}
|
||||||
|
|
||||||
|
local gls_abbv_page = guide_section(sect_construct_data, gls_page, "Abbreviations", docs.glossary.abbvs, 130)
|
||||||
|
local gls_term_page = guide_section(sect_construct_data, gls_page, "Terminology", docs.glossary.terms, 100)
|
||||||
|
|
||||||
|
PushButton{parent=gls,y=3,text="Abbreviations >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_abbv_page.nav_to}
|
||||||
|
PushButton{parent=gls,text="Terminology >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=gls_term_page.nav_to}
|
||||||
|
|
||||||
|
-- setup multipane
|
||||||
|
local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
||||||
|
app.set_root_pane(u_pane)
|
||||||
|
|
||||||
|
-- link help resources
|
||||||
|
db.nav.link_help(doc_map)
|
||||||
|
|
||||||
|
-- done, show the app
|
||||||
|
load_pane.set_value(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delete the elements and switch back to the loading screen
|
||||||
|
local function unload()
|
||||||
|
if page_div then
|
||||||
|
page_div.delete()
|
||||||
|
page_div = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } })
|
||||||
|
app.delete_pages()
|
||||||
|
|
||||||
|
-- show loading screen
|
||||||
|
load_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
app.set_load(load)
|
||||||
|
app.set_unload(unload)
|
||||||
|
|
||||||
|
return main
|
||||||
|
end
|
||||||
|
|
||||||
|
return new_view
|
||||||
49
pocket/ui/apps/loader.lua
Normal file
49
pocket/ui/apps/loader.lua
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
--
|
||||||
|
-- Loading Screen App
|
||||||
|
--
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
|
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
|
local LINK_STATE = iocontrol.LINK_STATE
|
||||||
|
|
||||||
|
-- create the connecting to SV & API page
|
||||||
|
---@param root graphics_element parent
|
||||||
|
local function create_pages(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local main = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
db.nav.register_app(APP_ID.LOADER, main).new_page(nil, function () end)
|
||||||
|
|
||||||
|
local conn_sv_wait = conn_waiting(main, 6, false)
|
||||||
|
local conn_api_wait = conn_waiting(main, 6, true)
|
||||||
|
local main_pane = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
|
local root_pane = MultiPane{parent=main,x=1,y=1,panes={conn_sv_wait,conn_api_wait,main_pane}}
|
||||||
|
|
||||||
|
root_pane.register(db.ps, "link_state", function (state)
|
||||||
|
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
||||||
|
root_pane.set_value(1)
|
||||||
|
elseif state == LINK_STATE.SV_LINK_ONLY then
|
||||||
|
root_pane.set_value(2)
|
||||||
|
else
|
||||||
|
root_pane.set_value(3)
|
||||||
|
db.nav.on_loader_connected()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
TextBox{parent=main_pane,text="Connected!",x=1,y=6,alignment=core.ALIGN.CENTER}
|
||||||
|
end
|
||||||
|
|
||||||
|
return create_pages
|
||||||
@@ -19,9 +19,10 @@ local TextBox = require("graphics.elements.textbox")
|
|||||||
|
|
||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
-- create system app pages
|
-- create system app pages
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
@@ -34,7 +35,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local about_root = Div{parent=root,x=1,y=1}
|
local about_root = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
local about_app = db.nav.register_app(iocontrol.APP_ID.ABOUT, about_root)
|
local about_app = db.nav.register_app(APP_ID.ABOUT, about_root)
|
||||||
|
|
||||||
local about_page = about_app.new_page(nil, 1)
|
local about_page = about_app.new_page(nil, 1)
|
||||||
local nt_page = about_app.new_page(about_page, 2)
|
local nt_page = about_app.new_page(about_page, 2)
|
||||||
@@ -43,7 +44,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local about = Div{parent=about_root,x=1,y=2}
|
local about = Div{parent=about_root,x=1,y=2}
|
||||||
|
|
||||||
TextBox{parent=about,y=1,text="System Information",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=about,y=1,text="System Information",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
local btn_fg_bg = cpair(colors.lightBlue, colors.black)
|
local btn_fg_bg = cpair(colors.lightBlue, colors.black)
|
||||||
local btn_active = cpair(colors.white, colors.black)
|
local btn_active = cpair(colors.white, colors.black)
|
||||||
@@ -58,36 +59,36 @@ local function create_pages(root)
|
|||||||
local config = pocket.config
|
local config = pocket.config
|
||||||
|
|
||||||
local nt_div = Div{parent=about_root,x=1,y=2}
|
local nt_div = Div{parent=about_root,x=1,y=2}
|
||||||
TextBox{parent=nt_div,y=1,text="Network Details",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=nt_div,y=1,text="Network Details",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
PushButton{parent=nt_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
PushButton{parent=nt_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
TextBox{parent=nt_div,x=2,y=3,text="Pocket Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=nt_div,x=2,y=3,text="Pocket Address",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
TextBox{parent=nt_div,x=2,text=util.c(os.getComputerID(),":",config.PKT_Channel),height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=nt_div,x=2,text=util.c(os.getComputerID(),":",config.PKT_Channel),alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
nt_div.line_break()
|
nt_div.line_break()
|
||||||
TextBox{parent=nt_div,x=2,text="Supervisor Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=nt_div,x=2,text="Supervisor Address",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
local sv = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
local sv = TextBox{parent=nt_div,x=2,text="",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
nt_div.line_break()
|
nt_div.line_break()
|
||||||
TextBox{parent=nt_div,x=2,text="Coordinator Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=nt_div,x=2,text="Coordinator Address",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
local coord = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
local coord = TextBox{parent=nt_div,x=2,text="",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
sv.register(db.ps, "sv_addr", function (addr) sv.set_value(util.c(addr, ":", config.SVR_Channel)) end)
|
sv.register(db.ps, "sv_addr", sv.set_value)
|
||||||
coord.register(db.ps, "api_addr", function (addr) coord.set_value(util.c(addr, ":", config.CRD_Channel)) end)
|
coord.register(db.ps, "api_addr", coord.set_value)
|
||||||
|
|
||||||
nt_div.line_break()
|
nt_div.line_break()
|
||||||
TextBox{parent=nt_div,x=2,text="Message Authentication",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=nt_div,x=2,text="Message Authentication",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
local auth = util.trinary(type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0, "HMAC-MD5", "None")
|
local auth = util.trinary(type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0, "HMAC-MD5", "None")
|
||||||
TextBox{parent=nt_div,x=2,text=auth,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=nt_div,x=2,text=auth,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Firmware Versions
|
--#region Firmware Versions
|
||||||
|
|
||||||
local fw_div = Div{parent=about_root,x=1,y=2}
|
local fw_div = Div{parent=about_root,x=1,y=2}
|
||||||
TextBox{parent=fw_div,y=1,text="Firmware Versions",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=fw_div,y=1,text="Firmware Versions",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
PushButton{parent=fw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
PushButton{parent=fw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
@@ -95,44 +96,44 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local fw_list = Div{parent=fw_list_box,x=1,y=2,height=18}
|
local fw_list = Div{parent=fw_list_box,x=1,y=2,height=18}
|
||||||
|
|
||||||
TextBox{parent=fw_list,x=2,text="Pocket Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="Pocket Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=db.version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=db.version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_list.line_break()
|
fw_list.line_break()
|
||||||
TextBox{parent=fw_list,x=2,text="Comms Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="Comms Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=comms.version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=comms.version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_list.line_break()
|
fw_list.line_break()
|
||||||
TextBox{parent=fw_list,x=2,text="API Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="API Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=comms.api_version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=comms.api_version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_list.line_break()
|
fw_list.line_break()
|
||||||
TextBox{parent=fw_list,x=2,text="Common Lib Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="Common Lib Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=util.version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=util.version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_list.line_break()
|
fw_list.line_break()
|
||||||
TextBox{parent=fw_list,x=2,text="Graphics Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="Graphics Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=core.version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=core.version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_list.line_break()
|
fw_list.line_break()
|
||||||
TextBox{parent=fw_list,x=2,text="Lockbox Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=fw_list,x=2,text="Lockbox Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=fw_list,x=2,text=lockbox.version,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=fw_list,x=2,text=lockbox.version,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Host Versions
|
--#region Host Versions
|
||||||
|
|
||||||
local hw_div = Div{parent=about_root,x=1,y=2}
|
local hw_div = Div{parent=about_root,x=1,y=2}
|
||||||
TextBox{parent=hw_div,y=1,text="Host Versions",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=hw_div,y=1,text="Host Versions",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
PushButton{parent=hw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
PushButton{parent=hw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
hw_div.line_break()
|
hw_div.line_break()
|
||||||
TextBox{parent=hw_div,x=2,text="Lua Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=hw_div,x=2,text="Lua Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=hw_div,x=2,text=_VERSION,height=1,alignment=ALIGN.LEFT}
|
TextBox{parent=hw_div,x=2,text=_VERSION,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
hw_div.line_break()
|
hw_div.line_break()
|
||||||
TextBox{parent=hw_div,x=2,text="Environment",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
TextBox{parent=hw_div,x=2,text="Environment",alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
TextBox{parent=hw_div,x=2,text=_HOST,height=6,alignment=ALIGN.LEFT}
|
TextBox{parent=hw_div,x=2,text=_HOST,height=6,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|||||||
@@ -2,40 +2,43 @@
|
|||||||
-- Unit Overview Page
|
-- Unit Overview Page
|
||||||
--
|
--
|
||||||
|
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
-- local log = require("scada-common.log")
|
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local boiler = require("pocket.ui.pages.unit_boiler")
|
||||||
local MultiPane = require("graphics.elements.multipane")
|
local reactor = require("pocket.ui.pages.unit_reactor")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local turbine = require("pocket.ui.pages.unit_turbine")
|
||||||
|
|
||||||
local DataIndicator = require("graphics.elements.indicators.data")
|
local core = require("graphics.core")
|
||||||
local IconIndicator = require("graphics.elements.indicators.icon")
|
|
||||||
-- local RadIndicator = require("graphics.elements.indicators.rad")
|
|
||||||
-- local VerticalBar = require("graphics.elements.indicators.vbar")
|
|
||||||
|
|
||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local WaitingAnim = require("graphics.elements.animations.waiting")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local IconIndicator = require("graphics.elements.indicators.icon")
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local basic_states = {
|
local APP_ID = pocket.APP_ID
|
||||||
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
|
||||||
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
|
||||||
{ color = cpair(colors.black, colors.yellow), symbol = "\x1e" },
|
|
||||||
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
|
||||||
}
|
|
||||||
|
|
||||||
local mode_states = {
|
-- local label = style.label
|
||||||
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
local lu_col = style.label_unit_pair
|
||||||
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
local text_fg = style.text_fg
|
||||||
{ color = cpair(colors.black, colors.green), symbol = "+" },
|
local basic_states = style.icon_states.basic_states
|
||||||
{ color = cpair(colors.black, colors.purple), symbol = "A" }
|
local mode_states = style.icon_states.mode_states
|
||||||
}
|
local red_ind_s = style.icon_states.red_ind_s
|
||||||
|
local yel_ind_s = style.icon_states.yel_ind_s
|
||||||
|
|
||||||
local emc_ind_s = {
|
local emc_ind_s = {
|
||||||
{ color = cpair(colors.black, colors.gray), symbol = "-" },
|
{ color = cpair(colors.black, colors.gray), symbol = "-" },
|
||||||
@@ -43,60 +46,58 @@ local emc_ind_s = {
|
|||||||
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
||||||
}
|
}
|
||||||
|
|
||||||
local red_ind_s = {
|
|
||||||
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
|
||||||
{ color = cpair(colors.black, colors.red), symbol = "-" }
|
|
||||||
}
|
|
||||||
|
|
||||||
local yel_ind_s = {
|
|
||||||
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
|
||||||
{ color = cpair(colors.black, colors.yellow), symbol = "-" }
|
|
||||||
}
|
|
||||||
|
|
||||||
-- new unit page view
|
-- new unit page view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
local function new_view(root)
|
local function new_view(root)
|
||||||
local db = iocontrol.get_db()
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
local frame = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
local app = db.nav.register_app(iocontrol.APP_ID.UNITS, main)
|
local app = db.nav.register_app(APP_ID.UNITS, frame, nil, false, true)
|
||||||
|
|
||||||
TextBox{parent=main,y=2,text="Units App",height=1,alignment=ALIGN.CENTER}
|
local load_div = Div{parent=frame,x=1,y=1}
|
||||||
|
local main = Div{parent=frame,x=1,y=1}
|
||||||
|
|
||||||
TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=load_div,y=12,text="Loading...",alignment=ALIGN.CENTER}
|
||||||
|
WaitingAnim{parent=load_div,x=math.floor(main.get_width()/2)-1,y=8,fg_bg=cpair(colors.yellow,colors._INHERIT)}
|
||||||
|
|
||||||
|
local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}}
|
||||||
|
|
||||||
|
app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } })
|
||||||
|
|
||||||
local btn_fg_bg = cpair(colors.yellow, colors.black)
|
local btn_fg_bg = cpair(colors.yellow, colors.black)
|
||||||
local btn_active = cpair(colors.white, colors.black)
|
local btn_active = cpair(colors.white, colors.black)
|
||||||
-- local label = cpair(colors.lightGray, colors.black)
|
|
||||||
|
|
||||||
local nav_links = {}
|
local nav_links = {}
|
||||||
|
local page_div = nil ---@type nil|graphics_element
|
||||||
|
|
||||||
|
-- set sidebar to display unit-specific fields based on a specified unit
|
||||||
local function set_sidebar(id)
|
local function set_sidebar(id)
|
||||||
-- local unit = db.units[id] ---@type pioctl_unit
|
local unit = db.units[id] ---@type pioctl_unit
|
||||||
|
|
||||||
local list = {
|
local list = {
|
||||||
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end },
|
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end },
|
||||||
{ label = "U-" .. id, color = core.cpair(colors.black, colors.yellow), callback = function () app.switcher(id) end },
|
{ label = "U-" .. id, color = core.cpair(colors.black, colors.yellow), callback = function () app.switcher(id) end },
|
||||||
{ label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = nav_links[id].alarm },
|
{ label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = nav_links[id].alarm },
|
||||||
{ label = "RPS", tall = true, color = core.cpair(colors.black, colors.cyan), callback = nav_links[id].rps },
|
{ label = "RPS", tall = true, color = core.cpair(colors.black, colors.cyan), callback = nav_links[id].rps },
|
||||||
-- { label = " R ", color = core.cpair(colors.black, colors.lightGray), callback = function () end },
|
{ label = " R ", color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].reactor },
|
||||||
{ label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = nav_links[id].rcs },
|
{ label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = nav_links[id].rcs },
|
||||||
}
|
}
|
||||||
|
|
||||||
-- for i = 1, unit.num_boilers do
|
for i = 1, unit.num_boilers do
|
||||||
-- table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightBlue), callback = function () end })
|
table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].boiler[i] })
|
||||||
-- end
|
end
|
||||||
|
|
||||||
-- for i = 1, unit.num_turbines do
|
for i = 1, unit.num_turbines do
|
||||||
-- table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end })
|
table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.lightGray), callback = nav_links[id].turbine[i] })
|
||||||
-- end
|
end
|
||||||
|
|
||||||
app.set_sidebar(list)
|
app.set_sidebar(list)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- load the app (create the elements)
|
||||||
local function load()
|
local function load()
|
||||||
local page_div = Div{parent=main,x=2,y=2,width=main.get_width()-2}
|
page_div = Div{parent=main,y=2,width=main.get_width()}
|
||||||
|
|
||||||
local panes = {}
|
local panes = {}
|
||||||
|
|
||||||
@@ -124,7 +125,8 @@ local function new_view(root)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, db.facility.num_units do
|
for i = 1, db.facility.num_units do
|
||||||
local u_div = panes[i] ---@type graphics_element
|
local u_pane = panes[i]
|
||||||
|
local u_div = Div{parent=u_pane,x=2,width=main.get_width()-2}
|
||||||
local unit = db.units[i] ---@type pioctl_unit
|
local unit = db.units[i] ---@type pioctl_unit
|
||||||
local u_ps = unit.unit_ps
|
local u_ps = unit.unit_ps
|
||||||
|
|
||||||
@@ -142,23 +144,20 @@ local function new_view(root)
|
|||||||
local u_page = app.new_page(nil, i)
|
local u_page = app.new_page(nil, i)
|
||||||
u_page.tasks = { update }
|
u_page.tasks = { update }
|
||||||
|
|
||||||
TextBox{parent=u_div,y=1,text="Reactor Unit #"..i,height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=u_div,y=1,text="Reactor Unit #"..i,alignment=ALIGN.CENTER}
|
||||||
PushButton{parent=u_div,x=1,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()prev(i)end}
|
PushButton{parent=u_div,x=1,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()prev(i)end}
|
||||||
PushButton{parent=u_div,x=21,y=1,text=">",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()next(i)end}
|
PushButton{parent=u_div,x=21,y=1,text=">",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()next(i)end}
|
||||||
|
|
||||||
local type = util.trinary(unit.num_boilers > 0, "Sodium Cooled Reactor", "Boiling Water Reactor")
|
local type = util.trinary(unit.num_boilers > 0, "Sodium Cooled Reactor", "Boiling Water Reactor")
|
||||||
TextBox{parent=u_div,y=3,text=type,height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)}
|
TextBox{parent=u_div,y=3,text=type,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
|
||||||
local lu_col = cpair(colors.lightGray, colors.lightGray)
|
local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Burn",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
local text_fg = cpair(colors.white, colors._INHERIT)
|
local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
|
|
||||||
local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Rate",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
|
||||||
local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit="K",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
|
||||||
|
|
||||||
local ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states}
|
local ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states}
|
||||||
|
|
||||||
rate.register(u_ps, "act_burn_rate", rate.update)
|
rate.register(u_ps, "act_burn_rate", rate.update)
|
||||||
temp.register(u_ps, "temp", temp.update)
|
temp.register(u_ps, "temp", function (t) temp.update(db.temp_convert(t)) end)
|
||||||
ctrl.register(u_ps, "U_ControlStatus", ctrl.update)
|
ctrl.register(u_ps, "U_ControlStatus", ctrl.update)
|
||||||
|
|
||||||
u_div.line_break()
|
u_div.line_break()
|
||||||
@@ -186,6 +185,8 @@ local function new_view(root)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
--#region Alarms Tab
|
--#region Alarms Tab
|
||||||
|
|
||||||
local alm_div = Div{parent=page_div}
|
local alm_div = Div{parent=page_div}
|
||||||
@@ -193,26 +194,52 @@ local function new_view(root)
|
|||||||
|
|
||||||
local alm_page = app.new_page(u_page, #panes)
|
local alm_page = app.new_page(u_page, #panes)
|
||||||
alm_page.tasks = { update }
|
alm_page.tasks = { update }
|
||||||
|
|
||||||
nav_links[i].alarm = alm_page.nav_to
|
nav_links[i].alarm = alm_page.nav_to
|
||||||
|
|
||||||
TextBox{parent=alm_div,y=1,text="Unit Alarms",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=alm_div,y=1,text="Status Info Display",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
TextBox{parent=alm_div,y=3,text="work in progress",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)}
|
local ecam_disp = ListBox{parent=alm_div,x=2,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
ecam_disp.register(u_ps, "U_ECAM", function (data)
|
||||||
|
local ecam = textutils.unserialize(data)
|
||||||
|
|
||||||
|
ecam_disp.remove_all()
|
||||||
|
for _, entry in ipairs(ecam) do
|
||||||
|
local div = Div{parent=ecam_disp,height=1+#entry.items,fg_bg=cpair(entry.color,colors.black)}
|
||||||
|
local text = TextBox{parent=div,text=entry.text}
|
||||||
|
|
||||||
|
if entry.help then
|
||||||
|
PushButton{parent=div,x=21,y=text.get_y(),text="?",callback=function()db.nav.open_help(entry.help)end,fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, item in ipairs(entry.items) do
|
||||||
|
local fg_bg = nil
|
||||||
|
if item.color then fg_bg = cpair(item.color, colors.black) end
|
||||||
|
|
||||||
|
text = TextBox{parent=div,x=3,text=item.text,fg_bg=fg_bg}
|
||||||
|
|
||||||
|
if item.help then
|
||||||
|
PushButton{parent=div,x=21,y=text.get_y(),text="?",callback=function()db.nav.open_help(item.help)end,fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ecam_disp.line_break()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region RPS Tab
|
--#region RPS Tab
|
||||||
|
|
||||||
local rps_div = Div{parent=page_div}
|
local rps_pane = Div{parent=page_div}
|
||||||
|
local rps_div = Div{parent=rps_pane,x=2,width=main.get_width()-2}
|
||||||
table.insert(panes, rps_div)
|
table.insert(panes, rps_div)
|
||||||
|
|
||||||
local rps_page = app.new_page(u_page, #panes)
|
local rps_page = app.new_page(u_page, #panes)
|
||||||
rps_page.tasks = { update }
|
rps_page.tasks = { update }
|
||||||
|
|
||||||
nav_links[i].rps = rps_page.nav_to
|
nav_links[i].rps = rps_page.nav_to
|
||||||
|
|
||||||
TextBox{parent=rps_div,y=1,text="Protection System",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=rps_div,y=1,text="Protection System",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
local r_trip = IconIndicator{parent=rps_div,y=3,label="RPS Trip",states=basic_states}
|
local r_trip = IconIndicator{parent=rps_div,y=3,label="RPS Trip",states=basic_states}
|
||||||
r_trip.register(u_ps, "U_RPS", r_trip.update)
|
r_trip.register(u_ps, "U_RPS", r_trip.update)
|
||||||
@@ -246,17 +273,24 @@ local function new_view(root)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Reactor Tab
|
||||||
|
|
||||||
|
nav_links[i].reactor = reactor(app, u_page, panes, page_div, u_ps, update)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region RCS Tab
|
--#region RCS Tab
|
||||||
|
|
||||||
local rcs_div = Div{parent=page_div}
|
local rcs_pane = Div{parent=page_div}
|
||||||
table.insert(panes, rcs_div)
|
local rcs_div = Div{parent=rcs_pane,x=2,width=main.get_width()-2}
|
||||||
|
table.insert(panes, rcs_pane)
|
||||||
|
|
||||||
local rcs_page = app.new_page(u_page, #panes)
|
local rcs_page = app.new_page(u_page, #panes)
|
||||||
rcs_page.tasks = { update }
|
rcs_page.tasks = { update }
|
||||||
|
|
||||||
nav_links[i].rcs = rcs_page.nav_to
|
nav_links[i].rcs = rcs_page.nav_to
|
||||||
|
|
||||||
TextBox{parent=rcs_div,y=1,text="Coolant System",height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=rcs_div,y=1,text="Coolant System",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
local r_rtrip = IconIndicator{parent=rcs_div,y=3,label="RCP Trip",states=red_ind_s}
|
local r_rtrip = IconIndicator{parent=rcs_div,y=3,label="RCP Trip",states=red_ind_s}
|
||||||
local r_cflow = IconIndicator{parent=rcs_div,label="RCS Flow Lo",states=yel_ind_s}
|
local r_cflow = IconIndicator{parent=rcs_div,label="RCS Flow Lo",states=yel_ind_s}
|
||||||
@@ -275,7 +309,7 @@ local function new_view(root)
|
|||||||
c_mwrf.register(u_ps, "MaxWaterReturnFeed", c_mwrf.update)
|
c_mwrf.register(u_ps, "MaxWaterReturnFeed", c_mwrf.update)
|
||||||
|
|
||||||
-- rcs_div.line_break()
|
-- rcs_div.line_break()
|
||||||
-- TextBox{parent=rcs_div,text="Mismatches",height=1,alignment=ALIGN.CENTER,fg_bg=label}
|
-- TextBox{parent=rcs_div,text="Mismatches",alignment=ALIGN.CENTER,fg_bg=label}
|
||||||
local c_cfm = IconIndicator{parent=rcs_div,label="Coolant Feed",states=yel_ind_s}
|
local c_cfm = IconIndicator{parent=rcs_div,label="Coolant Feed",states=yel_ind_s}
|
||||||
local c_brm = IconIndicator{parent=rcs_div,label="Boil Rate",states=yel_ind_s}
|
local c_brm = IconIndicator{parent=rcs_div,label="Boil Rate",states=yel_ind_s}
|
||||||
local c_sfm = IconIndicator{parent=rcs_div,label="Steam Feed",states=yel_ind_s}
|
local c_sfm = IconIndicator{parent=rcs_div,label="Steam Feed",states=yel_ind_s}
|
||||||
@@ -285,7 +319,7 @@ local function new_view(root)
|
|||||||
c_sfm.register(u_ps, "SteamFeedMismatch", c_sfm.update)
|
c_sfm.register(u_ps, "SteamFeedMismatch", c_sfm.update)
|
||||||
|
|
||||||
rcs_div.line_break()
|
rcs_div.line_break()
|
||||||
-- TextBox{parent=rcs_div,text="Aggregate Checks",height=1,alignment=ALIGN.CENTER,fg_bg=label}
|
-- TextBox{parent=rcs_div,text="Aggregate Checks",alignment=ALIGN.CENTER,fg_bg=label}
|
||||||
|
|
||||||
if unit.num_boilers > 0 then
|
if unit.num_boilers > 0 then
|
||||||
local wll = IconIndicator{parent=rcs_div,label="Boiler Water Lo",states=red_ind_s}
|
local wll = IconIndicator{parent=rcs_div,label="Boiler Water Lo",states=red_ind_s}
|
||||||
@@ -304,6 +338,32 @@ local function new_view(root)
|
|||||||
ttrip.register(u_ps, "U_TurbineTrip", ttrip.update)
|
ttrip.register(u_ps, "U_TurbineTrip", ttrip.update)
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Boiler Tabs
|
||||||
|
|
||||||
|
local blr_pane = Div{parent=page_div}
|
||||||
|
nav_links[i].boiler = {}
|
||||||
|
|
||||||
|
for b_id = 1, unit.num_boilers do
|
||||||
|
local ps = unit.boiler_ps_tbl[b_id]
|
||||||
|
nav_links[i].boiler[b_id] = boiler(app, u_page, panes, blr_pane, b_id, ps, update)
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Turbine Tabs
|
||||||
|
|
||||||
|
local tbn_pane = Div{parent=page_div}
|
||||||
|
nav_links[i].turbine = {}
|
||||||
|
|
||||||
|
for t_id = 1, unit.num_turbines do
|
||||||
|
local ps = unit.turbine_ps_tbl[t_id]
|
||||||
|
nav_links[i].turbine[t_id] = turbine(app, u_page, panes, tbn_pane, i, t_id, ps, update)
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- setup multipane
|
-- setup multipane
|
||||||
@@ -311,9 +371,27 @@ local function new_view(root)
|
|||||||
app.set_root_pane(u_pane)
|
app.set_root_pane(u_pane)
|
||||||
|
|
||||||
set_sidebar(active_unit)
|
set_sidebar(active_unit)
|
||||||
|
|
||||||
|
-- done, show the app
|
||||||
|
load_pane.set_value(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
app.set_on_load(load)
|
-- delete the elements and switch back to the loading screen
|
||||||
|
local function unload()
|
||||||
|
if page_div then
|
||||||
|
page_div.delete()
|
||||||
|
page_div = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
app.set_sidebar({ { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end } })
|
||||||
|
app.delete_pages()
|
||||||
|
|
||||||
|
-- show loading screen
|
||||||
|
load_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
app.set_load(load)
|
||||||
|
app.set_unload(unload)
|
||||||
|
|
||||||
return main
|
return main
|
||||||
end
|
end
|
||||||
@@ -23,16 +23,16 @@ local function init(parent, y, is_api)
|
|||||||
local root = Div{parent=parent,x=1,y=1}
|
local root = Div{parent=parent,x=1,y=1}
|
||||||
|
|
||||||
-- bounding box div
|
-- bounding box div
|
||||||
local box = Div{parent=root,x=1,y=y,height=5}
|
local box = Div{parent=root,x=1,y=y,height=6}
|
||||||
|
|
||||||
local waiting_x = math.floor(parent.get_width() / 2) - 1
|
local waiting_x = math.floor(parent.get_width() / 2) - 1
|
||||||
|
|
||||||
if is_api then
|
if is_api then
|
||||||
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.blue,style.root.bkg)}
|
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.blue,style.root.bkg)}
|
||||||
TextBox{parent=box,text="Connecting to API",alignment=ALIGN.CENTER,y=5,height=1,fg_bg=cpair(colors.white,style.root.bkg)}
|
TextBox{parent=box,text="Connecting to API",alignment=ALIGN.CENTER,y=5,fg_bg=cpair(colors.white,style.root.bkg)}
|
||||||
else
|
else
|
||||||
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.green,style.root.bkg)}
|
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.green,style.root.bkg)}
|
||||||
TextBox{parent=box,text="Connecting to Supervisor",alignment=ALIGN.CENTER,y=5,height=1,fg_bg=cpair(colors.white,style.root.bkg)}
|
TextBox{parent=box,text="Connecting to Supervisor",alignment=ALIGN.CENTER,y=5,fg_bg=cpair(colors.white,style.root.bkg)}
|
||||||
end
|
end
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|||||||
136
pocket/ui/docs.lua
Normal file
136
pocket/ui/docs.lua
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
local docs = {}
|
||||||
|
|
||||||
|
local target
|
||||||
|
|
||||||
|
local function doc(key, name, desc)
|
||||||
|
---@class pocket_doc_item
|
||||||
|
local item = { key = key, name = name, desc = desc }
|
||||||
|
table.insert(target, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- important to note in the future: The PLC should always be in a chunk with the reactor to ensure it can protect it on chunk load if you do not keep it all chunk loaded
|
||||||
|
|
||||||
|
docs.alarms = {}
|
||||||
|
|
||||||
|
target = docs.alarms
|
||||||
|
doc("ContainmentBreach", "Containment Breach", "Reactor disconnected or indicated unformed while being at or above 100% damage; explosion assumed.")
|
||||||
|
doc("ContainmentRadiation", "Containment Radiation", "Environment detector(s) assigned to the unit have observed high levels of radiation.")
|
||||||
|
doc("ReactorLost", "Reactor Lost", "Reactor PLC has stopped communicating with the supervisor.")
|
||||||
|
doc("CriticalDamage", "Damage Critical", "Reactor damage has reached or exceeded 100%, so it will explode at any moment.")
|
||||||
|
doc("ReactorDamage", "Reactor Damage", "Reactor temperature causing increasing damage to the reactor casing.")
|
||||||
|
doc("ReactorOverTemp", "Reactor Over Temp", "Reactor temperature is at or above maximum safe temperature, so it is now taking damage.")
|
||||||
|
doc("ReactorHighTemp", "Reactor High Temp", "Reactor temperature is above expected operating levels and may exceed maximum safe temperature soon.")
|
||||||
|
doc("ReactorWasteLeak", "Reactor Waste Leak", "The reactor is full of spent waste so it will now emit radiation if additional waste is generated.")
|
||||||
|
doc("ReactorHighWaste", "Reactor High Waste", "Reactor waste levels are high and may leak soon.")
|
||||||
|
doc("RPSTransient", "RPS Transient", "Reactor protection system was activated.")
|
||||||
|
doc("RCSTransient", "RCS Transient", "Something is wrong with the reactor coolant system, check RCS indicators for details.")
|
||||||
|
doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due to having full energy storage. This will prevent cooling, so it needs to be resolved before using that unit.")
|
||||||
|
|
||||||
|
docs.annunc = {
|
||||||
|
unit = {
|
||||||
|
main_section = {}, rps_section = {}, rcs_section = {}, fac_section = {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target = docs.annunc.unit.main_section
|
||||||
|
doc("PLCOnline", "PLC Online", "Indicates if the fission reactor PLC is connected. If it isn't, check that your PLC is on and configured properly.")
|
||||||
|
doc("PLCHeartbeat", "PLC Heartbeat", "An indicator of status data being live. As status messages are received from the PLC, this light will turn on and off. If it gets stuck, the supervisor has stopped receiving data or a screen has frozen.")
|
||||||
|
doc("RadiationMonitor", "Radiation Monitor", "On if at least one environment detector is connected and assigned to this unit.")
|
||||||
|
doc("AutoControl", "Automatic Control", "On if the reactor is under the control of one of the automatic control modes.")
|
||||||
|
doc("ReactorSCRAM", "Reactor SCRAM", "On if the reactor protection system is holding the reactor SCRAM'd.")
|
||||||
|
doc("ManualReactorSCRAM", "Manual Reactor SCRAM", "On if the operator (you) initiated a SCRAM.")
|
||||||
|
doc("AutoReactorSCRAM", "Auto Reactor SCRAM", "On if the automatic control system initiated a SCRAM. The main view screen annunciator will have an indication as to why.")
|
||||||
|
doc("RadiationWarning", "Radiation Warning", "On if radiation levels are above normal. There is likely a leak somewhere, so that should be identified and fixed. Hazmat suit recommended.")
|
||||||
|
doc("RCPTrip", "RCP Trip", "Reactor coolant pump tripped. This is a technical concept not directly mapping to Mekansim. Here, it indicates if there is either high heated coolant or low cooled coolant that caused an RPS trip. Check the coolant system if this occurs.")
|
||||||
|
doc("RCSFlowLow", "RCS Flow Low", "Indicates if the reactor coolant system flow is low. This is observed when the cooled coolant level in the reactor is dropping. This can occur while a turbine spins up, but if it persists, check that the cooling system is operating properly. This can occur with smaller boilers or when using pipes and not having enough.")
|
||||||
|
doc("CoolantLevelLow", "Coolant Level Low", "On if the reactor coolant level is lower than it should be. Check the coolant system.")
|
||||||
|
doc("ReactorTempHigh", "Reactor Temp. High", "On if the reactor temperature is above expected maximum operating temperature. This is not yet damaging, but should be attended to. Check coolant system.")
|
||||||
|
doc("ReactorHighDeltaT", "Reactor High Delta T", "On if the reactor temperature is climbing rapidly. This can occur when a reactor is starting up, but it is a concern if it happens while the burn rate is not increasing.")
|
||||||
|
doc("FuelInputRateLow", "Fuel Input Rate Low", "On if the fissile fuel levels in the reactor are dropping or very low. Ensure a steady supply of fuel is entering the reactor.")
|
||||||
|
doc("WasteLineOcclusion", "Waste Line Occlusion", "Waste levels in the reactor are increasing. Ensure your waste processing system is operating at a sufficient rate for your burn rate.")
|
||||||
|
doc("HighStartupRate", "Startup Rate High", "This is a rough calculation of if your burn rate is high enough to cause a loss of coolant on startup. A burn rate above this is likely to cause that, but it could occur at even higher or even lower rates depending on your setup (such as pipes, water supplies, and boiler tanks).")
|
||||||
|
|
||||||
|
target = docs.annunc.unit.rps_section
|
||||||
|
doc("rps_tripped", "RPS Trip", "Indicates if the reactor protection system has caused a SCRAM.")
|
||||||
|
doc("manual", "Manual Reactor SCRAM", "Indicates if the operator (you) tripped the RPS by pressing SCRAM.")
|
||||||
|
doc("automatic", "Auto Reactor SCRAM", "Indicates if the automatic control system tripped the RPS.")
|
||||||
|
doc("high_dmg", "Damage Level High", "Indicates if the RPS tripped due to significant reactor damage. Await damage levels to lower.")
|
||||||
|
doc("ex_waste", "Excess Waste", "Indicates if the RPS tripped due to very high waste levels. Ensure waste processing system is keeping up.")
|
||||||
|
doc("ex_hcool", "Excess Heated Coolant", "Indicates if the RPS tripped due to very high heated coolant levels. Check that the cooling system is able to keep up with heated coolant flow.")
|
||||||
|
doc("high_temp", "Temperature High", "Indicates if the RPS tripped due to reaching damaging temperatures. Await damage levels to lower.")
|
||||||
|
doc("low_cool", "Coolant Level Low Low", "Indicates if the RPS tripped due to very low coolant levels that result in the temperature uncontrollably rising. Ensure that the cooling system can provide sufficient cooled coolant flow.")
|
||||||
|
doc("no_fuel", "No Fuel", "Indicates if the RPS tripped due to no fuel being available. Check fuel input.")
|
||||||
|
doc("fault", "PPM Fault", "Indicates if the RPS tripped due to a peripheral access fault. Something went wrong interfacing with the reactor, try restarting the PLC.")
|
||||||
|
doc("timeout", "Connection Timeout", "Indicates if the RPS tripped due to losing connection with the supervisory computer. Check that your PLC and supervisor remain chunk loaded.")
|
||||||
|
doc("sys_fail", "System Failure", "Indicates if the RPS tripped due to the reactor not being formed. Ensure that the multi-block is formed.")
|
||||||
|
|
||||||
|
target = docs.annunc.unit.rcs_section
|
||||||
|
doc("RCSFault", "RCS Hardware Fault", "Indicates if one or more of the RCS devices have a peripheral fault. Check that your machines are formed. If this persists, try rebooting affected RTUs.")
|
||||||
|
doc("EmergencyCoolant", "Emergency Coolant", "Off if no emergency coolant redstone is configured, white when it is configured but not in use, and green/blue when it is activated. This is based on an RTU having a redstone emergency coolant output configured for this unit.")
|
||||||
|
doc("CoolantFeedMismatch", "Coolant Feed Mismatch", "The coolant system is accumulating heated coolant or losing cooled coolant, likely due to one of the machines not keeping up with the needs of the reactor. The flow monitor can help figure out where the problem is.")
|
||||||
|
doc("BoilRateMismatch", "Boil Rate Mismatch", "The total heating rate of the reactor exceed the tolerance from the steam input rate of the turbines OR for sodium setups, the boiler boil rates exceed the tolerance from the steam input rate of the turbines. The flow monitor can help figure out where the problem is.")
|
||||||
|
doc("SteamFeedMismatch", "Steam Feed Mismatch", "There is an above tolerance difference between turbine flow and steam input rates or the reactor/boilers are gaining steam or losing water. The flow monitor can help figure out where the problem is.")
|
||||||
|
doc("MaxWaterReturnFeed", "Max Water Return Feed", "The turbines are condensing the max rate of water that they can per the structure build. If water return is insufficient, add more saturating condensers to your turbine(s).")
|
||||||
|
doc("WaterLevelLow", "Water Level Low", "The water level in the boiler is low. A larger boiler water tank may help, or you can feed additional water into the boiler from elsewhere.")
|
||||||
|
doc("HeatingRateLow", "Heating Rate Low", "The boiler is not hot enough to boil water, but it is receiving heated coolant. This is almost never a safety concern.")
|
||||||
|
doc("SteamDumpOpen", "Steam Relief Valve Open", "This turns yellow if the turbine is set to dumping excess and red if it is set to dumping [all]. 'Relief Valve' in this case is that setting allowing the venting of steam. You should never have this set to dumping [all]. Emergency coolant activation from the supervisor will automatically set it to dumping excess to ensure there is no backup of steam as water is added.")
|
||||||
|
doc("TurbineOverSpeed", "Turbine Over Speed", "The turbine is at steam capacity, but not tripped. You may need more turbines if they can't keep up.")
|
||||||
|
doc("GeneratorTrip", "Generator Trip", "The turbine is no longer outputting power due to it having nowhere to go. Likely due to full power storage. This will lead to a Turbine Trip if not addressed.")
|
||||||
|
doc("TurbineTrip", "Turbine Trip", "The turbine has reached its maximum power charge and has stopped rotating, and as a result stopped cooling steam to water. Ensure the turbine has somewhere to output power, as this is the most common cause of reactor meltdowns. However, the likelihood of a meltdown with this system in place is much lower, especially with emergency coolant helping during turbine trips.")
|
||||||
|
|
||||||
|
target = docs.annunc.unit.fac_section
|
||||||
|
doc("?", "Unit Systems Online", "All unit systems (reactors, boilers, and turbines) are connected.")
|
||||||
|
doc("?", "Radiation Monitor", "At least one facility radiation monitor is connected")
|
||||||
|
doc("?", "Induction Matrix", "The induction matrix is connected.")
|
||||||
|
doc("?", "SPS Connected", "Indicates if the super-critical phase shifter is connected.")
|
||||||
|
doc("?", "Configured Units Ready", "All units assigned to automatic control are ready to run automatic control.")
|
||||||
|
doc("?", "Process Active", "Automatic process control is active.")
|
||||||
|
doc("?", "Process Ramping", "Automatic process control is performing an initial ramp-up of the reactors for later PID control (generation and charge mode).")
|
||||||
|
doc("?", "Min/Max Burn Rate", "Auto control has either commanded 0 mB/t or the maximum total burn rate available (from assigned units).")
|
||||||
|
doc("?", "Automatic SCRAM", "Automatic control system SCRAM'ed the assigned reactors due to a safety hazard, shown by the below indicators.")
|
||||||
|
doc("?", "Matrix Disconnected", "Automatic SCRAM occurred due to loss of induction matrix connection.")
|
||||||
|
doc("?", "Matrix Charge High", "Automatic SCRAM occurred due to induction matrix charge exceeding acceptable limit.")
|
||||||
|
doc("?", "Unit Critical Alarm", "Automatic SCRAM occurred due to critical level unit alarm(s).")
|
||||||
|
doc("?", "Facility Radiation High", "Automatic SCRAM occurred due to high facility radiation levels.")
|
||||||
|
doc("?", "Gen. Control Fault", "Automatic SCRAM occurred due to assigned units being degraded/no longer ready during generation mode. The system will automatically resume (starting with initial ramp) once the problem is resolved.")
|
||||||
|
|
||||||
|
docs.glossary = {
|
||||||
|
abbvs = {}, terms = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
target = docs.glossary.abbvs
|
||||||
|
doc("G_ACK", "ACK", "Alarm ACKnowledge. Pressing this acknowledges that you understand an alarm occurred and would like to stop the audio tone(s).")
|
||||||
|
doc("G_Auto", "Auto", "Automatic.")
|
||||||
|
doc("G_CRD", "CRD", "Coordinator. Abbreviation for the coordinator computer.")
|
||||||
|
doc("G_DBG", "DBG", "Debug. Abbreviation for the debugging sessions from pocket computers found on the supervisor's front panel.")
|
||||||
|
doc("G_FP", "FP", "Front Panel. See Terminology.")
|
||||||
|
doc("G_Hi", "Hi", "High.")
|
||||||
|
doc("G_Lo", "Lo", "Low.")
|
||||||
|
doc("G_PID", "PID", "A Proportional Integral Derivitave closed-loop controller.")
|
||||||
|
doc("G_PKT", "PKT", "Pocket. Abbreviation for the pocket computer.")
|
||||||
|
doc("G_PLC", "PLC", "Programmable Logic Controller. A device that not only reports data and controls outputs, but can also make decisions on its own.")
|
||||||
|
doc("G_PPM", "PPM", "Protected Peripheral Manager. This is an abstraction layer created for this project that prevents peripheral calls from crashing applications.")
|
||||||
|
doc("G_RCP", "RCP", "Reactor Coolant Pump. This is from real-world terminology with water-cooled (boiling water and pressurized water) reactors, but in this system it just reflects to the functioning of reactor coolant flow. See the annunciator page on it for more information.")
|
||||||
|
doc("G_RCS", "RCS", "Reactor Cooling System. The combination of all machines used to cool the reactor (turbines, boilers, dynamic tanks).")
|
||||||
|
doc("G_RPS", "RPS", "Reactor Protection System. A component of the reactor PLC responsible for keeping the reactor safe.")
|
||||||
|
doc("G_RTU", "RTU", "Remote Terminal Unit. Provides monitoring to and basic output from a SCADA system, interfacing with various types of devices/interfaces.")
|
||||||
|
doc("G_SCADA", "SCADA", "Supervisory Control and Data Acquisition. A control systems architecture used in a wide variety process control applications.")
|
||||||
|
doc("G_SVR", "SVR", "Supervisor. Abbreviation for the supervisory computer.")
|
||||||
|
doc("G_UI", "UI", "User Interface.")
|
||||||
|
|
||||||
|
target = docs.glossary.terms
|
||||||
|
doc("G_AssignedUnit", "Assigned Unit", "A unit that is assigned to an automatic control group (not assigned to Manual).")
|
||||||
|
doc("G_Fault", "Fault", "Something has gone wrong and/or failed to function.")
|
||||||
|
doc("G_FrontPanel", "Front Panel", "A basic interface on the front of a device for viewing and sometimes modifying its state. This is what you see when looking at a computer running one of the SCADA applications.")
|
||||||
|
doc("G_HighHigh", "High High", "Very High.")
|
||||||
|
doc("G_LowLow", "Low Low", "Very Low.")
|
||||||
|
doc("G_Nominal", "Nominal", "Normal operation. Everything operating as intended.")
|
||||||
|
doc("G_Ringback", "Ringback", "An indication that an alarm had gone off but is no longer having its trip condition(s) met. This is to make you are aware that it occurred.")
|
||||||
|
doc("G_SCRAM", "SCRAM", "[Emergency] shut-down of a reactor by stopping the fission. In Mekanism and here, it isn't always for an emergency.")
|
||||||
|
doc("G_Transient", "Transient", "A temporary change in state from normal operation. Coolant levels dropping or core temperature rising above nominal values are examples of transients.")
|
||||||
|
doc("G_Trip", "Trip", "A checked condition had occurred, see 'Tripped'.")
|
||||||
|
doc("G_Tripped", "Tripped", "An alarm condition has been met, and is still met.")
|
||||||
|
doc("G_Tripping", "Tripping", "Alarm condition(s) is/are met, but has/have not reached the minimum time before the condition(s) is/are deemed a problem.")
|
||||||
|
doc("G_TurbineTrip", "Turbine Trip", "The turbine stopped, which prevents heated coolant from being cooled. In Mekanism, this would occur when a turbine cannot generate any more energy due to filling its buffer and having no output with any remaining energy capacity.")
|
||||||
|
|
||||||
|
return docs
|
||||||
@@ -2,92 +2,84 @@
|
|||||||
-- Pocket GUI Root
|
-- Pocket GUI Root
|
||||||
--
|
--
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local diag_apps = require("pocket.ui.apps.diag_apps")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
local dummy_app = require("pocket.ui.apps.dummy_app")
|
local pocket = require("pocket.pocket")
|
||||||
local sys_apps = require("pocket.ui.apps.sys_apps")
|
|
||||||
|
|
||||||
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
local diag_apps = require("pocket.ui.apps.diag_apps")
|
||||||
|
local dummy_app = require("pocket.ui.apps.dummy_app")
|
||||||
|
local guide_app = require("pocket.ui.apps.guide")
|
||||||
|
local loader_app = require("pocket.ui.apps.loader")
|
||||||
|
local sys_apps = require("pocket.ui.apps.sys_apps")
|
||||||
|
local unit_app = require("pocket.ui.apps.unit")
|
||||||
|
|
||||||
local home_page = require("pocket.ui.pages.home_page")
|
local home_page = require("pocket.ui.pages.home_page")
|
||||||
local unit_page = require("pocket.ui.pages.unit_page")
|
|
||||||
|
|
||||||
local style = require("pocket.ui.style")
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
local MultiPane = require("graphics.elements.multipane")
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local WaitingAnim = require("graphics.elements.animations.waiting")
|
||||||
local Sidebar = require("graphics.elements.controls.sidebar")
|
|
||||||
|
|
||||||
local SignalBar = require("graphics.elements.indicators.signal")
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
local Sidebar = require("graphics.elements.controls.sidebar")
|
||||||
|
|
||||||
local LINK_STATE = iocontrol.LINK_STATE
|
local SignalBar = require("graphics.elements.indicators.signal")
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
-- create new main view
|
-- create new main view
|
||||||
---@param main graphics_element main displaybox
|
---@param main graphics_element main displaybox
|
||||||
local function init(main)
|
local function init(main)
|
||||||
local db = iocontrol.get_db()
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
-- window header message
|
-- window header message and connection status
|
||||||
TextBox{parent=main,y=1,text="WIP ALPHA APP S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
TextBox{parent=main,y=1,text="EARLY ACCESS ALPHA S C ",alignment=ALIGN.LEFT,fg_bg=style.header}
|
||||||
local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
|
|
||||||
db.ps.subscribe("svr_conn_quality", svr_conn.set_value)
|
db.ps.subscribe("svr_conn_quality", svr_conn.set_value)
|
||||||
db.ps.subscribe("crd_conn_quality", crd_conn.set_value)
|
db.ps.subscribe("crd_conn_quality", crd_conn.set_value)
|
||||||
|
|
||||||
--#region root panel panes (connection screens + main screen)
|
local start_pane = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=main,x=1,y=2}
|
|
||||||
|
|
||||||
local conn_sv_wait = conn_waiting(root_pane_div, 6, false)
|
|
||||||
local conn_api_wait = conn_waiting(root_pane_div, 6, true)
|
|
||||||
local main_pane = Div{parent=main,x=1,y=2}
|
local main_pane = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
local root_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={conn_sv_wait,conn_api_wait,main_pane}}
|
WaitingAnim{parent=start_pane,x=12,y=7,fg_bg=cpair(colors.lightBlue,style.root.bkg)}
|
||||||
|
TextBox{parent=start_pane,y=11,text="starting up...",alignment=ALIGN.CENTER,fg_bg=cpair(colors.lightGray,style.root.bkg)}
|
||||||
|
|
||||||
root_pane.register(db.ps, "link_state", function (state)
|
local root_pane = MultiPane{parent=main,x=1,y=2,panes={start_pane,main_pane}}
|
||||||
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
|
||||||
root_pane.set_value(1)
|
|
||||||
elseif state == LINK_STATE.SV_LINK_ONLY then
|
|
||||||
root_pane.set_value(2)
|
|
||||||
else
|
|
||||||
root_pane.set_value(3)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region main page panel panes & sidebar
|
|
||||||
|
|
||||||
local page_div = Div{parent=main_pane,x=4,y=1}
|
local page_div = Div{parent=main_pane,x=4,y=1}
|
||||||
|
|
||||||
|
-- create all the apps & pages
|
||||||
home_page(page_div)
|
home_page(page_div)
|
||||||
unit_page(page_div)
|
unit_app(page_div)
|
||||||
|
guide_app(page_div)
|
||||||
diag_apps(page_div)
|
loader_app(page_div)
|
||||||
sys_apps(page_div)
|
sys_apps(page_div)
|
||||||
|
diag_apps(page_div)
|
||||||
dummy_app(page_div)
|
dummy_app(page_div)
|
||||||
|
|
||||||
assert(#db.nav.get_containers() == iocontrol.APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered")
|
-- verify all apps were created
|
||||||
|
assert(util.table_len(db.nav.get_containers()) == APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered")
|
||||||
|
|
||||||
db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()})
|
db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()})
|
||||||
db.nav.set_sidebar(Sidebar{parent=main_pane,x=1,y=1,height=18,fg_bg=cpair(colors.white,colors.gray)})
|
db.nav.set_sidebar(Sidebar{parent=main_pane,x=1,y=1,height=18,fg_bg=cpair(colors.white,colors.gray)})
|
||||||
|
|
||||||
PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up}
|
PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up}
|
||||||
|
|
||||||
db.nav.open_app(iocontrol.APP_ID.ROOT)
|
db.nav.open_app(APP_ID.ROOT)
|
||||||
|
|
||||||
--#endregion
|
-- done with initial render, lets go!
|
||||||
|
root_pane.set_value(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
return init
|
return init
|
||||||
|
|||||||
68
pocket/ui/pages/guide_section.lua
Normal file
68
pocket/ui/pages/guide_section.lua
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
local log = require("scada-common.log")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
-- new guide documentation section
|
||||||
|
---@param data _guide_section_constructor_data
|
||||||
|
---@param base_page nav_tree_page
|
||||||
|
---@param title string
|
||||||
|
---@param items table
|
||||||
|
---@param scroll_height integer
|
||||||
|
---@return nav_tree_page
|
||||||
|
return function (data, base_page, title, items, scroll_height)
|
||||||
|
local app, page_div, panes, doc_map, search_db, btn_fg_bg, btn_active = table.unpack(data)
|
||||||
|
|
||||||
|
local section_page = app.new_page(base_page, #panes + 1)
|
||||||
|
local section_div = Div{parent=page_div,x=2}
|
||||||
|
table.insert(panes, section_div)
|
||||||
|
TextBox{parent=section_div,y=1,text=title,alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=section_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=base_page.nav_to}
|
||||||
|
|
||||||
|
local view_page = app.new_page(section_page, #panes + 1)
|
||||||
|
local section_view_div = Div{parent=page_div,x=2}
|
||||||
|
table.insert(panes, section_view_div)
|
||||||
|
TextBox{parent=section_view_div,y=1,text=title,alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=section_view_div,x=3,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=section_page.nav_to}
|
||||||
|
|
||||||
|
local name_list = ListBox{parent=section_div,x=1,y=3,scroll_height=30,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)}
|
||||||
|
local def_list = ListBox{parent=section_view_div,x=1,y=3,scroll_height=scroll_height,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
local _end
|
||||||
|
|
||||||
|
for i = 1, #items do
|
||||||
|
local item = items[i] ---@type pocket_doc_item
|
||||||
|
|
||||||
|
local anchor = TextBox{parent=def_list,text=item.name,anchor=true,fg_bg=cpair(colors.blue,colors.black)}
|
||||||
|
TextBox{parent=def_list,text=item.desc}
|
||||||
|
_end = Div{parent=def_list,height=1,can_focus=true}
|
||||||
|
|
||||||
|
local function view()
|
||||||
|
_end.focus()
|
||||||
|
view_page.nav_to()
|
||||||
|
anchor.focus()
|
||||||
|
end
|
||||||
|
|
||||||
|
doc_map[item.key] = view
|
||||||
|
table.insert(search_db, { string.lower(item.name), item.name, title, view })
|
||||||
|
|
||||||
|
PushButton{parent=name_list,text=item.name,alignment=ALIGN.LEFT,fg_bg=cpair(colors.blue,colors.black),active_fg_bg=btn_active,callback=view}
|
||||||
|
|
||||||
|
if i % 12 == 0 then util.nop() end
|
||||||
|
end
|
||||||
|
|
||||||
|
log.debug("guide section " .. title .. " generated with final height ".. _end.get_y())
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
|
return section_page
|
||||||
|
end
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
@@ -12,11 +13,10 @@ local TextBox = require("graphics.elements.textbox")
|
|||||||
|
|
||||||
local App = require("graphics.elements.controls.app")
|
local App = require("graphics.elements.controls.app")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local APP_ID = iocontrol.APP_ID
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
|
||||||
|
|
||||||
-- new home page view
|
-- new home page view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
@@ -25,7 +25,7 @@ local function new_view(root)
|
|||||||
|
|
||||||
local main = Div{parent=root,x=1,y=1,height=19}
|
local main = Div{parent=root,x=1,y=1,height=19}
|
||||||
|
|
||||||
local app = db.nav.register_app(iocontrol.APP_ID.ROOT, main)
|
local app = db.nav.register_app(APP_ID.ROOT, main)
|
||||||
|
|
||||||
local apps_1 = Div{parent=main,x=1,y=1,height=15}
|
local apps_1 = Div{parent=main,x=1,y=1,height=15}
|
||||||
local apps_2 = Div{parent=main,x=1,y=1,height=15}
|
local apps_2 = Div{parent=main,x=1,y=1,height=15}
|
||||||
@@ -51,10 +51,10 @@ local function new_view(root)
|
|||||||
App{parent=apps_1,x=2,y=7,text="\x17",title="Process",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.purple),active_fg_bg=active_fg_bg}
|
App{parent=apps_1,x=2,y=7,text="\x17",title="Process",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.purple),active_fg_bg=active_fg_bg}
|
||||||
App{parent=apps_1,x=9,y=7,text="\x7f",title="Waste",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.brown),active_fg_bg=active_fg_bg}
|
App{parent=apps_1,x=9,y=7,text="\x7f",title="Waste",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.brown),active_fg_bg=active_fg_bg}
|
||||||
App{parent=apps_1,x=16,y=7,text="\x08",title="Devices",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.lightGray),active_fg_bg=active_fg_bg}
|
App{parent=apps_1,x=16,y=7,text="\x08",title="Devices",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.lightGray),active_fg_bg=active_fg_bg}
|
||||||
App{parent=apps_1,x=2,y=12,text="\xb6",title="Guide",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
App{parent=apps_1,x=2,y=12,text="\xb6",title="Guide",callback=function()open(APP_ID.GUIDE)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
||||||
App{parent=apps_1,x=9,y=12,text="?",title="About",callback=function()open(APP_ID.ABOUT)end,app_fg_bg=cpair(colors.black,colors.white),active_fg_bg=active_fg_bg}
|
App{parent=apps_1,x=9,y=12,text="?",title="About",callback=function()open(APP_ID.ABOUT)end,app_fg_bg=cpair(colors.black,colors.white),active_fg_bg=active_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=apps_2,text="Diagnostic Apps",x=1,y=2,height=1,alignment=ALIGN.CENTER}
|
TextBox{parent=apps_2,text="Diagnostic Apps",x=1,y=2,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
App{parent=apps_2,x=2,y=4,text="\x0f",title="Alarm",callback=function()open(APP_ID.ALARMS)end,app_fg_bg=cpair(colors.black,colors.red),active_fg_bg=active_fg_bg}
|
App{parent=apps_2,x=2,y=4,text="\x0f",title="Alarm",callback=function()open(APP_ID.ALARMS)end,app_fg_bg=cpair(colors.black,colors.red),active_fg_bg=active_fg_bg}
|
||||||
App{parent=apps_2,x=9,y=4,text="\x1e",title="LoopT",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
App{parent=apps_2,x=9,y=4,text="\x1e",title="LoopT",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
||||||
|
|||||||
131
pocket/ui/pages/unit_boiler.lua
Normal file
131
pocket/ui/pages/unit_boiler.lua
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
local types = require("scada-common.types")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local StateIndicator = require("graphics.elements.indicators.state")
|
||||||
|
local IconIndicator = require("graphics.elements.indicators.icon")
|
||||||
|
local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local label = style.label
|
||||||
|
local lu_col = style.label_unit_pair
|
||||||
|
local text_fg = style.text_fg
|
||||||
|
local red_ind_s = style.icon_states.red_ind_s
|
||||||
|
local yel_ind_s = style.icon_states.yel_ind_s
|
||||||
|
|
||||||
|
-- create a boiler view in the unit app
|
||||||
|
---@param app pocket_app
|
||||||
|
---@param u_page nav_tree_page
|
||||||
|
---@param panes table
|
||||||
|
---@param blr_pane graphics_element
|
||||||
|
---@param b_id integer boiler ID
|
||||||
|
---@param ps psil
|
||||||
|
---@param update function
|
||||||
|
return function (app, u_page, panes, blr_pane, b_id, ps, update)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local blr_div = Div{parent=blr_pane,x=2,width=blr_pane.get_width()-2}
|
||||||
|
table.insert(panes, blr_div)
|
||||||
|
|
||||||
|
local blr_page = app.new_page(u_page, #panes)
|
||||||
|
blr_page.tasks = { update }
|
||||||
|
|
||||||
|
TextBox{parent=blr_div,y=1,text="BLR #"..b_id,width=8}
|
||||||
|
local status = StateIndicator{parent=blr_div,x=10,y=1,states=style.boiler.states,value=1,min_width=12}
|
||||||
|
status.register(ps, "BoilerStateStatus", status.update)
|
||||||
|
|
||||||
|
local hcool = VerticalBar{parent=blr_div,x=1,y=4,fg_bg=cpair(colors.orange,colors.gray),height=5,width=1}
|
||||||
|
local water = VerticalBar{parent=blr_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1}
|
||||||
|
local steam = VerticalBar{parent=blr_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1}
|
||||||
|
local ccool = VerticalBar{parent=blr_div,x=21,y=4,fg_bg=cpair(colors.lightBlue,colors.gray),height=5,width=1}
|
||||||
|
|
||||||
|
TextBox{parent=blr_div,text="H",x=1,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=blr_div,text="W",x=3,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=blr_div,text="S",x=19,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=blr_div,text="C",x=21,y=3,width=1,fg_bg=label}
|
||||||
|
|
||||||
|
hcool.register(ps, "hcool_fill", hcool.update)
|
||||||
|
water.register(ps, "water_fill", water.update)
|
||||||
|
steam.register(ps, "steam_fill", steam.update)
|
||||||
|
ccool.register(ps, "ccool_fill", ccool.update)
|
||||||
|
|
||||||
|
TextBox{parent=blr_div,text="Temperature",x=5,y=5,width=13,fg_bg=label}
|
||||||
|
local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10)
|
||||||
|
local temp = DataIndicator{parent=blr_div,x=5,y=6,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg}
|
||||||
|
|
||||||
|
temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end)
|
||||||
|
|
||||||
|
local b_wll = IconIndicator{parent=blr_div,y=10,label="Water Level Lo",states=red_ind_s}
|
||||||
|
local b_hr = IconIndicator{parent=blr_div,label="Heating Rate Lo",states=yel_ind_s}
|
||||||
|
|
||||||
|
b_wll.register(ps, "WaterLevelLow", b_wll.update)
|
||||||
|
b_hr.register(ps, "HeatingRateLow", b_hr.update)
|
||||||
|
|
||||||
|
TextBox{parent=blr_div,text="Boil Rate",x=1,y=13,width=12,fg_bg=label}
|
||||||
|
local boil_r = DataIndicator{parent=blr_div,x=6,y=14,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg}
|
||||||
|
|
||||||
|
boil_r.register(ps, "boil_rate", boil_r.update)
|
||||||
|
|
||||||
|
local blr_ext_div = Div{parent=blr_pane,x=2,width=blr_pane.get_width()-2}
|
||||||
|
table.insert(panes, blr_ext_div)
|
||||||
|
|
||||||
|
local blr_ext_page = app.new_page(blr_page, #panes)
|
||||||
|
blr_ext_page.tasks = { update }
|
||||||
|
|
||||||
|
PushButton{parent=blr_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=blr_ext_page.nav_to}
|
||||||
|
PushButton{parent=blr_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=blr_page.nav_to}
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,y=1,text="More Boiler Info",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local function update_amount(indicator)
|
||||||
|
return function (x) indicator.update(x.amount) end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,text="Hot Coolant",x=1,y=3,width=12,fg_bg=label}
|
||||||
|
local heated_p = DataIndicator{parent=blr_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local hcool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
heated_p.register(ps, "hcool_fill", function (x) heated_p.update(x * 100) end)
|
||||||
|
hcool_amnt.register(ps, "hcool", update_amount(hcool_amnt))
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,text="Water Tank",x=1,y=6,width=9,fg_bg=label}
|
||||||
|
local fuel_p = DataIndicator{parent=blr_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local fuel_amnt = DataIndicator{parent=blr_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
fuel_p.register(ps, "water_fill", function (x) fuel_p.update(x * 100) end)
|
||||||
|
fuel_amnt.register(ps, "water", update_amount(fuel_amnt))
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,text="Steam Tank",x=1,y=9,width=10,fg_bg=label}
|
||||||
|
local steam_p = DataIndicator{parent=blr_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local steam_amnt = DataIndicator{parent=blr_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
steam_p.register(ps, "steam_fill", function (x) steam_p.update(x * 100) end)
|
||||||
|
steam_amnt.register(ps, "steam", update_amount(steam_amnt))
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,text="Cool Coolant",x=1,y=12,width=12,fg_bg=label}
|
||||||
|
local cooled_p = DataIndicator{parent=blr_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local ccool_amnt = DataIndicator{parent=blr_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
cooled_p.register(ps, "ccool_fill", function (x) cooled_p.update(x * 100) end)
|
||||||
|
ccool_amnt.register(ps, "ccool", update_amount(ccool_amnt))
|
||||||
|
|
||||||
|
TextBox{parent=blr_ext_div,text="Env. Loss",x=1,y=15,width=9,fg_bg=label}
|
||||||
|
local env_loss = DataIndicator{parent=blr_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg}
|
||||||
|
|
||||||
|
env_loss.register(ps, "env_loss", env_loss.update)
|
||||||
|
|
||||||
|
return blr_page.nav_to
|
||||||
|
end
|
||||||
158
pocket/ui/pages/unit_reactor.lua
Normal file
158
pocket/ui/pages/unit_reactor.lua
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
local types = require("scada-common.types")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local StateIndicator = require("graphics.elements.indicators.state")
|
||||||
|
local IconIndicator = require("graphics.elements.indicators.icon")
|
||||||
|
local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local label = style.label
|
||||||
|
local lu_col = style.label_unit_pair
|
||||||
|
local text_fg = style.text_fg
|
||||||
|
local red_ind_s = style.icon_states.red_ind_s
|
||||||
|
local yel_ind_s = style.icon_states.yel_ind_s
|
||||||
|
|
||||||
|
-- create a reactor view in the unit app
|
||||||
|
---@param app pocket_app
|
||||||
|
---@param u_page nav_tree_page
|
||||||
|
---@param panes table
|
||||||
|
---@param page_div graphics_element
|
||||||
|
---@param u_ps psil
|
||||||
|
---@param update function
|
||||||
|
return function (app, u_page, panes, page_div, u_ps, update)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local rct_pane = Div{parent=page_div}
|
||||||
|
local rct_div = Div{parent=rct_pane,x=2,width=page_div.get_width()-2}
|
||||||
|
table.insert(panes, rct_div)
|
||||||
|
|
||||||
|
local rct_page = app.new_page(u_page, #panes)
|
||||||
|
rct_page.tasks = { update }
|
||||||
|
|
||||||
|
TextBox{parent=rct_div,y=1,text="Reactor",width=8}
|
||||||
|
local status = StateIndicator{parent=rct_div,x=10,y=1,states=style.reactor.states,value=1,min_width=12}
|
||||||
|
status.register(u_ps, "U_ReactorStateStatus", status.update)
|
||||||
|
|
||||||
|
local fuel = VerticalBar{parent=rct_div,x=1,y=4,fg_bg=cpair(colors.lightGray,colors.gray),height=5,width=1}
|
||||||
|
local ccool = VerticalBar{parent=rct_div,x=3,y=4,fg_bg=cpair(colors.blue,colors.gray),height=5,width=1}
|
||||||
|
local hcool = VerticalBar{parent=rct_div,x=19,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1}
|
||||||
|
local waste = VerticalBar{parent=rct_div,x=21,y=4,fg_bg=cpair(colors.brown,colors.gray),height=5,width=1}
|
||||||
|
|
||||||
|
TextBox{parent=rct_div,text="F",x=1,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=rct_div,text="C",x=3,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=rct_div,text="H",x=19,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=rct_div,text="W",x=21,y=3,width=1,fg_bg=label}
|
||||||
|
|
||||||
|
fuel.register(u_ps, "fuel_fill", fuel.update)
|
||||||
|
ccool.register(u_ps, "ccool_fill", ccool.update)
|
||||||
|
hcool.register(u_ps, "hcool_fill", hcool.update)
|
||||||
|
waste.register(u_ps, "waste_fill", waste.update)
|
||||||
|
|
||||||
|
ccool.register(u_ps, "ccool_type", function (type)
|
||||||
|
if type == types.FLUID.SODIUM then
|
||||||
|
ccool.recolor(cpair(colors.lightBlue, colors.gray))
|
||||||
|
else
|
||||||
|
ccool.recolor(cpair(colors.blue, colors.gray))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
hcool.register(u_ps, "hcool_type", function (type)
|
||||||
|
if type == types.FLUID.SUPERHEATED_SODIUM then
|
||||||
|
hcool.recolor(cpair(colors.orange, colors.gray))
|
||||||
|
else
|
||||||
|
hcool.recolor(cpair(colors.white, colors.gray))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
TextBox{parent=rct_div,text="Burn Rate",x=5,y=4,width=13,fg_bg=label}
|
||||||
|
local burn_rate = DataIndicator{parent=rct_div,x=5,y=5,lu_colors=lu_col,label="",unit="mB/t",format="%8.2f",value=0,commas=true,width=13,fg_bg=text_fg}
|
||||||
|
TextBox{parent=rct_div,text="Temperature",x=5,y=6,width=13,fg_bg=label}
|
||||||
|
local t_prec = util.trinary(db.temp_label == types.TEMP_SCALE_UNITS[types.TEMP_SCALE.KELVIN], 11, 10)
|
||||||
|
local core_temp = DataIndicator{parent=rct_div,x=5,y=7,lu_colors=lu_col,label="",unit=db.temp_label,format="%"..t_prec..".2f",value=0,commas=true,width=13,fg_bg=text_fg}
|
||||||
|
|
||||||
|
burn_rate.register(u_ps, "act_burn_rate", burn_rate.update)
|
||||||
|
core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end)
|
||||||
|
|
||||||
|
local r_temp = IconIndicator{parent=rct_div,y=10,label="Reactor Temp. Hi",states=red_ind_s}
|
||||||
|
local r_rhdt = IconIndicator{parent=rct_div,label="Hi Delta Temp.",states=yel_ind_s}
|
||||||
|
local r_firl = IconIndicator{parent=rct_div,label="Fuel Rate Lo",states=yel_ind_s}
|
||||||
|
local r_wloc = IconIndicator{parent=rct_div,label="Waste Line Occl.",states=yel_ind_s}
|
||||||
|
local r_hsrt = IconIndicator{parent=rct_div,label="Hi Startup Rate",states=yel_ind_s}
|
||||||
|
|
||||||
|
r_temp.register(u_ps, "ReactorTempHigh", r_temp.update)
|
||||||
|
r_rhdt.register(u_ps, "ReactorHighDeltaT", r_rhdt.update)
|
||||||
|
r_firl.register(u_ps, "FuelInputRateLow", r_firl.update)
|
||||||
|
r_wloc.register(u_ps, "WasteLineOcclusion", r_wloc.update)
|
||||||
|
r_hsrt.register(u_ps, "HighStartupRate", r_hsrt.update)
|
||||||
|
|
||||||
|
TextBox{parent=rct_div,text="HR",x=1,y=16,width=4,fg_bg=label}
|
||||||
|
local heating_r = DataIndicator{parent=rct_div,x=6,y=16,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=text_fg}
|
||||||
|
TextBox{parent=rct_div,text="DMG",x=1,y=17,width=4,fg_bg=label}
|
||||||
|
local damage_p = DataIndicator{parent=rct_div,x=6,y=17,lu_colors=lu_col,label="",unit="%",format="%11.2f",value=0,width=16,fg_bg=text_fg}
|
||||||
|
|
||||||
|
heating_r.register(u_ps, "heating_rate", heating_r.update)
|
||||||
|
damage_p.register(u_ps, "damage", damage_p.update)
|
||||||
|
|
||||||
|
local rct_ext_div = Div{parent=rct_pane,x=2,width=page_div.get_width()-2}
|
||||||
|
table.insert(panes, rct_ext_div)
|
||||||
|
|
||||||
|
local rct_ext_page = app.new_page(rct_page, #panes)
|
||||||
|
rct_ext_page.tasks = { update }
|
||||||
|
|
||||||
|
PushButton{parent=rct_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_ext_page.nav_to}
|
||||||
|
PushButton{parent=rct_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=rct_page.nav_to}
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,y=1,text="More Reactor Info",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,text="Fuel Tank",x=1,y=3,width=9,fg_bg=label}
|
||||||
|
local fuel_p = DataIndicator{parent=rct_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local fuel_amnt = DataIndicator{parent=rct_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
fuel_p.register(u_ps, "fuel_fill", function (x) fuel_p.update(x * 100) end)
|
||||||
|
fuel_amnt.register(u_ps, "fuel", fuel_amnt.update)
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,text="Cool Coolant",x=1,y=6,width=12,fg_bg=label}
|
||||||
|
local cooled_p = DataIndicator{parent=rct_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local ccool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
cooled_p.register(u_ps, "ccool_fill", function (x) cooled_p.update(x * 100) end)
|
||||||
|
ccool_amnt.register(u_ps, "ccool_amnt", ccool_amnt.update)
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,text="Hot Coolant",x=1,y=9,width=12,fg_bg=label}
|
||||||
|
local heated_p = DataIndicator{parent=rct_ext_div,x=14,y=9,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local hcool_amnt = DataIndicator{parent=rct_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
heated_p.register(u_ps, "hcool_fill", function (x) heated_p.update(x * 100) end)
|
||||||
|
hcool_amnt.register(u_ps, "hcool_amnt", hcool_amnt.update)
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,text="Waste Tank",x=1,y=12,width=10,fg_bg=label}
|
||||||
|
local waste_p = DataIndicator{parent=rct_ext_div,x=14,y=12,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local waste_amnt = DataIndicator{parent=rct_ext_div,x=1,y=13,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
waste_p.register(u_ps, "waste_fill", function (x) waste_p.update(x * 100) end)
|
||||||
|
waste_amnt.register(u_ps, "waste", waste_amnt.update)
|
||||||
|
|
||||||
|
TextBox{parent=rct_ext_div,text="Boil Eff.",x=1,y=15,width=9,fg_bg=label}
|
||||||
|
TextBox{parent=rct_ext_div,text="Env. Loss",x=1,y=16,width=9,fg_bg=label}
|
||||||
|
local boil_eff = DataIndicator{parent=rct_ext_div,x=11,y=15,lu_colors=lu_col,label="",unit="%",format="%9.2f",value=0,width=11,fg_bg=text_fg}
|
||||||
|
local env_loss = DataIndicator{parent=rct_ext_div,x=11,y=16,lu_colors=lu_col,label="",unit="",format="%11.8f",value=0,width=11,fg_bg=text_fg}
|
||||||
|
|
||||||
|
boil_eff.register(u_ps, "boil_eff", function (x) boil_eff.update(x * 100) end)
|
||||||
|
env_loss.register(u_ps, "env_loss", env_loss.update)
|
||||||
|
|
||||||
|
return rct_page.nav_to
|
||||||
|
end
|
||||||
116
pocket/ui/pages/unit_turbine.lua
Normal file
116
pocket/ui/pages/unit_turbine.lua
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local IconIndicator = require("graphics.elements.indicators.icon")
|
||||||
|
local PowerIndicator = require("graphics.elements.indicators.power")
|
||||||
|
local StateIndicator = require("graphics.elements.indicators.state")
|
||||||
|
local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local label = style.label
|
||||||
|
local lu_col = style.label_unit_pair
|
||||||
|
local text_fg = style.text_fg
|
||||||
|
local tri_ind_s = style.icon_states.tri_ind_s
|
||||||
|
local red_ind_s = style.icon_states.red_ind_s
|
||||||
|
local yel_ind_s = style.icon_states.yel_ind_s
|
||||||
|
|
||||||
|
-- create a turbine view in the unit app
|
||||||
|
---@param app pocket_app
|
||||||
|
---@param u_page nav_tree_page
|
||||||
|
---@param panes table
|
||||||
|
---@param tbn_pane graphics_element
|
||||||
|
---@param u_id integer unit ID
|
||||||
|
---@param t_id integer turbine ID
|
||||||
|
---@param ps psil
|
||||||
|
---@param update function
|
||||||
|
return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local tbn_div = Div{parent=tbn_pane,x=2,width=tbn_pane.get_width()-2}
|
||||||
|
table.insert(panes, tbn_div)
|
||||||
|
|
||||||
|
local tbn_page = app.new_page(u_page, #panes)
|
||||||
|
tbn_page.tasks = { update }
|
||||||
|
|
||||||
|
TextBox{parent=tbn_div,y=1,text="TRBN #"..t_id,width=8}
|
||||||
|
local status = StateIndicator{parent=tbn_div,x=10,y=1,states=style.turbine.states,value=1,min_width=12}
|
||||||
|
status.register(ps, "TurbineStateStatus", status.update)
|
||||||
|
|
||||||
|
local steam = VerticalBar{parent=tbn_div,x=1,y=4,fg_bg=cpair(colors.white,colors.gray),height=5,width=1}
|
||||||
|
local ccool = VerticalBar{parent=tbn_div,x=21,y=4,fg_bg=cpair(colors.green,colors.gray),height=5,width=1}
|
||||||
|
|
||||||
|
TextBox{parent=tbn_div,text="S",x=1,y=3,width=1,fg_bg=label}
|
||||||
|
TextBox{parent=tbn_div,text="E",x=21,y=3,width=1,fg_bg=label}
|
||||||
|
|
||||||
|
steam.register(ps, "steam_fill", steam.update)
|
||||||
|
ccool.register(ps, "energy_fill", ccool.update)
|
||||||
|
|
||||||
|
TextBox{parent=tbn_div,text="Production",x=3,y=3,width=17,fg_bg=label}
|
||||||
|
local prod_rate = PowerIndicator{parent=tbn_div,x=3,y=4,lu_colors=lu_col,label="",unit=db.energy_label,format="%11.2f",value=0,rate=true,width=17,fg_bg=text_fg}
|
||||||
|
TextBox{parent=tbn_div,text="Flow Rate",x=3,y=5,width=17,fg_bg=label}
|
||||||
|
local flow_rate = DataIndicator{parent=tbn_div,x=3,y=6,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg}
|
||||||
|
TextBox{parent=tbn_div,text="Steam Input Rate",x=3,y=7,width=17,fg_bg=label}
|
||||||
|
local input_rate = DataIndicator{parent=tbn_div,x=3,y=8,lu_colors=lu_col,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=17,fg_bg=text_fg}
|
||||||
|
|
||||||
|
prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(db.energy_convert(val)) end)
|
||||||
|
flow_rate.register(ps, "flow_rate", flow_rate.update)
|
||||||
|
input_rate.register(ps, "steam_input_rate", input_rate.update)
|
||||||
|
|
||||||
|
local t_sdo = IconIndicator{parent=tbn_div,y=10,label="Steam Dumping",states=tri_ind_s}
|
||||||
|
local t_tos = IconIndicator{parent=tbn_div,label="Over Speed",states=red_ind_s}
|
||||||
|
local t_gtrp = IconIndicator{parent=tbn_div,label="Generator Trip",states=yel_ind_s}
|
||||||
|
local t_trp = IconIndicator{parent=tbn_div,label="Turbine Trip",states=red_ind_s}
|
||||||
|
|
||||||
|
t_sdo.register(ps, "SteamDumpOpen", t_sdo.update)
|
||||||
|
t_tos.register(ps, "TurbineOverSpeed", t_tos.update)
|
||||||
|
t_gtrp.register(ps, "GeneratorTrip", t_gtrp.update)
|
||||||
|
t_trp.register(ps, "TurbineTrip", t_trp.update)
|
||||||
|
|
||||||
|
local tbn_ext_div = Div{parent=tbn_pane,x=2,width=tbn_pane.get_width()-2}
|
||||||
|
table.insert(panes, tbn_ext_div)
|
||||||
|
|
||||||
|
local tbn_ext_page = app.new_page(tbn_page, #panes)
|
||||||
|
tbn_ext_page.tasks = { update }
|
||||||
|
|
||||||
|
PushButton{parent=tbn_div,x=9,y=18,text="MORE",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=tbn_ext_page.nav_to}
|
||||||
|
PushButton{parent=tbn_ext_div,x=9,y=18,text="BACK",min_width=6,fg_bg=cpair(colors.lightGray,colors.gray),active_fg_bg=cpair(colors.gray,colors.lightGray),callback=tbn_page.nav_to}
|
||||||
|
|
||||||
|
TextBox{parent=tbn_ext_div,y=1,text="More Turbine Info",alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
TextBox{parent=tbn_ext_div,text="Steam Tank",x=1,y=3,width=10,fg_bg=label}
|
||||||
|
local steam_p = DataIndicator{parent=tbn_ext_div,x=14,y=3,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local steam_amnt = DataIndicator{parent=tbn_ext_div,x=1,y=4,lu_colors=lu_col,label="",unit="mB",format="%18.0f",value=0,commas=true,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
steam_p.register(ps, "steam_fill", function (x) steam_p.update(x * 100) end)
|
||||||
|
steam_amnt.register(ps, "steam", function (x) steam_amnt.update(x.amount) end)
|
||||||
|
|
||||||
|
TextBox{parent=tbn_ext_div,text="Energy Fill",x=1,y=6,width=12,fg_bg=label}
|
||||||
|
local charge_p = DataIndicator{parent=tbn_ext_div,x=14,y=6,lu_colors=lu_col,label="",unit="%",format="%6.2f",value=0,width=8,fg_bg=text_fg}
|
||||||
|
local charge_amnt = PowerIndicator{parent=tbn_ext_div,x=1,y=7,lu_colors=lu_col,label="",unit=db.energy_label,format="%17.4f",value=0,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
charge_p.register(ps, "energy_fill", function (x) charge_p.update(x * 100) end)
|
||||||
|
charge_amnt.register(ps, "energy", function (val) charge_amnt.update(db.energy_convert(val)) end)
|
||||||
|
|
||||||
|
TextBox{parent=tbn_ext_div,text="Rotation Rate",x=1,y=9,width=13,fg_bg=label}
|
||||||
|
local rotation = DataIndicator{parent=tbn_ext_div,x=1,y=10,lu_colors=lu_col,label="",unit="",format="%21.12f",value=0,width=21,fg_bg=text_fg}
|
||||||
|
|
||||||
|
rotation.register(ps, "steam", function ()
|
||||||
|
local ok, result = pcall(function () return util.turbine_rotation(db.units[u_id].turbine_data_tbl[t_id]) end)
|
||||||
|
if ok then rotation.update(result) end
|
||||||
|
end)
|
||||||
|
|
||||||
|
return tbn_page.nav_to
|
||||||
|
end
|
||||||
@@ -12,7 +12,9 @@ local cpair = core.cpair
|
|||||||
|
|
||||||
style.root = cpair(colors.white, colors.black)
|
style.root = cpair(colors.white, colors.black)
|
||||||
style.header = cpair(colors.white, colors.gray)
|
style.header = cpair(colors.white, colors.gray)
|
||||||
style.label = cpair(colors.gray, colors.lightGray)
|
style.text_fg = cpair(colors.white, colors._INHERIT)
|
||||||
|
style.label = cpair(colors.lightGray, colors.black)
|
||||||
|
style.label_unit_pair = cpair(colors.lightGray, colors.lightGray)
|
||||||
|
|
||||||
style.colors = {
|
style.colors = {
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
{ c = colors.red, hex = 0xdf4949 },
|
||||||
@@ -33,6 +35,46 @@ style.colors = {
|
|||||||
-- { c = colors.brown, hex = 0x7f664c }
|
-- { c = colors.brown, hex = 0x7f664c }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local states = {}
|
||||||
|
|
||||||
|
states.basic_states = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.yellow), symbol = "\x1e" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
||||||
|
}
|
||||||
|
|
||||||
|
states.mode_states = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.purple), symbol = "A" }
|
||||||
|
}
|
||||||
|
|
||||||
|
states.emc_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.gray), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.white), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
||||||
|
}
|
||||||
|
|
||||||
|
states.tri_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.yellow), symbol = "\x1e" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" }
|
||||||
|
}
|
||||||
|
|
||||||
|
states.red_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" }
|
||||||
|
}
|
||||||
|
|
||||||
|
states.yel_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.yellow), symbol = "-" }
|
||||||
|
}
|
||||||
|
|
||||||
|
style.icon_states = states
|
||||||
|
|
||||||
-- MAIN LAYOUT --
|
-- MAIN LAYOUT --
|
||||||
|
|
||||||
style.reactor = {
|
style.reactor = {
|
||||||
@@ -40,7 +82,7 @@ style.reactor = {
|
|||||||
states = {
|
states = {
|
||||||
{
|
{
|
||||||
color = cpair(colors.black, colors.yellow),
|
color = cpair(colors.black, colors.yellow),
|
||||||
text = "PLC OFF-LINE"
|
text = "OFF-LINE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color = cpair(colors.black, colors.orange),
|
color = cpair(colors.black, colors.orange),
|
||||||
@@ -64,7 +106,7 @@ style.reactor = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
color = cpair(colors.black, colors.red),
|
color = cpair(colors.black, colors.red),
|
||||||
text = "FORCE DISABLED"
|
text = "FORCE DSBL"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
237
reactor-plc/config/check.lua
Normal file
237
reactor-plc/config/check.lua
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
local comms = require("scada-common.comms")
|
||||||
|
local network = require("scada-common.network")
|
||||||
|
local ppm = require("scada-common.ppm")
|
||||||
|
local tcd = require("scada-common.tcd")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local plc = require("reactor-plc.plc")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local tri = util.trinary
|
||||||
|
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local PROTOCOL = comms.PROTOCOL
|
||||||
|
local DEVICE_TYPE = comms.DEVICE_TYPE
|
||||||
|
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
||||||
|
local MGMT_TYPE = comms.MGMT_TYPE
|
||||||
|
|
||||||
|
local self = {
|
||||||
|
nic = nil, ---@type nic
|
||||||
|
net_listen = false,
|
||||||
|
sv_addr = comms.BROADCAST,
|
||||||
|
sv_seq_num = util.time_ms() * 10,
|
||||||
|
|
||||||
|
self_check_pass = true,
|
||||||
|
|
||||||
|
settings = nil, ---@type plc_config
|
||||||
|
|
||||||
|
run_test_btn = nil, ---@type graphics_element
|
||||||
|
self_check_msg = nil ---@type function
|
||||||
|
}
|
||||||
|
|
||||||
|
-- send a management packet to the supervisor
|
||||||
|
---@param msg_type MGMT_TYPE
|
||||||
|
---@param msg table
|
||||||
|
local function send_sv(msg_type, msg)
|
||||||
|
local s_pkt = comms.scada_packet()
|
||||||
|
local pkt = comms.mgmt_packet()
|
||||||
|
|
||||||
|
pkt.make(msg_type, msg)
|
||||||
|
s_pkt.make(self.sv_addr, self.sv_seq_num, PROTOCOL.SCADA_MGMT, pkt.raw_sendable())
|
||||||
|
|
||||||
|
self.nic.transmit(self.settings.SVR_Channel, self.settings.PLC_Channel, s_pkt)
|
||||||
|
self.sv_seq_num = self.sv_seq_num + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle an establish message from the supervisor
|
||||||
|
---@param packet mgmt_frame
|
||||||
|
local function handle_packet(packet)
|
||||||
|
local error_msg = nil
|
||||||
|
|
||||||
|
if packet.scada_frame.local_channel() ~= self.settings.PLC_Channel then
|
||||||
|
error_msg = "error: unknown receive channel"
|
||||||
|
elseif packet.scada_frame.remote_channel() == self.settings.SVR_Channel and packet.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
|
||||||
|
if packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
|
if packet.length == 1 then
|
||||||
|
local est_ack = packet.data[1]
|
||||||
|
|
||||||
|
if est_ack== ESTABLISH_ACK.ALLOW then
|
||||||
|
self.self_check_msg(nil, true, "")
|
||||||
|
self.sv_addr = packet.scada_frame.src_addr()
|
||||||
|
send_sv(MGMT_TYPE.CLOSE, {})
|
||||||
|
elseif est_ack == ESTABLISH_ACK.DENY then
|
||||||
|
error_msg = "error: supervisor connection denied"
|
||||||
|
elseif est_ack == ESTABLISH_ACK.COLLISION then
|
||||||
|
error_msg = "another reactor PLC is connected with this reactor unit ID"
|
||||||
|
elseif est_ack == ESTABLISH_ACK.BAD_VERSION then
|
||||||
|
error_msg = "reactor PLC comms version does not match supervisor comms version, make sure both devices are up-to-date (ccmsi update ...)"
|
||||||
|
else
|
||||||
|
error_msg = "error: invalid reply from supervisor"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error_msg = "error: invalid reply length from supervisor"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error_msg = "error: didn't get an establish reply from supervisor"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.net_listen = false
|
||||||
|
self.run_test_btn.enable()
|
||||||
|
|
||||||
|
if error_msg then
|
||||||
|
self.self_check_msg(nil, false, error_msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle supervisor connection failure
|
||||||
|
local function handle_timeout()
|
||||||
|
self.net_listen = false
|
||||||
|
self.run_test_btn.enable()
|
||||||
|
self.self_check_msg(nil, false, "make sure your supervisor is running, your channels are correct, trusted ranges are set properly (if enabled), facility keys match (if set), and if you are using wireless modems rather than ender modems, that your devices are close together in the same dimension")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute the self-check
|
||||||
|
---@param sc_log graphics_element
|
||||||
|
local function self_check(sc_log)
|
||||||
|
self.run_test_btn.disable()
|
||||||
|
|
||||||
|
sc_log.remove_all()
|
||||||
|
ppm.mount_all()
|
||||||
|
|
||||||
|
self.self_check_pass = true
|
||||||
|
|
||||||
|
local modem = ppm.get_wireless_modem()
|
||||||
|
local reactor = ppm.get_fission_reactor()
|
||||||
|
local valid_cfg = plc.validate_config(self.settings)
|
||||||
|
|
||||||
|
self.self_check_msg("> check wireless/ender modem connected...", modem ~= nil, "you must connect an ender or wireless modem to the reactor PLC")
|
||||||
|
self.self_check_msg("> check fission reactor connected...", reactor ~= nil, "please connect the reactor PLC to the reactor's fission reactor logic adapter")
|
||||||
|
self.self_check_msg("> check fission reactor formed...")
|
||||||
|
-- this consumes events, but that is fine here
|
||||||
|
self.self_check_msg(nil, reactor and reactor.isFormed(), "ensure the fission reactor multiblock is formed")
|
||||||
|
|
||||||
|
self.self_check_msg("> check configuration...", valid_cfg, "go through Configure System and apply settings to set any missing settings and repair any corrupted ones")
|
||||||
|
|
||||||
|
if valid_cfg and modem then
|
||||||
|
self.self_check_msg("> check supervisor connection...")
|
||||||
|
|
||||||
|
-- init mac as needed
|
||||||
|
if self.settings.AuthKey and string.len(self.settings.AuthKey) >= 8 then
|
||||||
|
network.init_mac(self.settings.AuthKey)
|
||||||
|
else
|
||||||
|
network.deinit_mac()
|
||||||
|
end
|
||||||
|
|
||||||
|
self.nic = network.nic(modem)
|
||||||
|
|
||||||
|
self.nic.closeAll()
|
||||||
|
self.nic.open(self.settings.PLC_Channel)
|
||||||
|
|
||||||
|
self.sv_addr = comms.BROADCAST
|
||||||
|
self.net_listen = true
|
||||||
|
|
||||||
|
send_sv(MGMT_TYPE.ESTABLISH, { comms.version, "0.0.0", DEVICE_TYPE.PLC, self.settings.UnitID })
|
||||||
|
|
||||||
|
tcd.dispatch_unique(8, handle_timeout)
|
||||||
|
else
|
||||||
|
self.run_test_btn.enable()
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.self_check_pass then
|
||||||
|
TextBox{parent=sc_log,text="> all tests passed!",fg_bg=cpair(colors.blue,colors._INHERIT)}
|
||||||
|
TextBox{parent=sc_log,text=""}
|
||||||
|
local more = Div{parent=sc_log,height=3,fg_bg=cpair(colors.gray,colors._INHERIT)}
|
||||||
|
TextBox{parent=more,text="if you still have a problem:"}
|
||||||
|
TextBox{parent=more,text="- check the wiki on GitHub"}
|
||||||
|
TextBox{parent=more,text="- ask for help on GitHub discussions or Discord"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- exit self check back home
|
||||||
|
---@param sc_log graphics_element
|
||||||
|
---@param main_pane graphics_element
|
||||||
|
local function exit_self_check(sc_log, main_pane)
|
||||||
|
tcd.abort(handle_timeout)
|
||||||
|
self.net_listen = false
|
||||||
|
self.run_test_btn.enable()
|
||||||
|
sc_log.remove_all()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local check = {}
|
||||||
|
|
||||||
|
-- create the self-check view
|
||||||
|
---@param main_pane graphics_element
|
||||||
|
---@param settings_cfg plc_config
|
||||||
|
---@param check_sys graphics_element
|
||||||
|
---@param style table
|
||||||
|
function check.create(main_pane, settings_cfg, check_sys, style)
|
||||||
|
local bw_fg_bg = style.bw_fg_bg
|
||||||
|
local g_lg_fg_bg = style.g_lg_fg_bg
|
||||||
|
local nav_fg_bg = style.nav_fg_bg
|
||||||
|
local btn_act_fg_bg = style.btn_act_fg_bg
|
||||||
|
local btn_dis_fg_bg = style.btn_dis_fg_bg
|
||||||
|
|
||||||
|
self.settings = settings_cfg
|
||||||
|
|
||||||
|
local sc = Div{parent=check_sys,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
TextBox{parent=check_sys,x=1,y=2,text=" Reactor PLC Self-Check",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
|
local sc_log = ListBox{parent=sc,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
|
local last_check = { nil, nil }
|
||||||
|
|
||||||
|
function self.self_check_msg(msg, success, fail_msg)
|
||||||
|
if type(msg) == "string" then
|
||||||
|
last_check[1] = Div{parent=sc_log,height=1}
|
||||||
|
local e = TextBox{parent=last_check[1],text=msg,fg_bg=bw_fg_bg}
|
||||||
|
last_check[2] = e.get_x()+string.len(msg)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(fail_msg) == "string" then
|
||||||
|
TextBox{parent=last_check[1],x=last_check[2],y=1,text=tri(success,"PASS","FAIL"),fg_bg=tri(success,cpair(colors.green,colors._INHERIT),cpair(colors.red,colors._INHERIT))}
|
||||||
|
|
||||||
|
if not success then
|
||||||
|
local fail = Div{parent=sc_log,height=#util.strwrap(fail_msg, 46)}
|
||||||
|
TextBox{parent=fail,x=3,text=fail_msg,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
end
|
||||||
|
|
||||||
|
self.self_check_pass = self.self_check_pass and success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=sc,x=1,y=14,text="\x1b Back",callback=function()exit_self_check(sc_log,main_pane)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
self.run_test_btn = PushButton{parent=sc,x=40,y=14,min_width=10,text="Run Test",callback=function()self_check(sc_log)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle incoming modem messages
|
||||||
|
---@param side string
|
||||||
|
---@param sender integer
|
||||||
|
---@param reply_to integer
|
||||||
|
---@param message any
|
||||||
|
---@param distance integer
|
||||||
|
function check.receive_sv(side, sender, reply_to, message, distance)
|
||||||
|
if self.nic ~= nil and self.net_listen then
|
||||||
|
local s_pkt = self.nic.receive(side, sender, reply_to, message, distance)
|
||||||
|
|
||||||
|
if s_pkt and s_pkt.protocol() == PROTOCOL.SCADA_MGMT then
|
||||||
|
local mgmt_pkt = comms.mgmt_packet()
|
||||||
|
if mgmt_pkt.decode(s_pkt) then
|
||||||
|
tcd.abort(handle_timeout)
|
||||||
|
handle_packet(mgmt_pkt.get())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return check
|
||||||
618
reactor-plc/config/system.lua
Normal file
618
reactor-plc/config/system.lua
Normal file
@@ -0,0 +1,618 @@
|
|||||||
|
local log = require("scada-common.log")
|
||||||
|
local rsio = require("scada-common.rsio")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local CheckBox = require("graphics.elements.controls.checkbox")
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
local Radio2D = require("graphics.elements.controls.radio_2d")
|
||||||
|
local RadioButton = require("graphics.elements.controls.radio_button")
|
||||||
|
|
||||||
|
local NumberField = require("graphics.elements.form.number_field")
|
||||||
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local IndLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local LEFT = core.ALIGN.LEFT
|
||||||
|
local RIGHT = core.ALIGN.RIGHT
|
||||||
|
|
||||||
|
local self = {
|
||||||
|
importing_legacy = false,
|
||||||
|
|
||||||
|
set_networked = nil, ---@type function
|
||||||
|
bundled_emcool = nil, ---@type function
|
||||||
|
|
||||||
|
show_auth_key = nil, ---@type function
|
||||||
|
show_key_btn = nil, ---@type graphics_element
|
||||||
|
auth_key_textbox = nil, ---@type graphics_element
|
||||||
|
auth_key_value = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
||||||
|
local side_options_map = { "top", "bottom", "left", "right", "front", "back" }
|
||||||
|
local color_options = { "Red", "Orange", "Yellow", "Lime", "Green", "Cyan", "Light Blue", "Blue", "Purple", "Magenta", "Pink", "White", "Light Gray", "Gray", "Black", "Brown" }
|
||||||
|
local color_options_map = { colors.red, colors.orange, colors.yellow, colors.lime, colors.green, colors.cyan, colors.lightBlue, colors.blue, colors.purple, colors.magenta, colors.pink, colors.white, colors.lightGray, colors.gray, colors.black, colors.brown }
|
||||||
|
|
||||||
|
-- convert text representation to index
|
||||||
|
---@param side string
|
||||||
|
local function side_to_idx(side)
|
||||||
|
for k, v in ipairs(side_options_map) do
|
||||||
|
if v == side then return k end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- convert color to index
|
||||||
|
---@param color color
|
||||||
|
local function color_to_idx(color)
|
||||||
|
for k, v in ipairs(color_options_map) do
|
||||||
|
if v == color then return k end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local system = {}
|
||||||
|
|
||||||
|
-- create the system configuration view
|
||||||
|
---@param tool_ctl _plc_cfg_tool_ctl
|
||||||
|
---@param main_pane graphics_element
|
||||||
|
---@param cfg_sys table
|
||||||
|
---@param divs table
|
||||||
|
---@param style table
|
||||||
|
---@param exit function
|
||||||
|
function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit)
|
||||||
|
---@type plc_config, plc_config, plc_config, table, function
|
||||||
|
local settings_cfg, ini_cfg, tmp_cfg, fields, load_settings = table.unpack(cfg_sys)
|
||||||
|
|
||||||
|
---@type graphics_element, graphics_element, graphics_element, graphics_element, graphics_element
|
||||||
|
local plc_cfg, net_cfg, log_cfg, clr_cfg, summary = table.unpack(divs)
|
||||||
|
|
||||||
|
local bw_fg_bg = style.bw_fg_bg
|
||||||
|
local g_lg_fg_bg = style.g_lg_fg_bg
|
||||||
|
local nav_fg_bg = style.nav_fg_bg
|
||||||
|
local btn_act_fg_bg = style.btn_act_fg_bg
|
||||||
|
local btn_dis_fg_bg = style.btn_dis_fg_bg
|
||||||
|
|
||||||
|
--#region PLC
|
||||||
|
|
||||||
|
local plc_c_1 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
||||||
|
local plc_c_2 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
||||||
|
local plc_c_3 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
||||||
|
local plc_c_4 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local plc_pane = MultiPane{parent=plc_cfg,x=1,y=4,panes={plc_c_1,plc_c_2,plc_c_3,plc_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=plc_cfg,x=1,y=2,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_1,x=1,y=1,text="Would you like to set this PLC as networked?"}
|
||||||
|
TextBox{parent=plc_c_1,x=1,y=3,height=4,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
local networked = CheckBox{parent=plc_c_1,x=1,y=8,label="Networked",default=ini_cfg.Networked,box_fg_bg=cpair(colors.orange,colors.black)}
|
||||||
|
|
||||||
|
local function submit_networked()
|
||||||
|
self.set_networked(networked.get_value())
|
||||||
|
plc_pane.set_value(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=plc_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=plc_c_1,x=44,y=14,text="Next \x1a",callback=submit_networked,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_2,x=1,y=1,text="Please enter the reactor unit ID for this PLC."}
|
||||||
|
TextBox{parent=plc_c_2,x=1,y=3,height=3,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_2,x=1,y=6,text="Unit #"}
|
||||||
|
local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_chars=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
|
local u_id_err = TextBox{parent=plc_c_2,x=8,y=14,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
|
function self.set_networked(enable)
|
||||||
|
tmp_cfg.Networked = enable
|
||||||
|
if enable then u_id.set_max(4) else u_id.set_max(999) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_id()
|
||||||
|
local unit_id = tonumber(u_id.get_value())
|
||||||
|
if unit_id ~= nil then
|
||||||
|
u_id_err.hide(true)
|
||||||
|
tmp_cfg.UnitID = unit_id
|
||||||
|
plc_pane.set_value(3)
|
||||||
|
else u_id_err.show() end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=plc_c_2,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=plc_c_2,x=44,y=14,text="Next \x1a",callback=submit_id,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_3,x=1,y=1,height=4,text="When networked, the supervisor takes care of emergency coolant via RTUs. However, you can configure independent emergency coolant via the PLC."}
|
||||||
|
TextBox{parent=plc_c_3,x=1,y=6,height=5,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
local en_em_cool = CheckBox{parent=plc_c_3,x=1,y=11,label="Enable PLC Emergency Coolant Control",default=ini_cfg.EmerCoolEnable,box_fg_bg=cpair(colors.orange,colors.black)}
|
||||||
|
|
||||||
|
local function next_from_plc()
|
||||||
|
if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(4) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_en_emcool()
|
||||||
|
tmp_cfg.EmerCoolEnable = en_em_cool.get_value()
|
||||||
|
if tmp_cfg.EmerCoolEnable then plc_pane.set_value(4) else next_from_plc() end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=plc_c_3,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=plc_c_3,x=44,y=14,text="Next \x1a",callback=submit_en_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_4,x=1,y=1,text="Emergency Coolant Redstone Output Side"}
|
||||||
|
local side = Radio2D{parent=plc_c_4,x=1,y=2,rows=2,columns=3,default=side_to_idx(ini_cfg.EmerCoolSide),options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.orange}
|
||||||
|
|
||||||
|
TextBox{parent=plc_c_4,x=1,y=5,text="Bundled Redstone Configuration"}
|
||||||
|
local bundled = CheckBox{parent=plc_c_4,x=1,y=6,label="Is Bundled?",default=ini_cfg.EmerCoolColor~=nil,box_fg_bg=cpair(colors.orange,colors.black),callback=function(v)self.bundled_emcool(v)end}
|
||||||
|
local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
||||||
|
if ini_cfg.EmerCoolColor == nil then color.disable() end
|
||||||
|
|
||||||
|
function self.bundled_emcool(en) if en then color.enable() else color.disable() end end
|
||||||
|
|
||||||
|
local function submit_emcool()
|
||||||
|
tmp_cfg.EmerCoolSide = side_options_map[side.get_value()]
|
||||||
|
tmp_cfg.EmerCoolColor = util.trinary(bundled.get_value(), color_options_map[color.get_value()], nil)
|
||||||
|
next_from_plc()
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=plc_c_4,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=plc_c_4,x=44,y=14,text="Next \x1a",callback=submit_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Network
|
||||||
|
|
||||||
|
local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49}
|
||||||
|
local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49}
|
||||||
|
local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3}}
|
||||||
|
|
||||||
|
TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_1,x=1,y=1,text="Please set the network channels below."}
|
||||||
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_1,x=1,y=8,text="Supervisor Channel"}
|
||||||
|
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
|
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
TextBox{parent=net_c_1,x=1,y=11,text="PLC Channel"}
|
||||||
|
local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
|
TextBox{parent=net_c_1,x=9,y=12,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
|
local function submit_channels()
|
||||||
|
local svr_c = tonumber(svr_chan.get_value())
|
||||||
|
local plc_c = tonumber(plc_chan.get_value())
|
||||||
|
if svr_c ~= nil and plc_c ~= nil then
|
||||||
|
tmp_cfg.SVR_Channel = svr_c
|
||||||
|
tmp_cfg.PLC_Channel = plc_c
|
||||||
|
net_pane.set_value(2)
|
||||||
|
chan_err.hide(true)
|
||||||
|
elseif svr_c == nil then
|
||||||
|
chan_err.set_value("Please set the supervisor channel.")
|
||||||
|
chan_err.show()
|
||||||
|
else
|
||||||
|
chan_err.set_value("Please set the PLC channel.")
|
||||||
|
chan_err.show()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_2,x=1,y=1,text="Connection Timeout"}
|
||||||
|
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
||||||
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_2,x=1,y=8,text="Trusted Range"}
|
||||||
|
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
local p2_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
|
local function submit_ct_tr()
|
||||||
|
local timeout_val = tonumber(timeout.get_value())
|
||||||
|
local range_val = tonumber(range.get_value())
|
||||||
|
if timeout_val ~= nil and range_val ~= nil then
|
||||||
|
tmp_cfg.ConnTimeout = timeout_val
|
||||||
|
tmp_cfg.TrustedRange = range_val
|
||||||
|
net_pane.set_value(3)
|
||||||
|
p2_err.hide(true)
|
||||||
|
elseif timeout_val == nil then
|
||||||
|
p2_err.set_value("Please set the connection timeout.")
|
||||||
|
p2_err.show()
|
||||||
|
else
|
||||||
|
p2_err.set_value("Please set the trusted range.")
|
||||||
|
p2_err.show()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_ct_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_3,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
||||||
|
TextBox{parent=net_c_3,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=net_c_3,x=1,y=11,text="Facility Auth Key"}
|
||||||
|
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||||
|
|
||||||
|
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
||||||
|
|
||||||
|
hide_key.set_value(true)
|
||||||
|
censor_key(true)
|
||||||
|
|
||||||
|
local key_err = TextBox{parent=net_c_3,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
|
local function submit_auth()
|
||||||
|
local v = key.get_value()
|
||||||
|
if string.len(v) == 0 or string.len(v) >= 8 then
|
||||||
|
tmp_cfg.AuthKey = key.get_value()
|
||||||
|
main_pane.set_value(4)
|
||||||
|
key_err.hide(true)
|
||||||
|
else key_err.show() end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Logging
|
||||||
|
|
||||||
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
||||||
|
|
||||||
|
TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."}
|
||||||
|
|
||||||
|
TextBox{parent=log_c_1,x=1,y=3,text="Log File Mode"}
|
||||||
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
||||||
|
|
||||||
|
TextBox{parent=log_c_1,x=1,y=7,text="Log File Path"}
|
||||||
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
||||||
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
|
local function submit_log()
|
||||||
|
if path.get_value() ~= "" then
|
||||||
|
path_err.hide(true)
|
||||||
|
tmp_cfg.LogMode = mode.get_value() - 1
|
||||||
|
tmp_cfg.LogPath = path.get_value()
|
||||||
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
tool_ctl.color_next.show()
|
||||||
|
main_pane.set_value(5)
|
||||||
|
else path_err.show() end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function back_from_log()
|
||||||
|
if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(2) end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=back_from_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Color Options
|
||||||
|
|
||||||
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=7,text="Front Panel Theme"}
|
||||||
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=7,text="Preview"}
|
||||||
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
||||||
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
||||||
|
|
||||||
|
local function recolor(value)
|
||||||
|
local c = themes.smooth_stone.color_modes[value]
|
||||||
|
|
||||||
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
b_off.hide()
|
||||||
|
g_off.show()
|
||||||
|
else
|
||||||
|
g_off.hide()
|
||||||
|
b_off.show()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #c == 0 then
|
||||||
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
||||||
|
else
|
||||||
|
term.setPaletteColor(colors.green, c[1].hex)
|
||||||
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
||||||
|
term.setPaletteColor(colors.red, c[3].hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"}
|
||||||
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function back_from_colors()
|
||||||
|
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4))
|
||||||
|
tool_ctl.jumped_to_color = false
|
||||||
|
recolor(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_access()
|
||||||
|
clr_pane.set_value(2)
|
||||||
|
recolor(c_mode.get_value())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_colors()
|
||||||
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
||||||
|
tmp_cfg.ColorMode = c_mode.get_value()
|
||||||
|
|
||||||
|
if tool_ctl.jumped_to_color then
|
||||||
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
||||||
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
||||||
|
|
||||||
|
if settings.save("/reactor-plc.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
clr_pane.set_value(3)
|
||||||
|
else
|
||||||
|
clr_pane.set_value(4)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
self.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
main_pane.set_value(6)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
|
local function c_go_home()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"}
|
||||||
|
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=clr_c_4,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
PushButton{parent=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Summary and Saving
|
||||||
|
|
||||||
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
|
local sum_c_2 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
|
local sum_c_3 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
|
local sum_c_4 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=summary,x=1,y=2,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
||||||
|
|
||||||
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
|
local function back_from_settings()
|
||||||
|
if tool_ctl.viewing_config or self.importing_legacy then
|
||||||
|
main_pane.set_value(1)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
self.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
else
|
||||||
|
main_pane.set_value(5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param element graphics_element
|
||||||
|
---@param data any
|
||||||
|
local function try_set(element, data)
|
||||||
|
if data ~= nil then element.set_value(data) end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function save_and_continue()
|
||||||
|
for k, v in pairs(tmp_cfg) do settings.set(k, v) end
|
||||||
|
|
||||||
|
if settings.save("/reactor-plc.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
|
||||||
|
try_set(networked, ini_cfg.Networked)
|
||||||
|
try_set(u_id, ini_cfg.UnitID)
|
||||||
|
try_set(en_em_cool, ini_cfg.EmerCoolEnable)
|
||||||
|
try_set(side, side_to_idx(ini_cfg.EmerCoolSide))
|
||||||
|
try_set(bundled, ini_cfg.EmerCoolColor ~= nil)
|
||||||
|
if ini_cfg.EmerCoolColor ~= nil then try_set(color, color_to_idx(ini_cfg.EmerCoolColor)) end
|
||||||
|
try_set(svr_chan, ini_cfg.SVR_Channel)
|
||||||
|
try_set(plc_chan, ini_cfg.PLC_Channel)
|
||||||
|
try_set(timeout, ini_cfg.ConnTimeout)
|
||||||
|
try_set(range, ini_cfg.TrustedRange)
|
||||||
|
try_set(key, ini_cfg.AuthKey)
|
||||||
|
try_set(mode, ini_cfg.LogMode)
|
||||||
|
try_set(path, ini_cfg.LogPath)
|
||||||
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
||||||
|
try_set(c_mode, ini_cfg.ColorMode)
|
||||||
|
|
||||||
|
tool_ctl.view_cfg.enable()
|
||||||
|
tool_ctl.color_cfg.enable()
|
||||||
|
|
||||||
|
if self.importing_legacy then
|
||||||
|
self.importing_legacy = false
|
||||||
|
sum_pane.set_value(3)
|
||||||
|
else
|
||||||
|
sum_pane.set_value(2)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
sum_pane.set_value(4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
self.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()self.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg}
|
||||||
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=sum_c_2,x=1,y=1,text="Settings saved!"}
|
||||||
|
TextBox{parent=sum_c_2,x=1,y=3,text="Tip: you can run a Self-Check from the configurator home screen to make sure everything is going to work right!"}
|
||||||
|
|
||||||
|
local function go_home()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
plc_pane.set_value(1)
|
||||||
|
net_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
|
sum_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."}
|
||||||
|
|
||||||
|
local function delete_legacy()
|
||||||
|
fs.delete("/reactor-plc/config.lua")
|
||||||
|
exit()
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Tool Functions
|
||||||
|
|
||||||
|
-- load a legacy config file
|
||||||
|
function tool_ctl.load_legacy()
|
||||||
|
local config = require("reactor-plc.config")
|
||||||
|
|
||||||
|
tmp_cfg.Networked = config.NETWORKED
|
||||||
|
tmp_cfg.UnitID = config.REACTOR_ID
|
||||||
|
tmp_cfg.EmerCoolEnable = type(config.EMERGENCY_COOL) == "table"
|
||||||
|
|
||||||
|
if tmp_cfg.EmerCoolEnable then
|
||||||
|
tmp_cfg.EmerCoolSide = config.EMERGENCY_COOL.side
|
||||||
|
tmp_cfg.EmerCoolColor = config.EMERGENCY_COOL.color
|
||||||
|
else
|
||||||
|
tmp_cfg.EmerCoolSide = nil
|
||||||
|
tmp_cfg.EmerCoolColor = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
||||||
|
tmp_cfg.PLC_Channel = config.PLC_CHANNEL
|
||||||
|
tmp_cfg.ConnTimeout = config.COMMS_TIMEOUT
|
||||||
|
tmp_cfg.TrustedRange = config.TRUSTED_RANGE
|
||||||
|
tmp_cfg.AuthKey = config.AUTH_KEY or ""
|
||||||
|
tmp_cfg.LogMode = config.LOG_MODE
|
||||||
|
tmp_cfg.LogPath = config.LOG_PATH
|
||||||
|
tmp_cfg.LogDebug = config.LOG_DEBUG or false
|
||||||
|
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
sum_pane.set_value(1)
|
||||||
|
main_pane.set_value(6)
|
||||||
|
self.importing_legacy = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- expose the auth key on the summary page
|
||||||
|
function self.show_auth_key()
|
||||||
|
self.show_key_btn.disable()
|
||||||
|
self.auth_key_textbox.set_value(self.auth_key_value)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- generate the summary list
|
||||||
|
---@param cfg plc_config
|
||||||
|
function tool_ctl.gen_summary(cfg)
|
||||||
|
setting_list.remove_all()
|
||||||
|
|
||||||
|
local alternate = false
|
||||||
|
local inner_width = setting_list.get_width() - 1
|
||||||
|
|
||||||
|
if cfg.AuthKey then self.show_key_btn.enable() else self.show_key_btn.disable() end
|
||||||
|
self.auth_key_value = cfg.AuthKey or "" -- to show auth key
|
||||||
|
|
||||||
|
for i = 1, #fields do
|
||||||
|
local f = fields[i]
|
||||||
|
local height = 1
|
||||||
|
local label_w = string.len(f[2])
|
||||||
|
local val_max_w = (inner_width - label_w) + 1
|
||||||
|
local raw = cfg[f[1]]
|
||||||
|
local val = util.strval(raw)
|
||||||
|
|
||||||
|
if f[1] == "AuthKey" and raw then val = string.rep("*", string.len(val))
|
||||||
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
|
elseif f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw)
|
||||||
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
|
elseif f[1] == "ColorMode" then
|
||||||
|
val = util.strval(themes.color_mode_name(raw))
|
||||||
|
end
|
||||||
|
|
||||||
|
if val == "nil" then val = "<not set>" end
|
||||||
|
|
||||||
|
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
|
alternate = not alternate
|
||||||
|
|
||||||
|
if string.len(val) > val_max_w then
|
||||||
|
local lines = util.strwrap(val, inner_width)
|
||||||
|
height = #lines + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local line = Div{parent=setting_list,height=height,fg_bg=c}
|
||||||
|
TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
||||||
|
|
||||||
|
local textbox
|
||||||
|
if height > 1 then
|
||||||
|
textbox = TextBox{parent=line,x=1,y=2,text=val,height=height-1,alignment=LEFT}
|
||||||
|
else
|
||||||
|
textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT}
|
||||||
|
end
|
||||||
|
|
||||||
|
if f[1] == "AuthKey" then self.auth_key_textbox = textbox end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
end
|
||||||
|
|
||||||
|
return system
|
||||||
@@ -2,38 +2,30 @@
|
|||||||
-- Configuration GUI
|
-- Configuration GUI
|
||||||
--
|
--
|
||||||
|
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local rsio = require("scada-common.rsio")
|
local tcd = require("scada-common.tcd")
|
||||||
local tcd = require("scada-common.tcd")
|
local util = require("scada-common.util")
|
||||||
local util = require("scada-common.util")
|
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local check = require("reactor-plc.config.check")
|
||||||
local themes = require("graphics.themes")
|
local system = require("reactor-plc.config.system")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local core = require("graphics.core")
|
||||||
local Div = require("graphics.elements.div")
|
local themes = require("graphics.themes")
|
||||||
local ListBox = require("graphics.elements.listbox")
|
|
||||||
local MultiPane = require("graphics.elements.multipane")
|
|
||||||
local TextBox = require("graphics.elements.textbox")
|
|
||||||
|
|
||||||
local CheckBox = require("graphics.elements.controls.checkbox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local Div = require("graphics.elements.div")
|
||||||
local Radio2D = require("graphics.elements.controls.radio_2d")
|
local ListBox = require("graphics.elements.listbox")
|
||||||
local RadioButton = require("graphics.elements.controls.radio_button")
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
local NumberField = require("graphics.elements.form.number_field")
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
local TextField = require("graphics.elements.form.text_field")
|
|
||||||
|
|
||||||
local IndLight = require("graphics.elements.indicators.light")
|
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local tri = util.trinary
|
local tri = util.trinary
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local LEFT = core.ALIGN.LEFT
|
|
||||||
local CENTER = core.ALIGN.CENTER
|
local CENTER = core.ALIGN.CENTER
|
||||||
local RIGHT = core.ALIGN.RIGHT
|
|
||||||
|
|
||||||
-- changes to the config data/format to let the user know
|
-- changes to the config data/format to let the user know
|
||||||
local changes = {
|
local changes = {
|
||||||
@@ -48,22 +40,22 @@ local configurator = {}
|
|||||||
|
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
style.root = cpair(colors.black, colors.lightGray)
|
style.root = cpair(colors.black, colors.lightGray)
|
||||||
style.header = cpair(colors.white, colors.gray)
|
style.header = cpair(colors.white, colors.gray)
|
||||||
|
|
||||||
style.colors = themes.smooth_stone.colors
|
style.colors = themes.smooth_stone.colors
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
style.bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
style.g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
local nav_fg_bg = bw_fg_bg
|
style.nav_fg_bg = style.bw_fg_bg
|
||||||
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
style.btn_act_fg_bg = cpair(colors.white, colors.gray)
|
||||||
|
style.btn_dis_fg_bg = cpair(colors.lightGray, colors.white)
|
||||||
|
|
||||||
---@class _plc_cfg_tool_ctl
|
---@class _plc_cfg_tool_ctl
|
||||||
local tool_ctl = {
|
local tool_ctl = {
|
||||||
ask_config = false,
|
ask_config = false,
|
||||||
has_config = false,
|
has_config = false,
|
||||||
viewing_config = false,
|
viewing_config = false,
|
||||||
importing_legacy = false,
|
|
||||||
jumped_to_color = false,
|
jumped_to_color = false,
|
||||||
|
|
||||||
view_cfg = nil, ---@type graphics_element
|
view_cfg = nil, ---@type graphics_element
|
||||||
@@ -72,16 +64,8 @@ local tool_ctl = {
|
|||||||
color_apply = nil, ---@type graphics_element
|
color_apply = nil, ---@type graphics_element
|
||||||
settings_apply = nil, ---@type graphics_element
|
settings_apply = nil, ---@type graphics_element
|
||||||
|
|
||||||
set_networked = nil, ---@type function
|
|
||||||
bundled_emcool = nil, ---@type function
|
|
||||||
gen_summary = nil, ---@type function
|
gen_summary = nil, ---@type function
|
||||||
show_current_cfg = nil, ---@type function
|
|
||||||
load_legacy = nil, ---@type function
|
load_legacy = nil, ---@type function
|
||||||
|
|
||||||
show_auth_key = nil, ---@type function
|
|
||||||
show_key_btn = nil, ---@type graphics_element
|
|
||||||
auth_key_textbox = nil, ---@type graphics_element
|
|
||||||
auth_key_value = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class plc_config
|
---@class plc_config
|
||||||
@@ -127,27 +111,6 @@ local fields = {
|
|||||||
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
||||||
}
|
}
|
||||||
|
|
||||||
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
|
||||||
local side_options_map = { "top", "bottom", "left", "right", "front", "back" }
|
|
||||||
local color_options = { "Red", "Orange", "Yellow", "Lime", "Green", "Cyan", "Light Blue", "Blue", "Purple", "Magenta", "Pink", "White", "Light Gray", "Gray", "Black", "Brown" }
|
|
||||||
local color_options_map = { colors.red, colors.orange, colors.yellow, colors.lime, colors.green, colors.cyan, colors.lightBlue, colors.blue, colors.purple, colors.magenta, colors.pink, colors.white, colors.lightGray, colors.gray, colors.black, colors.brown }
|
|
||||||
|
|
||||||
-- convert text representation to index
|
|
||||||
---@param side string
|
|
||||||
local function side_to_idx(side)
|
|
||||||
for k, v in ipairs(side_options_map) do
|
|
||||||
if v == side then return k end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- convert color to index
|
|
||||||
---@param color color
|
|
||||||
local function color_to_idx(color)
|
|
||||||
for k, v in ipairs(color_options_map) do
|
|
||||||
if v == color then return k end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- load data from the settings file
|
-- load data from the settings file
|
||||||
---@param target plc_config
|
---@param target plc_config
|
||||||
---@param raw boolean? true to not use default values
|
---@param raw boolean? true to not use default values
|
||||||
@@ -164,11 +127,16 @@ end
|
|||||||
-- create the config view
|
-- create the config view
|
||||||
---@param display graphics_element
|
---@param display graphics_element
|
||||||
local function config_view(display)
|
local function config_view(display)
|
||||||
|
local bw_fg_bg = style.bw_fg_bg
|
||||||
|
local g_lg_fg_bg = style.g_lg_fg_bg
|
||||||
|
local nav_fg_bg = style.nav_fg_bg
|
||||||
|
local btn_act_fg_bg = style.btn_act_fg_bg
|
||||||
|
local btn_dis_fg_bg = style.btn_dis_fg_bg
|
||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local function exit() os.queueEvent("terminate") end
|
local function exit() os.queueEvent("terminate") end
|
||||||
|
|
||||||
TextBox{parent=display,y=1,text="Reactor PLC Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=display,y=1,text="Reactor PLC Configurator",alignment=CENTER,fg_bg=style.header}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=display,x=1,y=2}
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
||||||
|
|
||||||
@@ -179,17 +147,18 @@ local function config_view(display)
|
|||||||
local clr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local clr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local summary = Div{parent=root_pane_div,x=1,y=1}
|
local summary = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
local changelog = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
local check_sys = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
|
||||||
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog,check_sys}}
|
||||||
|
|
||||||
-- Main Page
|
--#region Main Page
|
||||||
|
|
||||||
local y_start = 5
|
local y_start = 5
|
||||||
|
|
||||||
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Reactor PLC configurator! Please select one of the following options."}
|
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Reactor PLC configurator! Please select one of the following options."}
|
||||||
|
|
||||||
if tool_ctl.ask_config then
|
if tool_ctl.ask_config then
|
||||||
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
||||||
y_start = y_start + 5
|
y_start = y_start + 5
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -206,7 +175,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg}
|
||||||
|
|
||||||
local function jump_color()
|
local function jump_color()
|
||||||
tool_ctl.jumped_to_color = true
|
tool_ctl.jumped_to_color = true
|
||||||
@@ -216,7 +185,8 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
PushButton{parent=main_page,x=10,y=17,min_width=12,text="Self-Check",callback=function()main_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg}
|
||||||
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=btn_dis_fg_bg}
|
||||||
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
if not tool_ctl.has_config then
|
if not tool_ctl.has_config then
|
||||||
@@ -224,554 +194,43 @@ local function config_view(display)
|
|||||||
tool_ctl.color_cfg.disable()
|
tool_ctl.color_cfg.disable()
|
||||||
end
|
end
|
||||||
|
|
||||||
--#region PLC
|
--#endregion
|
||||||
|
|
||||||
local plc_c_1 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
--#region System Configuration
|
||||||
local plc_c_2 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
|
||||||
local plc_c_3 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
|
||||||
local plc_c_4 = Div{parent=plc_cfg,x=2,y=4,width=49}
|
|
||||||
|
|
||||||
local plc_pane = MultiPane{parent=plc_cfg,x=1,y=4,panes={plc_c_1,plc_c_2,plc_c_3,plc_c_4}}
|
local settings = { settings_cfg, ini_cfg, tmp_cfg, fields, load_settings }
|
||||||
|
local divs = { plc_cfg, net_cfg, log_cfg, clr_cfg, summary }
|
||||||
|
|
||||||
TextBox{parent=plc_cfg,x=1,y=2,height=1,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)}
|
system.create(tool_ctl, main_pane, settings, divs, style, exit)
|
||||||
|
|
||||||
TextBox{parent=plc_c_1,x=1,y=1,height=1,text="Would you like to set this PLC as networked?"}
|
|
||||||
TextBox{parent=plc_c_1,x=1,y=3,height=4,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
local networked = CheckBox{parent=plc_c_1,x=1,y=8,label="Networked",default=ini_cfg.Networked,box_fg_bg=cpair(colors.orange,colors.black)}
|
|
||||||
|
|
||||||
local function submit_networked()
|
|
||||||
tool_ctl.set_networked(networked.get_value())
|
|
||||||
plc_pane.set_value(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=plc_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=plc_c_1,x=44,y=14,text="Next \x1a",callback=submit_networked,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=plc_c_2,x=1,y=1,height=1,text="Please enter the reactor unit ID for this PLC."}
|
|
||||||
TextBox{parent=plc_c_2,x=1,y=3,height=3,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=plc_c_2,x=1,y=6,height=1,text="Unit #"}
|
|
||||||
local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_chars=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg}
|
|
||||||
|
|
||||||
local u_id_err = TextBox{parent=plc_c_2,x=8,y=14,height=1,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
||||||
|
|
||||||
local function submit_id()
|
|
||||||
local unit_id = tonumber(u_id.get_value())
|
|
||||||
if unit_id ~= nil then
|
|
||||||
u_id_err.hide(true)
|
|
||||||
tmp_cfg.UnitID = unit_id
|
|
||||||
plc_pane.set_value(3)
|
|
||||||
else u_id_err.show() end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=plc_c_2,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=plc_c_2,x=44,y=14,text="Next \x1a",callback=submit_id,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=plc_c_3,x=1,y=1,height=4,text="When networked, the supervisor takes care of emergency coolant via RTUs. However, you can configure independent emergency coolant via the PLC."}
|
|
||||||
TextBox{parent=plc_c_3,x=1,y=6,height=5,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
local en_em_cool = CheckBox{parent=plc_c_3,x=1,y=11,label="Enable PLC Emergency Coolant Control",default=ini_cfg.EmerCoolEnable,box_fg_bg=cpair(colors.orange,colors.black)}
|
|
||||||
|
|
||||||
local function next_from_plc()
|
|
||||||
if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(4) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function submit_en_emcool()
|
|
||||||
tmp_cfg.EmerCoolEnable = en_em_cool.get_value()
|
|
||||||
if tmp_cfg.EmerCoolEnable then plc_pane.set_value(4) else next_from_plc() end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=plc_c_3,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=plc_c_3,x=44,y=14,text="Next \x1a",callback=submit_en_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=plc_c_4,x=1,y=1,height=1,text="Emergency Coolant Redstone Output Side"}
|
|
||||||
local side = Radio2D{parent=plc_c_4,x=1,y=2,rows=2,columns=3,default=side_to_idx(ini_cfg.EmerCoolSide),options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.orange}
|
|
||||||
|
|
||||||
TextBox{parent=plc_c_4,x=1,y=5,height=1,text="Bundled Redstone Configuration"}
|
|
||||||
local bundled = CheckBox{parent=plc_c_4,x=1,y=6,label="Is Bundled?",default=ini_cfg.EmerCoolColor~=nil,box_fg_bg=cpair(colors.orange,colors.black),callback=function(v)tool_ctl.bundled_emcool(v)end}
|
|
||||||
local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
|
||||||
if ini_cfg.EmerCoolColor == nil then color.disable() end
|
|
||||||
|
|
||||||
local function submit_emcool()
|
|
||||||
tmp_cfg.EmerCoolSide = side_options_map[side.get_value()]
|
|
||||||
tmp_cfg.EmerCoolColor = util.trinary(bundled.get_value(), color_options_map[color.get_value()], nil)
|
|
||||||
next_from_plc()
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=plc_c_4,x=1,y=14,text="\x1b Back",callback=function()plc_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=plc_c_4,x=44,y=14,text="Next \x1a",callback=submit_emcool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Network
|
--#region Config Change Log
|
||||||
|
|
||||||
local net_c_1 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
||||||
local net_c_2 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
||||||
local net_c_3 = Div{parent=net_cfg,x=2,y=4,width=49}
|
|
||||||
|
|
||||||
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3}}
|
|
||||||
|
|
||||||
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
|
||||||
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=8,height=1,text="Supervisor Channel"}
|
|
||||||
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
||||||
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
||||||
TextBox{parent=net_c_1,x=1,y=11,height=1,text="PLC Channel"}
|
|
||||||
local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
|
||||||
TextBox{parent=net_c_1,x=9,y=12,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
||||||
|
|
||||||
local function submit_channels()
|
|
||||||
local svr_c = tonumber(svr_chan.get_value())
|
|
||||||
local plc_c = tonumber(plc_chan.get_value())
|
|
||||||
if svr_c ~= nil and plc_c ~= nil then
|
|
||||||
tmp_cfg.SVR_Channel = svr_c
|
|
||||||
tmp_cfg.PLC_Channel = plc_c
|
|
||||||
net_pane.set_value(2)
|
|
||||||
chan_err.hide(true)
|
|
||||||
elseif svr_c == nil then
|
|
||||||
chan_err.set_value("Please set the supervisor channel.")
|
|
||||||
chan_err.show()
|
|
||||||
else
|
|
||||||
chan_err.set_value("Please set the PLC channel.")
|
|
||||||
chan_err.show()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Connection Timeout"}
|
|
||||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
||||||
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
|
||||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=8,height=1,text="Trusted Range"}
|
|
||||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
|
||||||
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
local p2_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
||||||
|
|
||||||
local function submit_ct_tr()
|
|
||||||
local timeout_val = tonumber(timeout.get_value())
|
|
||||||
local range_val = tonumber(range.get_value())
|
|
||||||
if timeout_val ~= nil and range_val ~= nil then
|
|
||||||
tmp_cfg.ConnTimeout = timeout_val
|
|
||||||
tmp_cfg.TrustedRange = range_val
|
|
||||||
net_pane.set_value(3)
|
|
||||||
p2_err.hide(true)
|
|
||||||
elseif timeout_val == nil then
|
|
||||||
p2_err.set_value("Please set the connection timeout.")
|
|
||||||
p2_err.show()
|
|
||||||
else
|
|
||||||
p2_err.set_value("Please set the trusted range.")
|
|
||||||
p2_err.show()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_ct_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
|
||||||
TextBox{parent=net_c_3,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=11,height=1,text="Facility Auth Key"}
|
|
||||||
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
|
||||||
|
|
||||||
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
|
||||||
|
|
||||||
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
|
||||||
|
|
||||||
hide_key.set_value(true)
|
|
||||||
censor_key(true)
|
|
||||||
|
|
||||||
local key_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
||||||
|
|
||||||
local function submit_auth()
|
|
||||||
local v = key.get_value()
|
|
||||||
if string.len(v) == 0 or string.len(v) >= 8 then
|
|
||||||
tmp_cfg.AuthKey = key.get_value()
|
|
||||||
main_pane.set_value(4)
|
|
||||||
key_err.hide(true)
|
|
||||||
else key_err.show() end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=net_c_3,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=net_c_3,x=44,y=14,text="Next \x1a",callback=submit_auth,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region Logging
|
|
||||||
|
|
||||||
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
|
||||||
|
|
||||||
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
|
||||||
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
|
||||||
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
|
||||||
|
|
||||||
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
|
||||||
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
|
||||||
|
|
||||||
local function submit_log()
|
|
||||||
if path.get_value() ~= "" then
|
|
||||||
path_err.hide(true)
|
|
||||||
tmp_cfg.LogMode = mode.get_value() - 1
|
|
||||||
tmp_cfg.LogPath = path.get_value()
|
|
||||||
tmp_cfg.LogDebug = en_dbg.get_value()
|
|
||||||
tool_ctl.color_apply.hide(true)
|
|
||||||
tool_ctl.color_next.show()
|
|
||||||
main_pane.set_value(5)
|
|
||||||
else path_err.show() end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function back_from_log()
|
|
||||||
if tmp_cfg.Networked then main_pane.set_value(3) else main_pane.set_value(2) end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=log_c_1,x=1,y=14,text="\x1b Back",callback=back_from_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=log_c_1,x=44,y=14,text="Next \x1a",callback=submit_log,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region Color Options
|
|
||||||
|
|
||||||
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
||||||
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
||||||
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
||||||
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
|
||||||
|
|
||||||
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
|
||||||
|
|
||||||
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
|
||||||
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
|
||||||
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
|
||||||
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
|
||||||
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
|
||||||
|
|
||||||
local function recolor(value)
|
|
||||||
local c = themes.smooth_stone.color_modes[value]
|
|
||||||
|
|
||||||
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
|
||||||
b_off.hide()
|
|
||||||
g_off.show()
|
|
||||||
else
|
|
||||||
g_off.hide()
|
|
||||||
b_off.show()
|
|
||||||
end
|
|
||||||
|
|
||||||
if #c == 0 then
|
|
||||||
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
|
||||||
else
|
|
||||||
term.setPaletteColor(colors.green, c[1].hex)
|
|
||||||
term.setPaletteColor(colors.yellow, c[2].hex)
|
|
||||||
term.setPaletteColor(colors.red, c[3].hex)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
|
||||||
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
|
||||||
|
|
||||||
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
local function back_from_colors()
|
|
||||||
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4))
|
|
||||||
tool_ctl.jumped_to_color = false
|
|
||||||
recolor(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function show_access()
|
|
||||||
clr_pane.set_value(2)
|
|
||||||
recolor(c_mode.get_value())
|
|
||||||
end
|
|
||||||
|
|
||||||
local function submit_colors()
|
|
||||||
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
|
||||||
tmp_cfg.ColorMode = c_mode.get_value()
|
|
||||||
|
|
||||||
if tool_ctl.jumped_to_color then
|
|
||||||
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
|
||||||
settings.set("ColorMode", tmp_cfg.ColorMode)
|
|
||||||
|
|
||||||
if settings.save("/reactor-plc.settings") then
|
|
||||||
load_settings(settings_cfg, true)
|
|
||||||
load_settings(ini_cfg)
|
|
||||||
clr_pane.set_value(3)
|
|
||||||
else
|
|
||||||
clr_pane.set_value(4)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
|
||||||
tool_ctl.viewing_config = false
|
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
main_pane.set_value(6)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
tool_ctl.color_apply.hide(true)
|
|
||||||
|
|
||||||
local function c_go_home()
|
|
||||||
main_pane.set_value(1)
|
|
||||||
clr_pane.set_value(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
|
||||||
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
||||||
PushButton{parent=clr_c_4,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
PushButton{parent=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region Summary and Saving
|
|
||||||
|
|
||||||
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
|
||||||
local sum_c_2 = Div{parent=summary,x=2,y=4,width=49}
|
|
||||||
local sum_c_3 = Div{parent=summary,x=2,y=4,width=49}
|
|
||||||
local sum_c_4 = Div{parent=summary,x=2,y=4,width=49}
|
|
||||||
|
|
||||||
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
|
||||||
|
|
||||||
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
|
||||||
|
|
||||||
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
|
||||||
|
|
||||||
local function back_from_settings()
|
|
||||||
if tool_ctl.viewing_config or tool_ctl.importing_legacy then
|
|
||||||
main_pane.set_value(1)
|
|
||||||
tool_ctl.viewing_config = false
|
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
else
|
|
||||||
main_pane.set_value(5)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param element graphics_element
|
|
||||||
---@param data any
|
|
||||||
local function try_set(element, data)
|
|
||||||
if data ~= nil then element.set_value(data) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function save_and_continue()
|
|
||||||
for k, v in pairs(tmp_cfg) do settings.set(k, v) end
|
|
||||||
|
|
||||||
if settings.save("/reactor-plc.settings") then
|
|
||||||
load_settings(settings_cfg, true)
|
|
||||||
load_settings(ini_cfg)
|
|
||||||
|
|
||||||
try_set(networked, ini_cfg.Networked)
|
|
||||||
try_set(u_id, ini_cfg.UnitID)
|
|
||||||
try_set(en_em_cool, ini_cfg.EmerCoolEnable)
|
|
||||||
try_set(side, side_to_idx(ini_cfg.EmerCoolSide))
|
|
||||||
try_set(bundled, ini_cfg.EmerCoolColor ~= nil)
|
|
||||||
if ini_cfg.EmerCoolColor ~= nil then try_set(color, color_to_idx(ini_cfg.EmerCoolColor)) end
|
|
||||||
try_set(svr_chan, ini_cfg.SVR_Channel)
|
|
||||||
try_set(plc_chan, ini_cfg.PLC_Channel)
|
|
||||||
try_set(timeout, ini_cfg.ConnTimeout)
|
|
||||||
try_set(range, ini_cfg.TrustedRange)
|
|
||||||
try_set(key, ini_cfg.AuthKey)
|
|
||||||
try_set(mode, ini_cfg.LogMode)
|
|
||||||
try_set(path, ini_cfg.LogPath)
|
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
|
||||||
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
|
||||||
try_set(c_mode, ini_cfg.ColorMode)
|
|
||||||
|
|
||||||
tool_ctl.view_cfg.enable()
|
|
||||||
|
|
||||||
if tool_ctl.importing_legacy then
|
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
sum_pane.set_value(3)
|
|
||||||
else
|
|
||||||
sum_pane.set_value(2)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sum_pane.set_value(4)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=sum_c_1,x=1,y=14,text="\x1b Back",callback=back_from_settings,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
|
||||||
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
|
||||||
|
|
||||||
local function go_home()
|
|
||||||
main_pane.set_value(1)
|
|
||||||
plc_pane.set_value(1)
|
|
||||||
net_pane.set_value(1)
|
|
||||||
clr_pane.set_value(1)
|
|
||||||
sum_pane.set_value(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=sum_c_2,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=sum_c_2,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
|
|
||||||
TextBox{parent=sum_c_3,x=1,y=1,height=2,text="The old config.lua file will now be deleted, then the configurator will exit."}
|
|
||||||
|
|
||||||
local function delete_legacy()
|
|
||||||
fs.delete("/reactor-plc/config.lua")
|
|
||||||
exit()
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=sum_c_3,x=1,y=14,min_width=8,text="Cancel",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=sum_c_3,x=44,y=14,min_width=6,text="OK",callback=delete_legacy,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
|
|
||||||
TextBox{parent=sum_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
|
||||||
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
|
||||||
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
-- Config Change Log
|
|
||||||
|
|
||||||
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
TextBox{parent=changelog,x=1,y=2,text=" Config Change Log",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=c_log,text=change[1],fg_bg=bw_fg_bg}
|
||||||
for _, v in ipairs(change[2]) do
|
for _, v in ipairs(change[2]) do
|
||||||
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
||||||
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=1,text="- ",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=cl,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
-- set tool functions now that we have the elements
|
--#endregion
|
||||||
|
|
||||||
function tool_ctl.set_networked(enable)
|
--#region Self-Check
|
||||||
tmp_cfg.Networked = enable
|
|
||||||
if enable then u_id.set_max(4) else u_id.set_max(999) end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tool_ctl.bundled_emcool(en) if en then color.enable() else color.disable() end end
|
check.create(main_pane, settings_cfg, check_sys, style)
|
||||||
|
|
||||||
-- load a legacy config file
|
--#endregion
|
||||||
function tool_ctl.load_legacy()
|
|
||||||
local config = require("reactor-plc.config")
|
|
||||||
|
|
||||||
tmp_cfg.Networked = config.NETWORKED
|
|
||||||
tmp_cfg.UnitID = config.REACTOR_ID
|
|
||||||
tmp_cfg.EmerCoolEnable = type(config.EMERGENCY_COOL) == "table"
|
|
||||||
|
|
||||||
if tmp_cfg.EmerCoolEnable then
|
|
||||||
tmp_cfg.EmerCoolSide = config.EMERGENCY_COOL.side
|
|
||||||
tmp_cfg.EmerCoolColor = config.EMERGENCY_COOL.color
|
|
||||||
else
|
|
||||||
tmp_cfg.EmerCoolSide = nil
|
|
||||||
tmp_cfg.EmerCoolColor = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
|
||||||
tmp_cfg.PLC_Channel = config.PLC_CHANNEL
|
|
||||||
tmp_cfg.ConnTimeout = config.COMMS_TIMEOUT
|
|
||||||
tmp_cfg.TrustedRange = config.TRUSTED_RANGE
|
|
||||||
tmp_cfg.AuthKey = config.AUTH_KEY or ""
|
|
||||||
tmp_cfg.LogMode = config.LOG_MODE
|
|
||||||
tmp_cfg.LogPath = config.LOG_PATH
|
|
||||||
tmp_cfg.LogDebug = config.LOG_DEBUG or false
|
|
||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
|
||||||
sum_pane.set_value(1)
|
|
||||||
main_pane.set_value(6)
|
|
||||||
tool_ctl.importing_legacy = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- expose the auth key on the summary page
|
|
||||||
function tool_ctl.show_auth_key()
|
|
||||||
tool_ctl.show_key_btn.disable()
|
|
||||||
tool_ctl.auth_key_textbox.set_value(tool_ctl.auth_key_value)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- generate the summary list
|
|
||||||
---@param cfg plc_config
|
|
||||||
function tool_ctl.gen_summary(cfg)
|
|
||||||
setting_list.remove_all()
|
|
||||||
|
|
||||||
local alternate = false
|
|
||||||
local inner_width = setting_list.get_width() - 1
|
|
||||||
|
|
||||||
if cfg.AuthKey then tool_ctl.show_key_btn.enable() else tool_ctl.show_key_btn.disable() end
|
|
||||||
tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key
|
|
||||||
|
|
||||||
for i = 1, #fields do
|
|
||||||
local f = fields[i]
|
|
||||||
local height = 1
|
|
||||||
local label_w = string.len(f[2])
|
|
||||||
local val_max_w = (inner_width - label_w) + 1
|
|
||||||
local raw = cfg[f[1]]
|
|
||||||
local val = util.strval(raw)
|
|
||||||
|
|
||||||
if f[1] == "AuthKey" and raw then val = string.rep("*", string.len(val))
|
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
|
||||||
elseif f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw)
|
|
||||||
elseif f[1] == "FrontPanelTheme" then
|
|
||||||
val = util.strval(themes.fp_theme_name(raw))
|
|
||||||
elseif f[1] == "ColorMode" then
|
|
||||||
val = util.strval(themes.color_mode_name(raw))
|
|
||||||
end
|
|
||||||
|
|
||||||
if val == "nil" then val = "<not set>" end
|
|
||||||
|
|
||||||
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
|
||||||
alternate = not alternate
|
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
|
||||||
local lines = util.strwrap(val, inner_width)
|
|
||||||
height = #lines + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local line = Div{parent=setting_list,height=height,fg_bg=c}
|
|
||||||
TextBox{parent=line,text=f[2],width=string.len(f[2]),fg_bg=cpair(colors.black,line.get_fg_bg().bkg)}
|
|
||||||
|
|
||||||
local textbox
|
|
||||||
if height > 1 then
|
|
||||||
textbox = TextBox{parent=line,x=1,y=2,text=val,height=height-1,alignment=LEFT}
|
|
||||||
else
|
|
||||||
textbox = TextBox{parent=line,x=label_w+1,y=1,text=val,alignment=RIGHT}
|
|
||||||
end
|
|
||||||
|
|
||||||
if f[1] == "AuthKey" then tool_ctl.auth_key_textbox = textbox end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset terminal screen
|
-- reset terminal screen
|
||||||
@@ -802,7 +261,7 @@ function configurator.configure(ask_config)
|
|||||||
config_view(display)
|
config_view(display)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local event, param1, param2, param3 = util.pull_event()
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
-- handle event
|
-- handle event
|
||||||
if event == "timer" then
|
if event == "timer" then
|
||||||
@@ -815,6 +274,8 @@ function configurator.configure(ask_config)
|
|||||||
if k_e then display.handle_key(k_e) end
|
if k_e then display.handle_key(k_e) end
|
||||||
elseif event == "paste" then
|
elseif event == "paste" then
|
||||||
display.handle_paste(param1)
|
display.handle_paste(param1)
|
||||||
|
elseif event == "modem_message" then
|
||||||
|
check.receive_sv(param1, param2, param3, param4, param5)
|
||||||
end
|
end
|
||||||
|
|
||||||
if event == "terminate" then return end
|
if event == "terminate" then return end
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ local function init(panel)
|
|||||||
|
|
||||||
local disabled_fg = style.fp.disabled_fg
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
local header = TextBox{parent=panel,y=1,text="FISSION REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
local header = TextBox{parent=panel,y=1,text="FISSION REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("FISSION REACTOR PLC - UNIT ", id)) end)
|
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("FISSION REACTOR PLC - UNIT ", id)) end)
|
||||||
|
|
||||||
--
|
--
|
||||||
@@ -115,7 +115,7 @@ local function init(panel)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=5,width=6,height=1,text=comp_id,fg_bg=disabled_fg}
|
TextBox{parent=system,x=9,y=5,width=6,text=comp_id,fg_bg=disabled_fg}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- status & controls
|
-- status & controls
|
||||||
@@ -148,8 +148,8 @@ local function init(panel)
|
|||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
|
|||||||
@@ -57,41 +57,47 @@ function plc.load_config()
|
|||||||
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
||||||
config.ColorMode = settings.get("ColorMode")
|
config.ColorMode = settings.get("ColorMode")
|
||||||
|
|
||||||
|
return plc.validate_config(config)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- validate a PLC configuration
|
||||||
|
---@param cfg plc_config
|
||||||
|
function plc.validate_config(cfg)
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
cfv.assert_type_bool(config.Networked)
|
cfv.assert_type_bool(cfg.Networked)
|
||||||
cfv.assert_type_int(config.UnitID)
|
cfv.assert_type_int(cfg.UnitID)
|
||||||
cfv.assert_type_bool(config.EmerCoolEnable)
|
cfv.assert_type_bool(cfg.EmerCoolEnable)
|
||||||
|
|
||||||
if config.Networked == true then
|
if cfg.Networked == true then
|
||||||
cfv.assert_channel(config.SVR_Channel)
|
cfv.assert_channel(cfg.SVR_Channel)
|
||||||
cfv.assert_channel(config.PLC_Channel)
|
cfv.assert_channel(cfg.PLC_Channel)
|
||||||
cfv.assert_type_num(config.ConnTimeout)
|
cfv.assert_type_num(cfg.ConnTimeout)
|
||||||
cfv.assert_min(config.ConnTimeout, 2)
|
cfv.assert_min(cfg.ConnTimeout, 2)
|
||||||
cfv.assert_type_num(config.TrustedRange)
|
cfv.assert_type_num(cfg.TrustedRange)
|
||||||
cfv.assert_min(config.TrustedRange, 0)
|
cfv.assert_min(cfg.TrustedRange, 0)
|
||||||
cfv.assert_type_str(config.AuthKey)
|
cfv.assert_type_str(cfg.AuthKey)
|
||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(cfg.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(cfg.AuthKey)
|
||||||
cfv.assert(len == 0 or len >= 8)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(cfg.LogMode)
|
||||||
cfv.assert_range(config.LogMode, 0, 1)
|
cfv.assert_range(cfg.LogMode, 0, 1)
|
||||||
cfv.assert_type_str(config.LogPath)
|
cfv.assert_type_str(cfg.LogPath)
|
||||||
cfv.assert_type_bool(config.LogDebug)
|
cfv.assert_type_bool(cfg.LogDebug)
|
||||||
|
|
||||||
cfv.assert_type_int(config.FrontPanelTheme)
|
cfv.assert_type_int(cfg.FrontPanelTheme)
|
||||||
cfv.assert_range(config.FrontPanelTheme, 1, 2)
|
cfv.assert_range(cfg.FrontPanelTheme, 1, 2)
|
||||||
cfv.assert_type_int(config.ColorMode)
|
cfv.assert_type_int(cfg.ColorMode)
|
||||||
cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
cfv.assert_range(cfg.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
||||||
|
|
||||||
-- check emergency coolant configuration if enabled
|
-- check emergency coolant configuration if enabled
|
||||||
if config.EmerCoolEnable then
|
if cfg.EmerCoolEnable then
|
||||||
cfv.assert_eq(rsio.is_valid_side(config.EmerCoolSide), true)
|
cfv.assert_eq(rsio.is_valid_side(cfg.EmerCoolSide), true)
|
||||||
cfv.assert_eq(config.EmerCoolColor == nil or rsio.is_color(config.EmerCoolColor), true)
|
cfv.assert_eq(cfg.EmerCoolColor == nil or rsio.is_color(cfg.EmerCoolColor), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
return cfv.valid()
|
return cfv.valid()
|
||||||
@@ -524,8 +530,8 @@ end
|
|||||||
function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||||
local self = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil,
|
r_seq_num = nil, ---@type nil|integer
|
||||||
scrammed = false,
|
scrammed = false,
|
||||||
linked = false,
|
linked = false,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||||
@@ -571,33 +577,17 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
self.seq_num = self.seq_num + 1
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- variable reactor status information, excluding heating rate
|
-- dynamic reactor status information, excluding heating rate
|
||||||
---@return table data_table, boolean faulted
|
---@return table data_table, boolean faulted
|
||||||
local function _reactor_status()
|
local function _get_reactor_status()
|
||||||
local fuel = nil
|
local fuel = nil
|
||||||
local waste = nil
|
local waste = nil
|
||||||
local coolant = nil
|
local coolant = nil
|
||||||
local hcoolant = nil
|
local hcoolant = nil
|
||||||
|
|
||||||
local data_table = {
|
local data_table = {}
|
||||||
false, -- getStatus
|
|
||||||
0, -- getBurnRate
|
reactor.__p_disable_afc()
|
||||||
0, -- getActualBurnRate
|
|
||||||
0, -- getTemperature
|
|
||||||
0, -- getDamagePercent
|
|
||||||
0, -- getBoilEfficiency
|
|
||||||
0, -- getEnvironmentalLoss
|
|
||||||
0, -- fuel_amnt
|
|
||||||
0, -- getFuelFilledPercentage
|
|
||||||
0, -- waste_amnt
|
|
||||||
0, -- getWasteFilledPercentage
|
|
||||||
"", -- coolant_name
|
|
||||||
0, -- coolant_amnt
|
|
||||||
0, -- getCoolantFilledPercentage
|
|
||||||
"", -- hcoolant_name
|
|
||||||
0, -- hcoolant_amnt
|
|
||||||
0 -- getHeatedCoolantFilledPercentage
|
|
||||||
}
|
|
||||||
|
|
||||||
local tasks = {
|
local tasks = {
|
||||||
function () data_table[1] = reactor.getStatus() end,
|
function () data_table[1] = reactor.getStatus() end,
|
||||||
@@ -637,30 +627,32 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
data_table[16] = hcoolant.amount
|
data_table[16] = hcoolant.amount
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reactor.__p_enable_afc()
|
||||||
|
|
||||||
return data_table, reactor.__p_is_faulted()
|
return data_table, reactor.__p_is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update the status cache if changed
|
-- update the status cache if changed
|
||||||
---@return boolean changed
|
---@return boolean changed
|
||||||
local function _update_status_cache()
|
local function _update_status_cache()
|
||||||
local status, faulted = _reactor_status()
|
local status, faulted = _get_reactor_status()
|
||||||
local changed = false
|
local changed = false
|
||||||
|
|
||||||
if self.status_cache ~= nil then
|
if not faulted then
|
||||||
if not faulted then
|
if self.status_cache ~= nil then
|
||||||
for i = 1, #status do
|
for i = 1, #status do
|
||||||
if status[i] ~= self.status_cache[i] then
|
if status[i] ~= self.status_cache[i] then
|
||||||
changed = true
|
changed = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
changed = true
|
||||||
end
|
end
|
||||||
else
|
|
||||||
changed = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if changed and not faulted then
|
if changed then
|
||||||
self.status_cache = status
|
self.status_cache = status
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
@@ -679,9 +671,11 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
_send(msg_type, { status })
|
_send(msg_type, { status })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- send structure properties (these should not change, server will cache these)
|
-- send static structure properties, cached by server
|
||||||
local function _send_struct()
|
local function _send_struct()
|
||||||
local mek_data = { false, 0, 0, 0, types.new_zero_coordinate(), types.new_zero_coordinate(), 0, 0, 0, 0, 0, 0, 0, 0 }
|
local mek_data = {}
|
||||||
|
|
||||||
|
reactor.__p_disable_afc()
|
||||||
|
|
||||||
local tasks = {
|
local tasks = {
|
||||||
function () mek_data[1] = reactor.getLength() end,
|
function () mek_data[1] = reactor.getLength() end,
|
||||||
@@ -705,6 +699,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
||||||
self.resend_build = false
|
self.resend_build = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reactor.__p_enable_afc()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
@@ -835,8 +831,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
if l_chan == config.PLC_Channel then
|
if l_chan == config.PLC_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.linked and (src_addr ~= self.sv_addr) then
|
elseif self.linked and (src_addr ~= self.sv_addr) then
|
||||||
@@ -844,7 +840,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed the watchdog first so it doesn't uhh...eat our packets :)
|
-- feed the watchdog first so it doesn't uhh...eat our packets :)
|
||||||
@@ -1030,10 +1026,9 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
println_ts("linked!")
|
println_ts("linked!")
|
||||||
log.info("supervisor establish request approved, linked to SV (CID#" .. src_addr .. ")")
|
log.info("supervisor establish request approved, linked to SV (CID#" .. src_addr .. ")")
|
||||||
|
|
||||||
-- link + reset remote sequence number and cache
|
-- link + reset cache
|
||||||
self.sv_addr = src_addr
|
self.sv_addr = src_addr
|
||||||
self.linked = true
|
self.linked = true
|
||||||
self.r_seq_num = nil
|
|
||||||
self.status_cache = nil
|
self.status_cache = nil
|
||||||
|
|
||||||
if plc_state.reactor_formed then _send_struct() end
|
if plc_state.reactor_formed then _send_struct() end
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
|
|||||||
local renderer = require("reactor-plc.renderer")
|
local renderer = require("reactor-plc.renderer")
|
||||||
local threads = require("reactor-plc.threads")
|
local threads = require("reactor-plc.threads")
|
||||||
|
|
||||||
local R_PLC_VERSION = "v1.7.11"
|
local R_PLC_VERSION = "v1.8.2"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
|||||||
@@ -84,7 +84,8 @@ assert(#PORT_DSGN == rsio.NUM_PORTS)
|
|||||||
local changes = {
|
local changes = {
|
||||||
{ "v1.7.9", { "ConnTimeout can now have a fractional part" } },
|
{ "v1.7.9", { "ConnTimeout can now have a fractional part" } },
|
||||||
{ "v1.7.15", { "Added front panel UI theme", "Added color accessibility modes" } },
|
{ "v1.7.15", { "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
{ "v1.9.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
{ "v1.9.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } },
|
||||||
|
{ "v1.10.2", { "Re-organized peripheral configuration UI, resulting in some input fields being re-ordered" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class rtu_rs_definition
|
---@class rtu_rs_definition
|
||||||
@@ -158,7 +159,6 @@ local tool_ctl = {
|
|||||||
p_idx = nil, ---@type graphics_element
|
p_idx = nil, ---@type graphics_element
|
||||||
p_unit = nil, ---@type graphics_element
|
p_unit = nil, ---@type graphics_element
|
||||||
p_assign_btn = nil, ---@type graphics_element
|
p_assign_btn = nil, ---@type graphics_element
|
||||||
p_assign_end = nil, ---@type graphics_element
|
|
||||||
p_desc = nil, ---@type graphics_element
|
p_desc = nil, ---@type graphics_element
|
||||||
p_desc_ext = nil, ---@type graphics_element
|
p_desc_ext = nil, ---@type graphics_element
|
||||||
p_err = nil, ---@type graphics_element
|
p_err = nil, ---@type graphics_element
|
||||||
@@ -264,7 +264,7 @@ local function config_view(display)
|
|||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local function exit() os.queueEvent("terminate") end
|
local function exit() os.queueEvent("terminate") end
|
||||||
|
|
||||||
TextBox{parent=display,y=1,text="RTU Gateway Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=display,y=1,text="RTU Gateway Configurator",alignment=CENTER,fg_bg=style.header}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=display,x=1,y=2}
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ local function config_view(display)
|
|||||||
local y_start = 2
|
local y_start = 2
|
||||||
|
|
||||||
if tool_ctl.ask_config then
|
if tool_ctl.ask_config then
|
||||||
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
||||||
y_start = y_start + 5
|
y_start = y_start + 5
|
||||||
else
|
else
|
||||||
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the RTU gateway configurator! Please select one of the following options."}
|
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the RTU gateway configurator! Please select one of the following options."}
|
||||||
@@ -344,7 +344,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49}
|
local spkr_c = Div{parent=spkr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=spkr_cfg,x=1,y=2,height=1,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)}
|
TextBox{parent=spkr_cfg,x=1,y=2,text=" Speaker Configuration",fg_bg=cpair(colors.black,colors.cyan)}
|
||||||
|
|
||||||
TextBox{parent=spkr_c,x=1,y=1,height=2,text="Speakers can be connected to this RTU gateway without RTU unit configuration entries."}
|
TextBox{parent=spkr_c,x=1,y=1,height=2,text="Speakers can be connected to this RTU gateway without RTU unit configuration entries."}
|
||||||
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
||||||
@@ -353,7 +353,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,height=1,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local s_vol_err = TextBox{parent=spkr_c,x=8,y=14,width=35,text="Please set a volume.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_vol()
|
local function submit_vol()
|
||||||
local vol = tonumber(s_vol.get_value())
|
local vol = tonumber(s_vol.get_value())
|
||||||
@@ -377,19 +377,19 @@ local function config_view(display)
|
|||||||
|
|
||||||
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3}}
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3}}
|
||||||
|
|
||||||
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
TextBox{parent=net_c_1,x=1,y=1,text="Please set the network channels below."}
|
||||||
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=8,height=1,text="Supervisor Channel"}
|
TextBox{parent=net_c_1,x=1,y=8,text="Supervisor Channel"}
|
||||||
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=9,y=9,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=1,y=11,height=1,text="RTU Channel"}
|
TextBox{parent=net_c_1,x=1,y=11,text="RTU Channel"}
|
||||||
local rtu_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.RTU_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local rtu_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.RTU_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=9,y=12,height=4,text="[RTU_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=9,y=12,height=4,text="[RTU_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_channels()
|
local function submit_channels()
|
||||||
local svr_c = tonumber(svr_chan.get_value())
|
local svr_c = tonumber(svr_chan.get_value())
|
||||||
@@ -411,16 +411,16 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Connection Timeout"}
|
TextBox{parent=net_c_2,x=1,y=1,text="Connection Timeout"}
|
||||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=8,height=1,text="Trusted Range"}
|
TextBox{parent=net_c_2,x=1,y=8,text="Trusted Range"}
|
||||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local p2_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local p2_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_ct_tr()
|
local function submit_ct_tr()
|
||||||
local timeout_val = tonumber(timeout.get_value())
|
local timeout_val = tonumber(timeout.get_value())
|
||||||
@@ -445,7 +445,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_3,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
TextBox{parent=net_c_3,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
||||||
TextBox{parent=net_c_3,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=11,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_3,x=1,y=11,text="Facility Auth Key"}
|
||||||
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function censor_key(enable) censor(tri(enable, "*", nil)) end
|
local function censor_key(enable) censor(tri(enable, "*", nil)) end
|
||||||
@@ -455,7 +455,7 @@ local function config_view(display)
|
|||||||
hide_key.set_value(true)
|
hide_key.set_value(true)
|
||||||
censor_key(true)
|
censor_key(true)
|
||||||
|
|
||||||
local key_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local key_err = TextBox{parent=net_c_3,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_auth()
|
local function submit_auth()
|
||||||
local v = key.get_value()
|
local v = key.get_value()
|
||||||
@@ -475,20 +475,20 @@ local function config_view(display)
|
|||||||
|
|
||||||
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
TextBox{parent=log_c_1,x=1,y=3,text="Log File Mode"}
|
||||||
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
TextBox{parent=log_c_1,x=1,y=7,text="Log File Path"}
|
||||||
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
||||||
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_log()
|
local function submit_log()
|
||||||
if path.get_value() ~= "" then
|
if path.get_value() ~= "" then
|
||||||
@@ -516,17 +516,17 @@ local function config_view(display)
|
|||||||
|
|
||||||
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
TextBox{parent=clr_c_1,x=1,y=7,text="Front Panel Theme"}
|
||||||
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
TextBox{parent=clr_c_2,x=21,y=7,text="Preview"}
|
||||||
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
@@ -553,7 +553,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"}
|
||||||
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
@@ -603,7 +603,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.color_apply.hide(true)
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"}
|
||||||
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -625,7 +625,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4,sum_c_5,sum_c_6,sum_c_7}}
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4,sum_c_5,sum_c_6,sum_c_7}}
|
||||||
|
|
||||||
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
TextBox{parent=summary,x=1,y=2,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
||||||
|
|
||||||
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -698,13 +698,13 @@ local function config_view(display)
|
|||||||
tool_ctl.settings_confirm = PushButton{parent=sum_c_1,x=41,y=14,min_width=9,text="Confirm",callback=function()sum_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
tool_ctl.settings_confirm = PushButton{parent=sum_c_1,x=41,y=14,min_width=9,text="Confirm",callback=function()sum_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.settings_confirm.hide()
|
tool_ctl.settings_confirm.hide()
|
||||||
|
|
||||||
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="The following peripherals will be imported:"}
|
TextBox{parent=sum_c_2,x=1,y=1,text="The following peripherals will be imported:"}
|
||||||
local peri_import_list = ListBox{parent=sum_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local peri_import_list = ListBox{parent=sum_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
PushButton{parent=sum_c_2,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_2,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=sum_c_2,x=41,y=14,min_width=9,text="Confirm",callback=function()sum_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_2,x=41,y=14,min_width=9,text="Confirm",callback=function()sum_pane.set_value(3)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=sum_c_3,x=1,y=1,height=1,text="The following redstone entries will be imported:"}
|
TextBox{parent=sum_c_3,x=1,y=1,text="The following redstone entries will be imported:"}
|
||||||
local rs_import_list = ListBox{parent=sum_c_3,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local rs_import_list = ListBox{parent=sum_c_3,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
PushButton{parent=sum_c_3,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_3,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
@@ -720,7 +720,7 @@ local function config_view(display)
|
|||||||
show_rs_conns()
|
show_rs_conns()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=sum_c_4,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=sum_c_4,x=1,y=1,text="Settings saved!"}
|
||||||
TextBox{parent=sum_c_4,x=1,y=3,height=4,text="Remember to configure any peripherals or redstone that you have connected to this RTU gateway if you have not already done so, or if you have added, removed, or modified any of them."}
|
TextBox{parent=sum_c_4,x=1,y=3,height=4,text="Remember to configure any peripherals or redstone that you have connected to this RTU gateway if you have not already done so, or if you have added, removed, or modified any of them."}
|
||||||
PushButton{parent=sum_c_4,x=1,y=8,min_width=24,text="Peripheral Connections",callback=jump_peri_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_4,x=1,y=8,min_width=24,text="Peripheral Connections",callback=jump_peri_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=sum_c_4,x=1,y=10,min_width=22,text="Redstone Connections",callback=jump_rs_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_4,x=1,y=10,min_width=22,text="Redstone Connections",callback=jump_rs_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
||||||
@@ -752,15 +752,15 @@ local function config_view(display)
|
|||||||
|
|
||||||
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
TextBox{parent=changelog,x=1,y=2,text=" Config Change Log",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=c_log,text=change[1],fg_bg=bw_fg_bg}
|
||||||
for _, v in ipairs(change[2]) do
|
for _, v in ipairs(change[2]) do
|
||||||
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
||||||
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=1,text="- ",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -781,7 +781,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local peri_pane = MultiPane{parent=peri_cfg,x=1,y=4,panes={peri_c_1,peri_c_2,peri_c_3,peri_c_4,peri_c_5,peri_c_6,peri_c_7}}
|
local peri_pane = MultiPane{parent=peri_cfg,x=1,y=4,panes={peri_c_1,peri_c_2,peri_c_3,peri_c_4,peri_c_5,peri_c_6,peri_c_7}}
|
||||||
|
|
||||||
TextBox{parent=peri_cfg,x=1,y=2,height=1,text=" Peripheral Connections",fg_bg=cpair(colors.black,colors.purple)}
|
TextBox{parent=peri_cfg,x=1,y=2,text=" Peripheral Connections",fg_bg=cpair(colors.black,colors.purple)}
|
||||||
|
|
||||||
local peri_list = ListBox{parent=peri_c_1,x=1,y=1,height=12,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local peri_list = ListBox{parent=peri_c_1,x=1,y=1,height=12,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -807,7 +807,7 @@ local function config_view(display)
|
|||||||
PushButton{parent=peri_c_1,x=35,y=14,min_width=7,text="Add +",callback=function()peri_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_1,x=35,y=14,min_width=7,text="Add +",callback=function()peri_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=peri_c_1,x=43,y=14,min_width=7,text="Apply",callback=peri_apply,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_1,x=43,y=14,min_width=7,text="Apply",callback=peri_apply,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=peri_c_2,x=1,y=1,height=1,text="Select one of the below devices to use."}
|
TextBox{parent=peri_c_2,x=1,y=1,text="Select one of the below devices to use."}
|
||||||
|
|
||||||
tool_ctl.ppm_devs = ListBox{parent=peri_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
tool_ctl.ppm_devs = ListBox{parent=peri_c_2,x=1,y=3,height=10,width=49,scroll_height=1000,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -828,53 +828,35 @@ local function config_view(display)
|
|||||||
tool_ctl.p_name_msg.set_value("Configuring peripheral on '" .. name .. "':")
|
tool_ctl.p_name_msg.set_value("Configuring peripheral on '" .. name .. "':")
|
||||||
tool_ctl.p_desc_ext.set_value("")
|
tool_ctl.p_desc_ext.set_value("")
|
||||||
|
|
||||||
|
local function reposition(prompt, idx_x, idx_max, unit_x, unit_y, desc_y)
|
||||||
|
tool_ctl.p_prompt.set_value(prompt)
|
||||||
|
tool_ctl.p_idx.reposition(idx_x, 4)
|
||||||
|
tool_ctl.p_idx.enable()
|
||||||
|
tool_ctl.p_idx.set_max(idx_max)
|
||||||
|
tool_ctl.p_idx.show()
|
||||||
|
tool_ctl.p_unit.reposition(unit_x, unit_y)
|
||||||
|
tool_ctl.p_unit.enable()
|
||||||
|
tool_ctl.p_unit.show()
|
||||||
|
tool_ctl.p_desc.reposition(1, desc_y)
|
||||||
|
end
|
||||||
|
|
||||||
if type == "boilerValve" then
|
if type == "boilerValve" then
|
||||||
tool_ctl.p_prompt.set_value("This is the # boiler for reactor unit # .")
|
reposition("This is reactor unit # 's # boiler.", 31, 2, 23, 4, 7)
|
||||||
tool_ctl.p_idx.show()
|
|
||||||
tool_ctl.p_idx.redraw()
|
|
||||||
tool_ctl.p_idx.enable()
|
|
||||||
tool_ctl.p_idx.set_max(2)
|
|
||||||
tool_ctl.p_unit.reposition(44, 4)
|
|
||||||
tool_ctl.p_unit.enable()
|
|
||||||
tool_ctl.p_unit.show()
|
|
||||||
tool_ctl.p_assign_btn.hide(true)
|
tool_ctl.p_assign_btn.hide(true)
|
||||||
tool_ctl.p_assign_end.hide(true)
|
tool_ctl.p_desc.set_value("Each unit can have at most 2 boilers. Boiler #1 shows up first on the main display, followed by boiler #2 below it. The numberings are per unit (unit 1 and unit 2 would both have a boiler #1 if each had one boiler) and can be split amongst multiple RTUs (one has #1, another has #2).")
|
||||||
tool_ctl.p_desc.reposition(1, 7)
|
|
||||||
tool_ctl.p_desc.set_value("Each unit can have at most 2 boilers. Boiler #1 shows up first on the main display, followed by boiler #2 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have boiler #1 and another can have #2, but both cannot have #1.")
|
|
||||||
elseif type == "turbineValve" then
|
elseif type == "turbineValve" then
|
||||||
tool_ctl.p_prompt.set_value("This is the # turbine for reactor unit # .")
|
reposition("This is reactor unit # 's # turbine.", 31, 3, 23, 4, 7)
|
||||||
tool_ctl.p_idx.show()
|
|
||||||
tool_ctl.p_idx.redraw()
|
|
||||||
tool_ctl.p_idx.enable()
|
|
||||||
tool_ctl.p_idx.set_max(3)
|
|
||||||
tool_ctl.p_unit.reposition(45, 4)
|
|
||||||
tool_ctl.p_unit.enable()
|
|
||||||
tool_ctl.p_unit.show()
|
|
||||||
tool_ctl.p_assign_btn.hide(true)
|
tool_ctl.p_assign_btn.hide(true)
|
||||||
tool_ctl.p_assign_end.hide(true)
|
tool_ctl.p_desc.set_value("Each unit can have at most 3 turbines. Turbine #1 shows up first on the main display, followed by #2 then #3 below it. The numberings are per unit (unit 1 and unit 2 would both have a turbine #1) and can be split amongst multiple RTUs (one has #1, another has #2).")
|
||||||
tool_ctl.p_desc.reposition(1, 7)
|
|
||||||
tool_ctl.p_desc.set_value("Each unit can have at most 3 turbines. Turbine #1 shows up first on the main display, followed by #2 then #3 below it. These numberings are independent of which RTU they are connected to. For example, one RTU can have turbine #1 and another can have #2, but both cannot have #1.")
|
|
||||||
elseif type == "solarNeutronActivator" then
|
elseif type == "solarNeutronActivator" then
|
||||||
|
reposition("This SNA is for reactor unit # .", 46, 1, 31, 4, 7)
|
||||||
tool_ctl.p_idx.hide()
|
tool_ctl.p_idx.hide()
|
||||||
tool_ctl.p_prompt.set_value("This SNA is for reactor unit # .")
|
|
||||||
tool_ctl.p_unit.reposition(31, 4)
|
|
||||||
tool_ctl.p_unit.enable()
|
|
||||||
tool_ctl.p_unit.show()
|
|
||||||
tool_ctl.p_assign_btn.hide(true)
|
tool_ctl.p_assign_btn.hide(true)
|
||||||
tool_ctl.p_assign_end.hide(true)
|
|
||||||
tool_ctl.p_desc_ext.set_value("Before adding lots of SNAs: multiply the \"PEAK\" rate on the flow monitor (after connecting at least 1 SNA) by 10 to get the mB/t of waste that they can process. Enough SNAs to provide 2x to 3x of your max burn rate should be a good margin to catch up after night or cloudy weather. Too many devices (such as SNAs) on one RTU can cause lag.")
|
tool_ctl.p_desc_ext.set_value("Before adding lots of SNAs: multiply the \"PEAK\" rate on the flow monitor (after connecting at least 1 SNA) by 10 to get the mB/t of waste that they can process. Enough SNAs to provide 2x to 3x of your max burn rate should be a good margin to catch up after night or cloudy weather. Too many devices (such as SNAs) on one RTU can cause lag.")
|
||||||
elseif type == "dynamicValve" then
|
elseif type == "dynamicValve" then
|
||||||
tool_ctl.p_prompt.set_value("This is the # dynamic tank for...")
|
reposition("This is the below system's # dynamic tank.", 29, 4, 17, 6, 8)
|
||||||
tool_ctl.p_assign_btn.show()
|
tool_ctl.p_assign_btn.show()
|
||||||
tool_ctl.p_assign_btn.redraw()
|
tool_ctl.p_assign_btn.redraw()
|
||||||
tool_ctl.p_assign_end.show()
|
|
||||||
tool_ctl.p_assign_end.redraw()
|
|
||||||
tool_ctl.p_idx.show()
|
|
||||||
tool_ctl.p_idx.redraw()
|
|
||||||
tool_ctl.p_idx.set_max(4)
|
|
||||||
tool_ctl.p_unit.reposition(18, 6)
|
|
||||||
tool_ctl.p_unit.enable()
|
|
||||||
tool_ctl.p_unit.show()
|
|
||||||
|
|
||||||
if tool_ctl.p_assign_btn.get_value() == 1 then
|
if tool_ctl.p_assign_btn.get_value() == 1 then
|
||||||
tool_ctl.p_idx.enable()
|
tool_ctl.p_idx.enable()
|
||||||
@@ -885,22 +867,12 @@ local function config_view(display)
|
|||||||
tool_ctl.p_unit.enable()
|
tool_ctl.p_unit.enable()
|
||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.p_desc.reposition(1, 8)
|
|
||||||
tool_ctl.p_desc.set_value("Each reactor unit can have at most 1 tank and the facility can have at most 4. Each facility tank must have a unique # 1 through 4, regardless of where it is connected. Only a total of 4 tanks can be displayed on the flow monitor.")
|
tool_ctl.p_desc.set_value("Each reactor unit can have at most 1 tank and the facility can have at most 4. Each facility tank must have a unique # 1 through 4, regardless of where it is connected. Only a total of 4 tanks can be displayed on the flow monitor.")
|
||||||
elseif type == "environmentDetector" then
|
elseif type == "environmentDetector" then
|
||||||
tool_ctl.p_prompt.set_value("This is the # environment detector for...")
|
reposition("This is the below system's # env. detector.", 29, 99, 17, 6, 8)
|
||||||
tool_ctl.p_assign_btn.show()
|
tool_ctl.p_assign_btn.show()
|
||||||
tool_ctl.p_assign_btn.redraw()
|
tool_ctl.p_assign_btn.redraw()
|
||||||
tool_ctl.p_assign_end.show()
|
|
||||||
tool_ctl.p_assign_end.redraw()
|
|
||||||
tool_ctl.p_idx.show()
|
|
||||||
tool_ctl.p_idx.redraw()
|
|
||||||
tool_ctl.p_idx.set_max(99)
|
|
||||||
tool_ctl.p_unit.reposition(18, 6)
|
|
||||||
tool_ctl.p_unit.enable()
|
|
||||||
tool_ctl.p_unit.show()
|
|
||||||
if tool_ctl.p_assign_btn.get_value() == 1 then tool_ctl.p_unit.disable() else tool_ctl.p_unit.enable() end
|
if tool_ctl.p_assign_btn.get_value() == 1 then tool_ctl.p_unit.disable() else tool_ctl.p_unit.enable() end
|
||||||
tool_ctl.p_desc.reposition(1, 8)
|
|
||||||
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
||||||
elseif type == "inductionPort" or type == "spsPort" then
|
elseif type == "inductionPort" or type == "spsPort" then
|
||||||
local dev = tri(type == "inductionPort", "induction matrix", "SPS")
|
local dev = tri(type == "inductionPort", "induction matrix", "SPS")
|
||||||
@@ -908,7 +880,6 @@ local function config_view(display)
|
|||||||
tool_ctl.p_unit.hide(true)
|
tool_ctl.p_unit.hide(true)
|
||||||
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
||||||
tool_ctl.p_assign_btn.hide(true)
|
tool_ctl.p_assign_btn.hide(true)
|
||||||
tool_ctl.p_assign_end.hide(true)
|
|
||||||
tool_ctl.p_desc.reposition(1, 7)
|
tool_ctl.p_desc.reposition(1, 7)
|
||||||
tool_ctl.p_desc.set_value("There can only be one of these devices per SCADA network, so it will be assigned as the sole " .. dev .. " for the facility. There must only be one of these across all the RTUs you have.")
|
tool_ctl.p_desc.set_value("There can only be one of these devices per SCADA network, so it will be assigned as the sole " .. dev .. " for the facility. There must only be one of these across all the RTUs you have.")
|
||||||
else
|
else
|
||||||
@@ -934,8 +905,8 @@ local function config_view(display)
|
|||||||
---@cast entry ppm_entry
|
---@cast entry ppm_entry
|
||||||
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
||||||
PushButton{parent=line,x=1,y=1,min_width=9,alignment=LEFT,height=1,text="> SELECT",callback=function()tool_ctl.peri_cfg_manual=false;new_peri(name,entry.type)end,fg_bg=cpair(colors.black,colors.purple),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=line,x=1,y=1,min_width=9,alignment=LEFT,height=1,text="> SELECT",callback=function()tool_ctl.peri_cfg_manual=false;new_peri(name,entry.type)end,fg_bg=cpair(colors.black,colors.purple),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=line,x=11,y=1,height=1,text=name,fg_bg=cpair(colors.black,bkg)}
|
TextBox{parent=line,x=11,y=1,text=name,fg_bg=cpair(colors.black,bkg)}
|
||||||
TextBox{parent=line,x=11,y=2,height=1,text=entry.type,fg_bg=cpair(colors.gray,bkg)}
|
TextBox{parent=line,x=11,y=2,text=entry.type,fg_bg=cpair(colors.gray,bkg)}
|
||||||
|
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
end
|
end
|
||||||
@@ -948,7 +919,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=peri_c_3,x=1,y=6,height=4,text="Peripheral Name"}
|
TextBox{parent=peri_c_3,x=1,y=6,height=4,text="Peripheral Name"}
|
||||||
local p_name = TextField{parent=peri_c_3,x=1,y=7,width=49,height=1,max_len=128,fg_bg=bw_fg_bg}
|
local p_name = TextField{parent=peri_c_3,x=1,y=7,width=49,height=1,max_len=128,fg_bg=bw_fg_bg}
|
||||||
local p_type = Radio2D{parent=peri_c_3,x=1,y=9,rows=4,columns=2,default=1,options=RTU_DEV_TYPES,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple}
|
local p_type = Radio2D{parent=peri_c_3,x=1,y=9,rows=4,columns=2,default=1,options=RTU_DEV_TYPES,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple}
|
||||||
local man_p_err = TextBox{parent=peri_c_3,x=8,y=14,height=1,width=35,text="Please enter a peripheral name.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local man_p_err = TextBox{parent=peri_c_3,x=8,y=14,width=35,text="Please enter a peripheral name.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
man_p_err.hide(true)
|
man_p_err.hide(true)
|
||||||
|
|
||||||
local function submit_manual_peri()
|
local function submit_manual_peri()
|
||||||
@@ -965,11 +936,10 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.p_name_msg = TextBox{parent=peri_c_4,x=1,y=1,height=2,text=""}
|
tool_ctl.p_name_msg = TextBox{parent=peri_c_4,x=1,y=1,height=2,text=""}
|
||||||
tool_ctl.p_prompt = TextBox{parent=peri_c_4,x=1,y=4,height=2,text=""}
|
tool_ctl.p_prompt = TextBox{parent=peri_c_4,x=1,y=4,height=2,text=""}
|
||||||
tool_ctl.p_idx = NumberField{parent=peri_c_4,x=14,y=4,width=4,max_chars=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.p_idx = NumberField{parent=peri_c_4,x=31,y=4,width=4,max_chars=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
tool_ctl.p_assign_btn = RadioButton{parent=peri_c_4,x=1,y=5,default=1,options={"the facility.","a unit. (unit #"},callback=function(v)tool_ctl.p_assign(v)end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple}
|
tool_ctl.p_assign_btn = RadioButton{parent=peri_c_4,x=1,y=5,default=1,options={"the facility","reactor unit #"},callback=function(v)tool_ctl.p_assign(v)end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple}
|
||||||
tool_ctl.p_assign_end = TextBox{parent=peri_c_4,x=22,y=6,height=6,width=1,text=")"}
|
|
||||||
|
|
||||||
tool_ctl.p_unit = NumberField{parent=peri_c_4,x=44,y=4,width=4,max_chars=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.p_unit = NumberField{parent=peri_c_4,x=23,y=4,width=4,max_chars=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
tool_ctl.p_unit.disable()
|
tool_ctl.p_unit.disable()
|
||||||
|
|
||||||
function tool_ctl.p_assign(opt)
|
function tool_ctl.p_assign(opt)
|
||||||
@@ -988,7 +958,7 @@ local function config_view(display)
|
|||||||
tool_ctl.p_desc = TextBox{parent=peri_c_4,x=1,y=7,height=6,text="",fg_bg=g_lg_fg_bg}
|
tool_ctl.p_desc = TextBox{parent=peri_c_4,x=1,y=7,height=6,text="",fg_bg=g_lg_fg_bg}
|
||||||
tool_ctl.p_desc_ext = TextBox{parent=peri_c_4,x=1,y=6,height=7,text="",fg_bg=g_lg_fg_bg}
|
tool_ctl.p_desc_ext = TextBox{parent=peri_c_4,x=1,y=6,height=7,text="",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
tool_ctl.p_err = TextBox{parent=peri_c_4,x=8,y=14,height=1,width=32,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
tool_ctl.p_err = TextBox{parent=peri_c_4,x=8,y=14,width=32,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
tool_ctl.p_err.hide(true)
|
tool_ctl.p_err.hide(true)
|
||||||
|
|
||||||
local function back_from_peri_opts()
|
local function back_from_peri_opts()
|
||||||
@@ -1073,7 +1043,7 @@ local function config_view(display)
|
|||||||
PushButton{parent=peri_c_4,x=1,y=14,text="\x1b Back",callback=back_from_peri_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_4,x=1,y=14,text="\x1b Back",callback=back_from_peri_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=peri_c_4,x=41,y=14,min_width=9,text="Confirm",callback=save_peri_entry,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_4,x=41,y=14,min_width=9,text="Confirm",callback=save_peri_entry,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=peri_c_5,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=peri_c_5,x=1,y=1,text="Settings saved!"}
|
||||||
PushButton{parent=peri_c_5,x=1,y=14,text="\x1b Back",callback=function()peri_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_5,x=1,y=14,text="\x1b Back",callback=function()peri_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=peri_c_5,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=peri_c_5,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -1095,9 +1065,9 @@ local function config_view(display)
|
|||||||
|
|
||||||
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6,rs_c_7}}
|
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6,rs_c_7}}
|
||||||
|
|
||||||
TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
TextBox{parent=rs_cfg,x=1,y=2,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
||||||
|
|
||||||
TextBox{parent=rs_c_1,x=1,y=1,height=1,text=" port side/color unit/facility",fg_bg=g_lg_fg_bg}
|
TextBox{parent=rs_c_1,x=1,y=1,text=" port side/color unit/facility",fg_bg=g_lg_fg_bg}
|
||||||
local rs_list = ListBox{parent=rs_c_1,x=1,y=2,height=11,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local rs_list = ListBox{parent=rs_c_1,x=1,y=2,height=11,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
local function rs_revert()
|
local function rs_revert()
|
||||||
@@ -1125,7 +1095,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=rs_c_6,x=1,y=1,height=5,text="You already configured this input. There can only be one entry for each input.\n\nPlease select a different port."}
|
TextBox{parent=rs_c_6,x=1,y=1,height=5,text="You already configured this input. There can only be one entry for each input.\n\nPlease select a different port."}
|
||||||
PushButton{parent=rs_c_6,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_6,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=rs_c_2,x=1,y=1,height=1,text="Select one of the below ports to use."}
|
TextBox{parent=rs_c_2,x=1,y=1,text="Select one of the below ports to use."}
|
||||||
|
|
||||||
local rs_ports = ListBox{parent=rs_c_2,x=1,y=3,height=10,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local rs_ports = ListBox{parent=rs_c_2,x=1,y=3,height=10,width=49,scroll_height=200,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -1186,8 +1156,8 @@ local function config_view(display)
|
|||||||
-- add entries to redstone option list
|
-- add entries to redstone option list
|
||||||
local all_w_macro = Div{parent=rs_ports,height=1}
|
local all_w_macro = Div{parent=rs_ports,height=1}
|
||||||
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=all_w_macro,x=16,y=1,width=5,height=1,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=all_w_macro,x=16,y=1,width=5,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=all_w_macro,x=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=all_w_macro,x=22,y=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
for i = 1, rsio.NUM_PORTS do
|
for i = 1, rsio.NUM_PORTS do
|
||||||
local p = PORT_DESC_MAP[i][1]
|
local p = PORT_DESC_MAP[i][1]
|
||||||
@@ -1197,8 +1167,8 @@ local function config_view(display)
|
|||||||
|
|
||||||
local entry = Div{parent=rs_ports,height=1}
|
local entry = Div{parent=rs_ports,height=1}
|
||||||
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(p)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(p)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=entry,x=16,y=1,width=5,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=entry,x=16,y=1,width=5,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=entry,x=22,y=1,height=1,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=22,y=1,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
@@ -1212,10 +1182,10 @@ local function config_view(display)
|
|||||||
TextBox{parent=rs_c_7,x=1,y=11,height=2,text="Analog Input: 0-15 redstone power level input\nAnalog Output: 0-15 scaled redstone power level output"}
|
TextBox{parent=rs_c_7,x=1,y=11,height=2,text="Analog Input: 0-15 redstone power level input\nAnalog Output: 0-15 scaled redstone power level output"}
|
||||||
PushButton{parent=rs_c_7,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_7,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_side_l = TextBox{parent=rs_c_3,x=1,y=4,width=11,height=1,text="Output Side"}
|
tool_ctl.rs_cfg_side_l = TextBox{parent=rs_c_3,x=1,y=4,width=11,text="Output Side"}
|
||||||
local side = Radio2D{parent=rs_c_3,x=1,y=5,rows=1,columns=6,default=1,options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.red}
|
local side = Radio2D{parent=rs_c_3,x=1,y=5,rows=1,columns=6,default=1,options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.red}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=25,y=7,width=7,height=1,text="Unit ID"}
|
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=25,y=7,width=7,text="Unit ID"}
|
||||||
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=33,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=33,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function set_bundled(bundled)
|
local function set_bundled(bundled)
|
||||||
@@ -1229,7 +1199,7 @@ local function config_view(display)
|
|||||||
tool_ctl.rs_cfg_color = Radio2D{parent=rs_c_3,x=1,y=9,rows=4,columns=4,default=1,options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
tool_ctl.rs_cfg_color = Radio2D{parent=rs_c_3,x=1,y=9,rows=4,columns=4,default=1,options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
||||||
tool_ctl.rs_cfg_color.disable()
|
tool_ctl.rs_cfg_color.disable()
|
||||||
|
|
||||||
local rs_err = TextBox{parent=rs_c_3,x=8,y=14,height=1,width=30,text="Unit ID must be within 1 to 4.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local rs_err = TextBox{parent=rs_c_3,x=8,y=14,width=30,text="Unit ID must be within 1 to 4.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
rs_err.hide(true)
|
rs_err.hide(true)
|
||||||
|
|
||||||
local function back_from_rs_opts()
|
local function back_from_rs_opts()
|
||||||
@@ -1285,7 +1255,7 @@ local function config_view(display)
|
|||||||
PushButton{parent=rs_c_3,x=1,y=14,text="\x1b Back",callback=back_from_rs_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_3,x=1,y=14,text="\x1b Back",callback=back_from_rs_opts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=rs_c_3,x=41,y=14,min_width=9,text="Confirm",callback=save_rs_entry,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_3,x=41,y=14,min_width=9,text="Confirm",callback=save_rs_entry,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=rs_c_4,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=rs_c_4,x=1,y=1,text="Settings saved!"}
|
||||||
PushButton{parent=rs_c_4,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_4,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=rs_c_4,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_4,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -1386,9 +1356,9 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local line = Div{parent=peri_import_list,height=3}
|
local line = Div{parent=peri_import_list,height=3}
|
||||||
TextBox{parent=line,x=1,y=1,height=1,text="@ "..def.name,fg_bg=cpair(colors.black,colors.white)}
|
TextBox{parent=line,x=1,y=1,text="@ "..def.name,fg_bg=cpair(colors.black,colors.white)}
|
||||||
TextBox{parent=line,x=1,y=2,height=1,text=status,fg_bg=cpair(color,colors.white)}
|
TextBox{parent=line,x=1,y=2,text=status,fg_bg=cpair(color,colors.white)}
|
||||||
TextBox{parent=line,x=1,y=3,height=1,text=desc,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=line,x=1,y=3,text=desc,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
|
|
||||||
rs_import_list.remove_all()
|
rs_import_list.remove_all()
|
||||||
@@ -1407,10 +1377,10 @@ local function config_view(display)
|
|||||||
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
||||||
|
|
||||||
local line = Div{parent=rs_import_list,height=1}
|
local line = Div{parent=rs_import_list,height=1}
|
||||||
TextBox{parent=line,x=1,y=1,width=1,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=line,x=1,y=1,width=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=line,x=2,y=1,width=14,height=1,text=name}
|
TextBox{parent=line,x=2,y=1,width=14,text=name}
|
||||||
TextBox{parent=line,x=18,y=1,width=string.len(conn),height=1,text=conn,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=line,x=18,y=1,width=string.len(conn),text=conn,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=line,x=40,y=1,height=1,text=unit,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=line,x=40,y=1,text=unit,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1545,9 +1515,9 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local entry = Div{parent=peri_list,height=3}
|
local entry = Div{parent=peri_list,height=3}
|
||||||
TextBox{parent=entry,x=1,y=1,height=1,text="@ "..def.name,fg_bg=cpair(colors.black,colors.white)}
|
TextBox{parent=entry,x=1,y=1,text="@ "..def.name,fg_bg=cpair(colors.black,colors.white)}
|
||||||
TextBox{parent=entry,x=1,y=2,height=1,text=" \x1a "..t_str,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=1,y=2,text=" \x1a "..t_str,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=entry,x=1,y=3,height=1,text=desc,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=1,y=3,text=desc,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
local edit_btn = PushButton{parent=entry,x=41,y=2,min_width=8,height=1,text="EDIT",callback=function()edit_peri_entry(i,def,t or "")end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
local edit_btn = PushButton{parent=entry,x=41,y=2,min_width=8,height=1,text="EDIT",callback=function()edit_peri_entry(i,def,t or "")end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
PushButton{parent=entry,x=41,y=3,min_width=8,height=1,text="DELETE",callback=function()delete_peri_entry(i)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=entry,x=41,y=3,min_width=8,height=1,text="DELETE",callback=function()delete_peri_entry(i)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -1613,10 +1583,10 @@ local function config_view(display)
|
|||||||
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
||||||
|
|
||||||
local entry = Div{parent=rs_list,height=1}
|
local entry = Div{parent=rs_list,height=1}
|
||||||
TextBox{parent=entry,x=1,y=1,width=1,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=entry,x=1,y=1,width=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=entry,x=2,y=1,width=14,height=1,text=name}
|
TextBox{parent=entry,x=2,y=1,width=14,text=name}
|
||||||
TextBox{parent=entry,x=16,y=1,width=string.len(conn),height=1,text=conn,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=16,y=1,width=string.len(conn),text=conn,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=entry,x=33,y=1,width=1,height=1,text=unit,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=33,y=1,width=1,text=unit,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
PushButton{parent=entry,x=35,y=1,min_width=6,height=1,text="EDIT",callback=function()edit_rs_entry(i)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=entry,x=35,y=1,min_width=6,height=1,text="EDIT",callback=function()edit_rs_entry(i)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=entry,x=41,y=1,min_width=8,height=1,text="DELETE",callback=function()delete_rs_entry(i)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=entry,x=41,y=1,min_width=8,height=1,text="DELETE",callback=function()delete_rs_entry(i)end,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC
|
|||||||
local function init(panel, units)
|
local function init(panel, units)
|
||||||
local disabled_fg = style.fp.disabled_fg
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- system indicators
|
-- system indicators
|
||||||
@@ -98,9 +98,9 @@ local function init(panel, units)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=disabled_fg}
|
TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=disabled_fg}
|
||||||
|
|
||||||
TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.fp.text_fg}
|
TextBox{parent=system,x=1,y=14,text="SPEAKERS",width=8,fg_bg=style.fp.text_fg}
|
||||||
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=style.theme.field_box}
|
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=style.theme.field_box}
|
||||||
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
||||||
|
|
||||||
@@ -109,8 +109,8 @@ local function init(panel, units)
|
|||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@@ -126,7 +126,7 @@ local function init(panel, units)
|
|||||||
|
|
||||||
-- show routine statuses
|
-- show routine statuses
|
||||||
for i = 1, list_length do
|
for i = 1, list_length do
|
||||||
TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1}
|
TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i)}
|
||||||
local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=ind_grn}
|
local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=ind_grn}
|
||||||
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
|
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
|
||||||
end
|
end
|
||||||
@@ -144,13 +144,13 @@ local function init(panel, units)
|
|||||||
|
|
||||||
-- unit name identifier (type + index)
|
-- unit name identifier (type + index)
|
||||||
local function get_name(t) return util.c(UNIT_TYPE_LABELS[t + 1], " ", util.trinary(util.is_int(unit.index), unit.index, "")) end
|
local function get_name(t) return util.c(UNIT_TYPE_LABELS[t + 1], " ", util.trinary(util.is_int(unit.index), unit.index, "")) end
|
||||||
local name_box = TextBox{parent=unit_hw_statuses,y=i,x=3,text=get_name(unit.type),width=15,height=1}
|
local name_box = TextBox{parent=unit_hw_statuses,y=i,x=3,text=get_name(unit.type),width=15}
|
||||||
|
|
||||||
name_box.register(databus.ps, "unit_type_" .. i, function (t) name_box.set_value(get_name(t)) end)
|
name_box.register(databus.ps, "unit_type_" .. i, function (t) name_box.set_value(get_name(t)) end)
|
||||||
|
|
||||||
-- assignment (unit # or facility)
|
-- assignment (unit # or facility)
|
||||||
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
||||||
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,height=1,fg_bg=disabled_fg}
|
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,fg_bg=disabled_fg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
15
rtu/rtu.lua
15
rtu/rtu.lua
@@ -284,8 +284,8 @@ end
|
|||||||
function rtu.comms(version, nic, conn_watchdog)
|
function rtu.comms(version, nic, conn_watchdog)
|
||||||
local self = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil,
|
r_seq_num = nil, ---@type nil|integer
|
||||||
txn_id = 0,
|
txn_id = 0,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
}
|
}
|
||||||
@@ -442,8 +442,8 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
if l_chan == config.RTU_Channel then
|
if l_chan == config.RTU_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif rtu_state.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif rtu_state.linked and (src_addr ~= self.sv_addr) then
|
elseif rtu_state.linked and (src_addr ~= self.sv_addr) then
|
||||||
@@ -451,7 +451,7 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@@ -556,7 +556,6 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
-- establish allowed
|
-- establish allowed
|
||||||
rtu_state.linked = true
|
rtu_state.linked = true
|
||||||
self.sv_addr = packet.scada_frame.src_addr()
|
self.sv_addr = packet.scada_frame.src_addr()
|
||||||
self.r_seq_num = nil
|
|
||||||
println_ts("supervisor connection established")
|
println_ts("supervisor connection established")
|
||||||
log.info("supervisor connection established")
|
log.info("supervisor connection established")
|
||||||
else
|
else
|
||||||
@@ -572,7 +571,9 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
public.unlink(rtu_state)
|
-- unlink
|
||||||
|
self.sv_addr = comms.BROADCAST
|
||||||
|
rtu_state.linked = false
|
||||||
end
|
end
|
||||||
|
|
||||||
self.last_est_ack = est_ack
|
self.last_est_ack = est_ack
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
|||||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||||
|
|
||||||
local RTU_VERSION = "v1.9.6"
|
local RTU_VERSION = "v1.10.3"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
@@ -93,14 +93,6 @@ local function main()
|
|||||||
network.init_mac(config.AuthKey)
|
network.init_mac(config.AuthKey)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get modem
|
|
||||||
local modem = ppm.get_wireless_modem()
|
|
||||||
if modem == nil then
|
|
||||||
println("boot> wireless modem not found")
|
|
||||||
log.fatal("no wireless modem on startup")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- generate alarm tones
|
-- generate alarm tones
|
||||||
audio.generate_tones()
|
audio.generate_tones()
|
||||||
|
|
||||||
@@ -116,14 +108,15 @@ local function main()
|
|||||||
|
|
||||||
-- RTU gateway devices (not RTU units)
|
-- RTU gateway devices (not RTU units)
|
||||||
rtu_dev = {
|
rtu_dev = {
|
||||||
|
modem = ppm.get_wireless_modem(),
|
||||||
sounders = {}
|
sounders = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
rtu_sys = {
|
rtu_sys = {
|
||||||
nic = network.nic(modem),
|
nic = nil, ---@type nic
|
||||||
rtu_comms = nil, ---@type rtu_comms
|
rtu_comms = nil, ---@type rtu_comms
|
||||||
conn_watchdog = nil, ---@type watchdog
|
conn_watchdog = nil, ---@type watchdog
|
||||||
units = {}
|
units = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -134,8 +127,9 @@ local function main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
local smem_sys = __shared_memory.rtu_sys
|
local smem_sys = __shared_memory.rtu_sys
|
||||||
|
local smem_dev = __shared_memory.rtu_dev
|
||||||
|
|
||||||
databus.tx_hw_modem(true)
|
local rtu_state = __shared_memory.rtu_state
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- interpret config and init units
|
-- interpret config and init units
|
||||||
@@ -501,8 +495,6 @@ local function main()
|
|||||||
-- start system
|
-- start system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local rtu_state = __shared_memory.rtu_state
|
|
||||||
|
|
||||||
log.debug("boot> running sys_config()")
|
log.debug("boot> running sys_config()")
|
||||||
|
|
||||||
if sys_config() then
|
if sys_config() then
|
||||||
@@ -517,23 +509,33 @@ local function main()
|
|||||||
log.info("startup> running in headless mode without front panel")
|
log.info("startup> running in headless mode without front panel")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check modem
|
||||||
|
if smem_dev.modem == nil then
|
||||||
|
println("startup> wireless modem not found")
|
||||||
|
log.fatal("no wireless modem on startup")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
databus.tx_hw_modem(true)
|
||||||
|
|
||||||
-- find and setup all speakers
|
-- find and setup all speakers
|
||||||
local speakers = ppm.get_all_devices("speaker")
|
local speakers = ppm.get_all_devices("speaker")
|
||||||
for _, s in pairs(speakers) do
|
for _, s in pairs(speakers) do
|
||||||
local sounder = rtu.init_sounder(s)
|
local sounder = rtu.init_sounder(s)
|
||||||
|
|
||||||
table.insert(__shared_memory.rtu_dev.sounders, sounder)
|
table.insert(smem_dev.sounders, sounder)
|
||||||
|
|
||||||
log.debug(util.c("startup> added speaker, attached as ", sounder.name))
|
log.debug(util.c("startup> added speaker, attached as ", sounder.name))
|
||||||
end
|
end
|
||||||
|
|
||||||
databus.tx_hw_spkr_count(#__shared_memory.rtu_dev.sounders)
|
databus.tx_hw_spkr_count(#smem_dev.sounders)
|
||||||
|
|
||||||
-- start connection watchdog
|
-- start connection watchdog
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
||||||
log.debug("startup> conn watchdog started")
|
log.debug("startup> conn watchdog started")
|
||||||
|
|
||||||
-- setup comms
|
-- setup comms
|
||||||
|
smem_sys.nic = network.nic(smem_dev.modem)
|
||||||
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_sys.nic, smem_sys.conn_watchdog)
|
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_sys.nic, smem_sys.conn_watchdog)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ local max_distance = nil
|
|||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
||||||
comms.version = "2.5.1"
|
comms.version = "3.0.0"
|
||||||
comms.api_version = "0.0.2"
|
comms.api_version = "0.0.3"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
local PROTOCOL = {
|
local PROTOCOL = {
|
||||||
@@ -240,6 +240,8 @@ function comms.scada_packet()
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.modem_event() return self.modem_msg_in end
|
function public.modem_event() return self.modem_msg_in end
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
function public.raw_header() return { self.src_addr, self.dest_addr, self.seq_num, self.protocol } end
|
||||||
|
---@nodiscard
|
||||||
function public.raw_sendable() return self.raw end
|
function public.raw_sendable() return self.raw end
|
||||||
|
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -278,7 +280,7 @@ function comms.authd_packet()
|
|||||||
src_addr = comms.BROADCAST,
|
src_addr = comms.BROADCAST,
|
||||||
dest_addr = comms.BROADCAST,
|
dest_addr = comms.BROADCAST,
|
||||||
mac = "",
|
mac = "",
|
||||||
payload = ""
|
payload = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class authd_packet
|
---@class authd_packet
|
||||||
@@ -286,14 +288,13 @@ function comms.authd_packet()
|
|||||||
|
|
||||||
-- make an authenticated SCADA packet
|
-- make an authenticated SCADA packet
|
||||||
---@param s_packet scada_packet scada packet to authenticate
|
---@param s_packet scada_packet scada packet to authenticate
|
||||||
---@param mac function message authentication function
|
---@param mac function message authentication hash function
|
||||||
function public.make(s_packet, mac)
|
function public.make(s_packet, mac)
|
||||||
self.valid = true
|
self.valid = true
|
||||||
self.src_addr = s_packet.src_addr()
|
self.src_addr = s_packet.src_addr()
|
||||||
self.dest_addr = s_packet.dest_addr()
|
self.dest_addr = s_packet.dest_addr()
|
||||||
self.payload = textutils.serialize(s_packet.raw_sendable(), { allow_repetitions = true, compact = true })
|
self.mac = mac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||||
self.mac = mac(self.payload)
|
self.raw = { self.src_addr, self.dest_addr, self.mac, s_packet.raw_sendable() }
|
||||||
self.raw = { self.src_addr, self.dest_addr, self.mac, self.payload }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse in a modem message as an authenticated SCADA packet
|
-- parse in a modem message as an authenticated SCADA packet
|
||||||
@@ -330,14 +331,14 @@ function comms.authd_packet()
|
|||||||
self.src_addr = nil
|
self.src_addr = nil
|
||||||
self.dest_addr = nil
|
self.dest_addr = nil
|
||||||
self.mac = ""
|
self.mac = ""
|
||||||
self.payload = ""
|
self.payload = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if this packet is destined for this device
|
-- check if this packet is destined for this device
|
||||||
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
|
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
|
||||||
|
|
||||||
self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
|
self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
|
||||||
type(self.mac) == "string" and type(self.payload) == "string"
|
type(self.mac) == "string" and type(self.payload) == "table"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ local alarms = {}
|
|||||||
|
|
||||||
-- unit alarms
|
-- unit alarms
|
||||||
|
|
||||||
alarms.HIGH_TEMP = 1150 -- temp >= 1150K
|
|
||||||
alarms.HIGH_WASTE = 0.85 -- fill > 85%
|
alarms.HIGH_WASTE = 0.85 -- fill > 85%
|
||||||
alarms.HIGH_RADIATION = 0.00005 -- 50 uSv/h, not yet damaging but this isn't good
|
alarms.HIGH_RADIATION = 0.00005 -- 50 uSv/h, not yet damaging but this isn't good
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ end
|
|||||||
---@param modem table modem to use
|
---@param modem table modem to use
|
||||||
function network.nic(modem)
|
function network.nic(modem)
|
||||||
local self = {
|
local self = {
|
||||||
connected = true, -- used to avoid costly MAC calculations if modem isn't even present
|
connected = true, -- used to avoid costly MAC calculations if modem isn't even present
|
||||||
channels = {}
|
channels = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ function network.nic(modem)
|
|||||||
modem.open(channel)
|
modem.open(channel)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- link all public functions except for transmit
|
-- link all public functions except for transmit, open, and close
|
||||||
for key, func in pairs(modem) do
|
for key, func in pairs(modem) do
|
||||||
if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end
|
if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end
|
||||||
end
|
end
|
||||||
@@ -175,7 +175,7 @@ function network.nic(modem)
|
|||||||
---@param packet scada_packet packet
|
---@param packet scada_packet packet
|
||||||
function public.transmit(dest_channel, local_channel, packet)
|
function public.transmit(dest_channel, local_channel, packet)
|
||||||
if self.connected then
|
if self.connected then
|
||||||
local tx_packet = packet ---@type authd_packet|scada_packet
|
local tx_packet = packet ---@type authd_packet|scada_packet
|
||||||
|
|
||||||
if c_eng.hmac ~= nil then
|
if c_eng.hmac ~= nil then
|
||||||
-- local start = util.time_ms()
|
-- local start = util.time_ms()
|
||||||
@@ -184,7 +184,7 @@ function network.nic(modem)
|
|||||||
---@cast tx_packet authd_packet
|
---@cast tx_packet authd_packet
|
||||||
tx_packet.make(packet, compute_hmac)
|
tx_packet.make(packet, compute_hmac)
|
||||||
|
|
||||||
-- log.debug("crypto.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("network.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
||||||
end
|
end
|
||||||
|
|
||||||
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
||||||
@@ -211,17 +211,18 @@ function network.nic(modem)
|
|||||||
a_packet.receive(side, sender, reply_to, message, distance)
|
a_packet.receive(side, sender, reply_to, message, distance)
|
||||||
|
|
||||||
if a_packet.is_valid() then
|
if a_packet.is_valid() then
|
||||||
-- local start = util.time_ms()
|
s_packet.receive(side, sender, reply_to, a_packet.data(), distance)
|
||||||
local packet_hmac = a_packet.mac()
|
|
||||||
local msg = a_packet.data()
|
|
||||||
local computed_hmac = compute_hmac(msg)
|
|
||||||
|
|
||||||
if packet_hmac == computed_hmac then
|
if s_packet.is_valid() then
|
||||||
-- log.debug("crypto.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
-- local start = util.time_ms()
|
||||||
s_packet.receive(side, sender, reply_to, textutils.unserialize(msg), distance)
|
local computed_hmac = compute_hmac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||||
s_packet.stamp_authenticated()
|
|
||||||
else
|
if a_packet.mac() == computed_hmac then
|
||||||
-- log.debug("crypto.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("network.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
||||||
|
s_packet.stamp_authenticated()
|
||||||
|
else
|
||||||
|
-- log.debug("network.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -74,6 +74,47 @@ function types.new_zero_coordinate() return { x = 0, y = 0, z = 0 } end
|
|||||||
-- ENUMERATION TYPES --
|
-- ENUMERATION TYPES --
|
||||||
--#region
|
--#region
|
||||||
|
|
||||||
|
---@enum TEMP_SCALE
|
||||||
|
types.TEMP_SCALE = {
|
||||||
|
KELVIN = 1,
|
||||||
|
CELSIUS = 2,
|
||||||
|
FAHRENHEIT = 3,
|
||||||
|
RANKINE = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
types.TEMP_SCALE_NAMES = {
|
||||||
|
"Kelvin",
|
||||||
|
"Celsius",
|
||||||
|
"Fahrenheit",
|
||||||
|
"Rankine"
|
||||||
|
}
|
||||||
|
|
||||||
|
types.TEMP_SCALE_UNITS = {
|
||||||
|
"K",
|
||||||
|
"\xb0C",
|
||||||
|
"\xb0F",
|
||||||
|
"\xb0R"
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum ENERGY_SCALE
|
||||||
|
types.ENERGY_SCALE = {
|
||||||
|
JOULES = 1,
|
||||||
|
FE = 2,
|
||||||
|
RF = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
types.ENERGY_SCALE_NAMES = {
|
||||||
|
"Joules (J)",
|
||||||
|
"Forge Energy (FE)",
|
||||||
|
"Redstone Flux (RF)"
|
||||||
|
}
|
||||||
|
|
||||||
|
types.ENERGY_SCALE_UNITS = {
|
||||||
|
"J",
|
||||||
|
"FE",
|
||||||
|
"RF"
|
||||||
|
}
|
||||||
|
|
||||||
---@enum PANEL_LINK_STATE
|
---@enum PANEL_LINK_STATE
|
||||||
types.PANEL_LINK_STATE = {
|
types.PANEL_LINK_STATE = {
|
||||||
LINKED = 1,
|
LINKED = 1,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
local cc_strings = require("cc.strings")
|
local cc_strings = require("cc.strings")
|
||||||
|
|
||||||
|
local const = require("scada-common.constants")
|
||||||
|
|
||||||
local math = math
|
local math = math
|
||||||
local string = string
|
local string = string
|
||||||
local table = table
|
local table = table
|
||||||
@@ -22,7 +24,7 @@ local t_pack = table.pack
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
-- scada-common version
|
-- scada-common version
|
||||||
util.version = "1.3.0"
|
util.version = "1.4.2"
|
||||||
|
|
||||||
util.TICK_TIME_S = 0.05
|
util.TICK_TIME_S = 0.05
|
||||||
util.TICK_TIME_MS = 50
|
util.TICK_TIME_MS = 50
|
||||||
@@ -111,9 +113,19 @@ end
|
|||||||
-- wrap a string into a table of lines
|
-- wrap a string into a table of lines
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param str string
|
---@param str string
|
||||||
---@param limit integer line limit
|
---@param limit integer line limit, must be greater than 0
|
||||||
---@return table lines
|
---@return table lines
|
||||||
function util.strwrap(str, limit) return cc_strings.wrap(str, limit) end
|
function util.strwrap(str, limit)
|
||||||
|
assert(limit > 0, "util.strwrap() limit not greater than 0")
|
||||||
|
return cc_strings.wrap(str, limit)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- make sure a string is at least 'width' long
|
||||||
|
---@nodiscard
|
||||||
|
---@param str string
|
||||||
|
---@param width integer minimum width
|
||||||
|
---@return string string
|
||||||
|
function util.strminw(str, width) return cc_strings.ensure_width(str, width) end
|
||||||
|
|
||||||
-- concatenation with built-in to string
|
-- concatenation with built-in to string
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -368,72 +380,86 @@ end
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region MEKANISM POWER
|
--#region MEKANISM MATH
|
||||||
|
|
||||||
-- convert Joules to FE
|
-- convert Joules to FE (or RF)
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param J number Joules
|
---@param J number Joules
|
||||||
---@return number FE Forge Energy
|
---@return number FE Forge Energy or Redstone Flux
|
||||||
function util.joules_to_fe(J) return (J * 0.4) end
|
function util.joules_to_fe_rf(J) return (J * 0.4) end
|
||||||
|
|
||||||
-- convert FE to Joules
|
-- convert FE (or RF) to Joules
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param FE number Forge Energy
|
---@param FE number Forge Energy or Redstone Flux
|
||||||
---@return number J Joules
|
---@return number J Joules
|
||||||
function util.fe_to_joules(FE) return (FE * 2.5) end
|
function util.fe_rf_to_joules(FE) return (FE * 2.5) end
|
||||||
|
|
||||||
local function kFE(fe) return fe / 1000.0 end
|
-- format a power value into XXX.XX UNIT format<br>
|
||||||
local function MFE(fe) return fe / 1000000.0 end
|
-- example for FE: FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE
|
||||||
local function GFE(fe) return fe / 1000000000.0 end
|
|
||||||
local function TFE(fe) return fe / 1000000000000.0 end
|
|
||||||
local function PFE(fe) return fe / 1000000000000000.0 end
|
|
||||||
local function EFE(fe) return fe / 1000000000000000000.0 end -- if you accomplish this please touch grass
|
|
||||||
local function ZFE(fe) return fe / 1000000000000000000000.0 end -- how & why did you do this?
|
|
||||||
|
|
||||||
-- format a power value into XXX.XX UNIT format (FE, kFE, MFE, GFE, TFE, PFE, EFE, ZFE)
|
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param fe number forge energy value
|
---@param e number energy value
|
||||||
|
---@param label string energy scale label
|
||||||
---@param combine_label? boolean if a label should be included in the string itself
|
---@param combine_label? boolean if a label should be included in the string itself
|
||||||
---@param format? string format override
|
---@param format? string format override
|
||||||
---@return string str, string? unit
|
---@return string str, string unit
|
||||||
function util.power_format(fe, combine_label, format)
|
function util.power_format(e, label, combine_label, format)
|
||||||
local unit, value
|
local unit, value
|
||||||
|
|
||||||
if type(format) ~= "string" then format = "%.2f" end
|
if type(format) ~= "string" then format = "%.2f" end
|
||||||
|
|
||||||
if fe < 1000.0 then
|
if e < 1000.0 then
|
||||||
unit = "FE"
|
unit = ""
|
||||||
value = fe
|
value = e
|
||||||
elseif fe < 1000000.0 then
|
elseif e < 1000000.0 then
|
||||||
unit = "kFE"
|
unit = "k"
|
||||||
value = kFE(fe)
|
value = e / 1000.0
|
||||||
elseif fe < 1000000000.0 then
|
elseif e < 1000000000.0 then
|
||||||
unit = "MFE"
|
unit = "M"
|
||||||
value = MFE(fe)
|
value = e / 1000000.0
|
||||||
elseif fe < 1000000000000.0 then
|
elseif e < 1000000000000.0 then
|
||||||
unit = "GFE"
|
unit = "G"
|
||||||
value = GFE(fe)
|
value = e / 1000000000.0
|
||||||
elseif fe < 1000000000000000.0 then
|
elseif e < 1000000000000000.0 then
|
||||||
unit = "TFE"
|
unit = "T"
|
||||||
value = TFE(fe)
|
value = e / 1000000000000.0
|
||||||
elseif fe < 1000000000000000000.0 then
|
elseif e < 1000000000000000000.0 then
|
||||||
unit = "PFE"
|
unit = "P"
|
||||||
value = PFE(fe)
|
value = e / 1000000000000000.0
|
||||||
elseif fe < 1000000000000000000000.0 then
|
elseif e < 1000000000000000000000.0 then
|
||||||
unit = "EFE"
|
-- if you accomplish this please touch grass
|
||||||
value = EFE(fe)
|
unit = "E"
|
||||||
|
value = e / 1000000000000000000.0
|
||||||
else
|
else
|
||||||
unit = "ZFE"
|
-- how & why did you do this?
|
||||||
value = ZFE(fe)
|
unit = "Z"
|
||||||
|
value = e / 1000000000000000000000.0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unit = unit .. label
|
||||||
|
|
||||||
if combine_label then
|
if combine_label then
|
||||||
return util.sprintf(util.c(format, " %s"), value, unit)
|
return util.sprintf(util.c(format, " %s"), value, unit), unit
|
||||||
else
|
else
|
||||||
return util.sprintf(format, value), unit
|
return util.sprintf(format, value), unit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- compute Mekanism's rotation rate for a turbine
|
||||||
|
---@nodiscard
|
||||||
|
---@param turbine turbinev_session_db turbine data
|
||||||
|
function util.turbine_rotation(turbine)
|
||||||
|
local build = turbine.build
|
||||||
|
|
||||||
|
local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK
|
||||||
|
local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol
|
||||||
|
local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW
|
||||||
|
|
||||||
|
local max_rate = math.min(disp_rate, vent_rate)
|
||||||
|
local flow = math.min(max_rate, turbine.tanks.steam.amount)
|
||||||
|
|
||||||
|
return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate
|
||||||
|
end
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region UTILITY CLASSES
|
--#region UTILITY CLASSES
|
||||||
|
|||||||
26
startup.lua
26
startup.lua
@@ -1,35 +1,31 @@
|
|||||||
local util = require("scada-common.util")
|
local BOOTLOADER_VERSION = "1.1"
|
||||||
|
|
||||||
local println = util.println
|
print("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
||||||
|
print("BOOT> SCANNING FOR APPLICATIONS...")
|
||||||
local BOOTLOADER_VERSION = "1.0"
|
|
||||||
|
|
||||||
println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
|
||||||
println("BOOT> SCANNING FOR APPLICATIONS...")
|
|
||||||
|
|
||||||
local exit_code
|
local exit_code
|
||||||
|
|
||||||
if fs.exists("reactor-plc/startup.lua") then
|
if fs.exists("reactor-plc/startup.lua") then
|
||||||
println("BOOT> EXEC REACTOR PLC STARTUP")
|
print("BOOT> EXEC REACTOR PLC STARTUP")
|
||||||
exit_code = shell.execute("reactor-plc/startup")
|
exit_code = shell.execute("reactor-plc/startup")
|
||||||
elseif fs.exists("rtu/startup.lua") then
|
elseif fs.exists("rtu/startup.lua") then
|
||||||
println("BOOT> EXEC RTU STARTUP")
|
print("BOOT> EXEC RTU STARTUP")
|
||||||
exit_code = shell.execute("rtu/startup")
|
exit_code = shell.execute("rtu/startup")
|
||||||
elseif fs.exists("supervisor/startup.lua") then
|
elseif fs.exists("supervisor/startup.lua") then
|
||||||
println("BOOT> EXEC SUPERVISOR STARTUP")
|
print("BOOT> EXEC SUPERVISOR STARTUP")
|
||||||
exit_code = shell.execute("supervisor/startup")
|
exit_code = shell.execute("supervisor/startup")
|
||||||
elseif fs.exists("coordinator/startup.lua") then
|
elseif fs.exists("coordinator/startup.lua") then
|
||||||
println("BOOT> EXEC COORDINATOR STARTUP")
|
print("BOOT> EXEC COORDINATOR STARTUP")
|
||||||
exit_code = shell.execute("coordinator/startup")
|
exit_code = shell.execute("coordinator/startup")
|
||||||
elseif fs.exists("pocket/startup.lua") then
|
elseif fs.exists("pocket/startup.lua") then
|
||||||
println("BOOT> EXEC POCKET STARTUP")
|
print("BOOT> EXEC POCKET STARTUP")
|
||||||
exit_code = shell.execute("pocket/startup")
|
exit_code = shell.execute("pocket/startup")
|
||||||
else
|
else
|
||||||
println("BOOT> NO SCADA STARTUP FOUND")
|
print("BOOT> NO SCADA STARTUP FOUND")
|
||||||
println("BOOT> EXIT")
|
print("BOOT> EXIT")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not exit_code then println("BOOT> APPLICATION CRASHED") end
|
if not exit_code then print("BOOT> APPLICATION CRASHED") end
|
||||||
|
|
||||||
return exit_code
|
return exit_code
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ local function config_view(display)
|
|||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local function exit() os.queueEvent("terminate") end
|
local function exit() os.queueEvent("terminate") end
|
||||||
|
|
||||||
TextBox{parent=display,y=1,text="Supervisor Configurator",alignment=CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=display,y=1,text="Supervisor Configurator",alignment=CENTER,fg_bg=style.header}
|
||||||
|
|
||||||
local root_pane_div = Div{parent=display,x=1,y=2}
|
local root_pane_div = Div{parent=display,x=1,y=2}
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Supervisor configurator! Please select one of the following options."}
|
TextBox{parent=main_page,x=2,y=2,height=2,text="Welcome to the Supervisor configurator! Please select one of the following options."}
|
||||||
|
|
||||||
if tool_ctl.ask_config then
|
if tool_ctl.ask_config then
|
||||||
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text="Notice: This device had no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
|
||||||
y_start = y_start + 5
|
y_start = y_start + 5
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -228,13 +228,13 @@ local function config_view(display)
|
|||||||
|
|
||||||
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6,svr_c_7}}
|
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6,svr_c_7}}
|
||||||
|
|
||||||
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=svr_cfg,x=1,y=2,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
TextBox{parent=svr_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
TextBox{parent=svr_c_1,x=1,y=1,height=3,text="Please enter the number of reactors you have, also referred to as reactor units or 'units' for short. A maximum of 4 is currently supported."}
|
||||||
local num_units = NumberField{parent=svr_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
local num_units = NumberField{parent=svr_c_1,x=1,y=5,width=5,max_chars=2,default=ini_cfg.UnitCount,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=svr_c_1,x=7,y=5,height=1,text="reactors"}
|
TextBox{parent=svr_c_1,x=7,y=5,text="reactors"}
|
||||||
|
|
||||||
local nu_error = TextBox{parent=svr_c_1,x=8,y=14,height=1,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local nu_error = TextBox{parent=svr_c_1,x=8,y=14,width=35,text="Please set the number of reactors.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_num_units()
|
local function submit_num_units()
|
||||||
local count = tonumber(num_units.get_value())
|
local count = tonumber(num_units.get_value())
|
||||||
@@ -255,7 +255,7 @@ local function config_view(display)
|
|||||||
PushButton{parent=svr_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_1,x=44,y=14,text="Next \x1a",callback=submit_num_units,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=svr_c_2,x=1,y=1,height=4,text="Please provide the reactor cooling configuration below. This includes the number of turbines, boilers, and if that reactor has a connection to a dynamic tank for emergency coolant."}
|
TextBox{parent=svr_c_2,x=1,y=1,height=4,text="Please provide the reactor cooling configuration below. This includes the number of turbines, boilers, and if that reactor has a connection to a dynamic tank for emergency coolant."}
|
||||||
TextBox{parent=svr_c_2,x=1,y=6,height=1,text="UNIT TURBINES BOILERS HAS TANK CONNECTION?",fg_bg=g_lg_fg_bg}
|
TextBox{parent=svr_c_2,x=1,y=6,text="UNIT TURBINES BOILERS HAS TANK CONNECTION?",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
local num_t, num_b, has_t = 1, 0, false
|
local num_t, num_b, has_t = 1, 0, false
|
||||||
@@ -277,7 +277,7 @@ local function config_view(display)
|
|||||||
tool_ctl.cooling_elems[i] = { line = line, turbines = turbines, boilers = boilers, tank = tank }
|
tool_ctl.cooling_elems[i] = { line = line, turbines = turbines, boilers = boilers, tank = tank }
|
||||||
end
|
end
|
||||||
|
|
||||||
local cool_err = TextBox{parent=svr_c_2,x=8,y=14,height=1,width=33,text="Please fill out all fields.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local cool_err = TextBox{parent=svr_c_2,x=8,y=14,width=33,text="Please fill out all fields.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_cooling()
|
local function submit_cooling()
|
||||||
local any_missing = false
|
local any_missing = false
|
||||||
@@ -345,15 +345,15 @@ local function config_view(display)
|
|||||||
local val = math.max(1, ini_cfg.FacilityTankDefs[i] or 2)
|
local val = math.max(1, ini_cfg.FacilityTankDefs[i] or 2)
|
||||||
local div = Div{parent=svr_c_4,x=1,y=3+(2*i),height=2}
|
local div = Div{parent=svr_c_4,x=1,y=3+(2*i),height=2}
|
||||||
|
|
||||||
TextBox{parent=div,x=1,y=1,width=33,height=1,text="Unit "..i.." will be connected to..."}
|
TextBox{parent=div,x=1,y=1,width=33,text="Unit "..i.." will be connected to..."}
|
||||||
TextBox{parent=div,x=6,y=2,width=3,height=1,text="..."}
|
TextBox{parent=div,x=6,y=2,width=3,text="..."}
|
||||||
local tank_opt = Radio2D{parent=div,x=9,y=2,rows=1,columns=2,default=val,options={"its own Unit Tank","a Facility Tank"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
local tank_opt = Radio2D{parent=div,x=9,y=2,rows=1,columns=2,default=val,options={"its own Unit Tank","a Facility Tank"},radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
|
||||||
local no_tank = TextBox{parent=div,x=9,y=2,width=34,height=1,text="no tank (as you set two steps ago)",fg_bg=cpair(colors.gray,colors.lightGray),hidden=true}
|
local no_tank = TextBox{parent=div,x=9,y=2,width=34,text="no tank (as you set two steps ago)",fg_bg=cpair(colors.gray,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
tool_ctl.tank_elems[i] = { div = div, tank_opt = tank_opt, no_tank = no_tank }
|
tool_ctl.tank_elems[i] = { div = div, tank_opt = tank_opt, no_tank = no_tank }
|
||||||
end
|
end
|
||||||
|
|
||||||
local tank_err = TextBox{parent=svr_c_4,x=8,y=14,height=1,width=33,text="You selected no facility tanks.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local tank_err = TextBox{parent=svr_c_4,x=8,y=14,width=33,text="You selected no facility tanks.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function hide_fconn(i)
|
local function hide_fconn(i)
|
||||||
if i > 1 then tool_ctl.vis_ftanks[i].pipe_conn.hide(true)
|
if i > 1 then tool_ctl.vis_ftanks[i].pipe_conn.hide(true)
|
||||||
@@ -402,8 +402,8 @@ local function config_view(display)
|
|||||||
PushButton{parent=svr_c_4,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_4,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=svr_c_4,x=44,y=14,text="Next \x1a",callback=submit_tank_defs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_4,x=44,y=14,text="Next \x1a",callback=submit_tank_defs,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=svr_c_5,x=1,y=1,height=1,text="Please select your dynamic tank layout."}
|
TextBox{parent=svr_c_5,x=1,y=1,text="Please select your dynamic tank layout."}
|
||||||
TextBox{parent=svr_c_5,x=12,y=3,height=1,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg}
|
TextBox{parent=svr_c_5,x=12,y=3,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
--#region Tank Layout Visualizer
|
--#region Tank Layout Visualizer
|
||||||
|
|
||||||
@@ -416,25 +416,25 @@ local function config_view(display)
|
|||||||
-- draw unit tanks and their pipes
|
-- draw unit tanks and their pipes
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
local line = Div{parent=vis,x=22,y=(i*2)-1,width=13,height=1}
|
local line = Div{parent=vis,x=22,y=(i*2)-1,width=13,height=1}
|
||||||
TextBox{parent=line,width=5,height=1,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
TextBox{parent=line,width=5,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
||||||
local label = TextBox{parent=line,x=7,y=1,width=7,height=1,text="Tank ?"}
|
local label = TextBox{parent=line,x=7,y=1,width=7,text="Tank ?"}
|
||||||
tool_ctl.vis_utanks[i] = { line = line, label = label }
|
tool_ctl.vis_utanks[i] = { line = line, label = label }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- draw facility tank connections
|
-- draw facility tank connections
|
||||||
|
|
||||||
local ftank_1 = Div{parent=vis,x=1,y=1,width=13,height=1}
|
local ftank_1 = Div{parent=vis,x=1,y=1,width=13,height=1}
|
||||||
TextBox{parent=ftank_1,width=7,height=1,text="Tank F1"}
|
TextBox{parent=ftank_1,width=7,text="Tank F1"}
|
||||||
tool_ctl.vis_ftanks[1] = {
|
tool_ctl.vis_ftanks[1] = {
|
||||||
line = ftank_1, pipe_direct = TextBox{parent=ftank_1,x=9,y=1,width=5,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
line = ftank_1, pipe_direct = TextBox{parent=ftank_1,x=9,y=1,width=5,text=string.rep("\x8c",5),fg_bg=pipe_cpair}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i = 2, 4 do
|
for i = 2, 4 do
|
||||||
local line = Div{parent=vis,x=1,y=(i-1)*2,width=13,height=2}
|
local line = Div{parent=vis,x=1,y=(i-1)*2,width=13,height=2}
|
||||||
local pipe_conn = TextBox{parent=line,x=13,y=2,width=1,height=1,text="\x8c",fg_bg=pipe_cpair}
|
local pipe_conn = TextBox{parent=line,x=13,y=2,width=1,text="\x8c",fg_bg=pipe_cpair}
|
||||||
local pipe_chain = TextBox{parent=line,x=12,y=1,width=1,height=2,text="\x95\n\x8d",fg_bg=pipe_cpair}
|
local pipe_chain = TextBox{parent=line,x=12,y=1,width=1,height=2,text="\x95\n\x8d",fg_bg=pipe_cpair}
|
||||||
local pipe_direct = TextBox{parent=line,x=9,y=2,width=4,height=1,text="\x8c\x8c\x8c\x8c",fg_bg=pipe_cpair}
|
local pipe_direct = TextBox{parent=line,x=9,y=2,width=4,text="\x8c\x8c\x8c\x8c",fg_bg=pipe_cpair}
|
||||||
local label = TextBox{parent=line,x=1,y=2,width=7,height=1,text=""}
|
local label = TextBox{parent=line,x=1,y=2,width=7,text=""}
|
||||||
tool_ctl.vis_ftanks[i] = { line = line, pipe_conn = pipe_conn, pipe_chain = pipe_chain, pipe_direct = pipe_direct, label = label }
|
tool_ctl.vis_ftanks[i] = { line = line, pipe_conn = pipe_conn, pipe_chain = pipe_chain, pipe_direct = pipe_direct, label = label }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -453,15 +453,16 @@ local function config_view(display)
|
|||||||
|
|
||||||
vis_unit_list.set_value(u_text)
|
vis_unit_list.set_value(u_text)
|
||||||
|
|
||||||
|
local vis_ftanks = tool_ctl.vis_ftanks
|
||||||
local next_idx = 1
|
local next_idx = 1
|
||||||
|
|
||||||
if is_ft(1) then
|
if is_ft(1) then
|
||||||
next_idx = 2
|
next_idx = 2
|
||||||
|
|
||||||
if (mode == 1 and (is_ft(2) or is_ft(3) or is_ft(4))) or (mode == 2 and (is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 5) and is_ft(2)) then
|
if (mode == 1 and (is_ft(2) or is_ft(3) or is_ft(4))) or (mode == 2 and (is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 5) and is_ft(2)) then
|
||||||
tool_ctl.vis_ftanks[1].pipe_direct.set_value("\x8c\x8c\x8c\x9c\x8c")
|
vis_ftanks[1].pipe_direct.set_value("\x8c\x8c\x8c\x9c\x8c")
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[1].pipe_direct.set_value(string.rep("\x8c",5))
|
vis_ftanks[1].pipe_direct.set_value(string.rep("\x8c",5))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -469,108 +470,108 @@ local function config_view(display)
|
|||||||
local _2_46_need_chain = (mode == 4 and (is_ft(3) or is_ft(4))) or (mode == 6 and is_ft(3))
|
local _2_46_need_chain = (mode == 4 and (is_ft(3) or is_ft(4))) or (mode == 6 and is_ft(3))
|
||||||
|
|
||||||
if is_ft(2) then
|
if is_ft(2) then
|
||||||
tool_ctl.vis_ftanks[2].label.set_value("Tank F" .. next_idx)
|
vis_ftanks[2].label.set_value("Tank F" .. next_idx)
|
||||||
|
|
||||||
if (mode < 4 or mode == 5) and is_ft(1) then
|
if (mode < 4 or mode == 5) and is_ft(1) then
|
||||||
tool_ctl.vis_ftanks[2].label.hide(true)
|
vis_ftanks[2].label.hide(true)
|
||||||
tool_ctl.vis_ftanks[2].pipe_direct.hide(true)
|
vis_ftanks[2].pipe_direct.hide(true)
|
||||||
if _2_12_need_passt then
|
if _2_12_need_passt then
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x9d")
|
vis_ftanks[2].pipe_chain.set_value("\x95\n\x9d")
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x8d")
|
vis_ftanks[2].pipe_chain.set_value("\x95\n\x8d")
|
||||||
end
|
end
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.show()
|
vis_ftanks[2].pipe_chain.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[2].label.show()
|
vis_ftanks[2].label.show()
|
||||||
next_idx = next_idx + 1
|
next_idx = next_idx + 1
|
||||||
|
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.hide(true)
|
vis_ftanks[2].pipe_chain.hide(true)
|
||||||
if _2_12_need_passt or _2_46_need_chain then
|
if _2_12_need_passt or _2_46_need_chain then
|
||||||
tool_ctl.vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x9c")
|
vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x9c")
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
vis_ftanks[2].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
||||||
end
|
end
|
||||||
tool_ctl.vis_ftanks[2].pipe_direct.show()
|
vis_ftanks[2].pipe_direct.show()
|
||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.vis_ftanks[2].line.show()
|
vis_ftanks[2].line.show()
|
||||||
elseif is_ft(1) and _2_12_need_passt then
|
elseif is_ft(1) and _2_12_need_passt then
|
||||||
tool_ctl.vis_ftanks[2].label.hide(true)
|
vis_ftanks[2].label.hide(true)
|
||||||
tool_ctl.vis_ftanks[2].pipe_direct.hide(true)
|
vis_ftanks[2].pipe_direct.hide(true)
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.set_value("\x95\n\x95")
|
vis_ftanks[2].pipe_chain.set_value("\x95\n\x95")
|
||||||
tool_ctl.vis_ftanks[2].pipe_chain.show()
|
vis_ftanks[2].pipe_chain.show()
|
||||||
tool_ctl.vis_ftanks[2].line.show()
|
vis_ftanks[2].line.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[2].line.hide(true)
|
vis_ftanks[2].line.hide(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_ft(3) then
|
if is_ft(3) then
|
||||||
tool_ctl.vis_ftanks[3].label.set_value("Tank F" .. next_idx)
|
vis_ftanks[3].label.set_value("Tank F" .. next_idx)
|
||||||
|
|
||||||
if (mode < 3 and (is_ft(1) or is_ft(2))) or ((mode == 4 or mode == 6) and is_ft(2)) then
|
if (mode < 3 and (is_ft(1) or is_ft(2))) or ((mode == 4 or mode == 6) and is_ft(2)) then
|
||||||
tool_ctl.vis_ftanks[3].label.hide(true)
|
vis_ftanks[3].label.hide(true)
|
||||||
tool_ctl.vis_ftanks[3].pipe_direct.hide(true)
|
vis_ftanks[3].pipe_direct.hide(true)
|
||||||
if (mode == 1 or mode == 4) and is_ft(4) then
|
if (mode == 1 or mode == 4) and is_ft(4) then
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x9d")
|
vis_ftanks[3].pipe_chain.set_value("\x95\n\x9d")
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x8d")
|
vis_ftanks[3].pipe_chain.set_value("\x95\n\x8d")
|
||||||
end
|
end
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.show()
|
vis_ftanks[3].pipe_chain.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[3].label.show()
|
vis_ftanks[3].label.show()
|
||||||
next_idx = next_idx + 1
|
next_idx = next_idx + 1
|
||||||
|
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.hide(true)
|
vis_ftanks[3].pipe_chain.hide(true)
|
||||||
if (mode == 1 or mode == 3 or mode == 4 or mode == 7) and is_ft(4) then
|
if (mode == 1 or mode == 3 or mode == 4 or mode == 7) and is_ft(4) then
|
||||||
tool_ctl.vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x9c")
|
vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x9c")
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
vis_ftanks[3].pipe_direct.set_value("\x8c\x8c\x8c\x8c")
|
||||||
end
|
end
|
||||||
tool_ctl.vis_ftanks[3].pipe_direct.show()
|
vis_ftanks[3].pipe_direct.show()
|
||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.vis_ftanks[3].line.show()
|
vis_ftanks[3].line.show()
|
||||||
elseif (mode == 1 and is_ft(4) and (is_ft(1) or is_ft(2))) or (mode == 4 and is_ft(2) and is_ft(4)) then
|
elseif (mode == 1 and is_ft(4) and (is_ft(1) or is_ft(2))) or (mode == 4 and is_ft(2) and is_ft(4)) then
|
||||||
tool_ctl.vis_ftanks[3].label.hide(true)
|
vis_ftanks[3].label.hide(true)
|
||||||
tool_ctl.vis_ftanks[3].pipe_direct.hide(true)
|
vis_ftanks[3].pipe_direct.hide(true)
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.set_value("\x95\n\x95")
|
vis_ftanks[3].pipe_chain.set_value("\x95\n\x95")
|
||||||
tool_ctl.vis_ftanks[3].pipe_chain.show()
|
vis_ftanks[3].pipe_chain.show()
|
||||||
tool_ctl.vis_ftanks[3].line.show()
|
vis_ftanks[3].line.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[3].line.hide(true)
|
vis_ftanks[3].line.hide(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_ft(4) then
|
if is_ft(4) then
|
||||||
tool_ctl.vis_ftanks[4].label.set_value("Tank F" .. next_idx)
|
vis_ftanks[4].label.set_value("Tank F" .. next_idx)
|
||||||
|
|
||||||
if (mode == 1 and (is_ft(1) or is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 7) and is_ft(3)) or (mode == 4 and (is_ft(2) or is_ft(3))) then
|
if (mode == 1 and (is_ft(1) or is_ft(2) or is_ft(3))) or ((mode == 3 or mode == 7) and is_ft(3)) or (mode == 4 and (is_ft(2) or is_ft(3))) then
|
||||||
tool_ctl.vis_ftanks[4].label.hide(true)
|
vis_ftanks[4].label.hide(true)
|
||||||
tool_ctl.vis_ftanks[4].pipe_direct.hide(true)
|
vis_ftanks[4].pipe_direct.hide(true)
|
||||||
tool_ctl.vis_ftanks[4].pipe_chain.show()
|
vis_ftanks[4].pipe_chain.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[4].label.show()
|
vis_ftanks[4].label.show()
|
||||||
tool_ctl.vis_ftanks[4].pipe_chain.hide(true)
|
vis_ftanks[4].pipe_chain.hide(true)
|
||||||
tool_ctl.vis_ftanks[4].pipe_direct.show()
|
vis_ftanks[4].pipe_direct.show()
|
||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.vis_ftanks[4].line.show()
|
vis_ftanks[4].line.show()
|
||||||
else
|
else
|
||||||
tool_ctl.vis_ftanks[4].line.hide(true)
|
vis_ftanks[4].line.hide(true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function change_mode(mode)
|
||||||
|
tmp_cfg.FacilityTankMode = mode
|
||||||
|
tool_ctl.vis_draw(mode)
|
||||||
|
end
|
||||||
|
|
||||||
local tank_modes = { "Mode 1", "Mode 2", "Mode 3", "Mode 4", "Mode 5", "Mode 6", "Mode 7", "Mode 8" }
|
local tank_modes = { "Mode 1", "Mode 2", "Mode 3", "Mode 4", "Mode 5", "Mode 6", "Mode 7", "Mode 8" }
|
||||||
local tank_mode = RadioButton{parent=svr_c_5,x=1,y=4,callback=tool_ctl.vis_draw,default=math.max(1,ini_cfg.FacilityTankMode),options=tank_modes,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow}
|
local tank_mode = RadioButton{parent=svr_c_5,x=1,y=4,callback=change_mode,default=math.max(1,ini_cfg.FacilityTankMode),options=tank_modes,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.yellow}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
local function submit_mode()
|
|
||||||
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
|
||||||
svr_pane.set_value(7)
|
|
||||||
end
|
|
||||||
|
|
||||||
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=svr_c_5,x=44,y=14,text="Next \x1a",callback=submit_mode,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_5,x=44,y=14,text="Next \x1a",callback=function()svr_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
PushButton{parent=svr_c_5,x=8,y=14,min_width=7,text="About",callback=function()svr_pane.set_value(6)end,fg_bg=cpair(colors.black,colors.lightBlue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_5,x=8,y=14,min_width=7,text="About",callback=function()svr_pane.set_value(6)end,fg_bg=cpair(colors.black,colors.lightBlue),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -608,32 +609,32 @@ local function config_view(display)
|
|||||||
|
|
||||||
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
local net_pane = MultiPane{parent=net_cfg,x=1,y=4,panes={net_c_1,net_c_2,net_c_3,net_c_4}}
|
||||||
|
|
||||||
TextBox{parent=net_cfg,x=1,y=2,height=1,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
TextBox{parent=net_cfg,x=1,y=2,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=1,height=1,text="Please set the network channels below."}
|
TextBox{parent=net_c_1,x=1,y=1,text="Please set the network channels below."}
|
||||||
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=1,y=3,height=4,text="Each of the 5 uniquely named channels must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=8,height=1,width=18,text="Supervisor Channel"}
|
TextBox{parent=net_c_1,x=1,y=8,width=18,text="Supervisor Channel"}
|
||||||
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local svr_chan = NumberField{parent=net_c_1,x=21,y=8,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=8,height=4,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=9,height=1,width=11,text="PLC Channel"}
|
TextBox{parent=net_c_1,x=1,y=9,width=11,text="PLC Channel"}
|
||||||
local plc_chan = NumberField{parent=net_c_1,x=21,y=9,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local plc_chan = NumberField{parent=net_c_1,x=21,y=9,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=9,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=9,height=4,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=10,height=1,width=19,text="RTU Gateway Channel"}
|
TextBox{parent=net_c_1,x=1,y=10,width=19,text="RTU Gateway Channel"}
|
||||||
local rtu_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.RTU_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local rtu_chan = NumberField{parent=net_c_1,x=21,y=10,width=7,default=ini_cfg.RTU_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[RTU_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=10,height=4,text="[RTU_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=11,height=1,width=19,text="Coordinator Channel"}
|
TextBox{parent=net_c_1,x=1,y=11,width=19,text="Coordinator Channel"}
|
||||||
local crd_chan = NumberField{parent=net_c_1,x=21,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local crd_chan = NumberField{parent=net_c_1,x=21,y=11,width=7,default=ini_cfg.CRD_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=11,height=4,text="[CRD_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_1,x=1,y=12,height=1,width=14,text="Pocket Channel"}
|
TextBox{parent=net_c_1,x=1,y=12,width=14,text="Pocket Channel"}
|
||||||
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
local pkt_chan = NumberField{parent=net_c_1,x=21,y=12,width=7,default=ini_cfg.PKT_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
|
||||||
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_1,x=29,y=12,height=4,text="[PKT_CHANNEL]",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local chan_err = TextBox{parent=net_c_1,x=8,y=14,width=35,text="Please set all channels.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_channels()
|
local function submit_channels()
|
||||||
local svr_c, plc_c, rtu_c = tonumber(svr_chan.get_value()), tonumber(plc_chan.get_value()), tonumber(rtu_chan.get_value())
|
local svr_c, plc_c, rtu_c = tonumber(svr_chan.get_value()), tonumber(plc_chan.get_value()), tonumber(rtu_chan.get_value())
|
||||||
@@ -649,24 +650,24 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=1,y=14,text="\x1b Back",callback=function()main_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Please set the connection timeouts below."}
|
TextBox{parent=net_c_2,x=1,y=1,text="Please set the connection timeouts below."}
|
||||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally should not need to modify these. On slow servers, you can try to increase this to make the system wait longer before assuming a disconnection. The default for all is 5 seconds.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=8,height=1,width=11,text="PLC Timeout"}
|
TextBox{parent=net_c_2,x=1,y=8,width=11,text="PLC Timeout"}
|
||||||
local plc_timeout = NumberField{parent=net_c_2,x=21,y=8,width=7,default=ini_cfg.PLC_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local plc_timeout = NumberField{parent=net_c_2,x=21,y=8,width=7,default=ini_cfg.PLC_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=9,height=1,width=19,text="RTU Gateway Timeout"}
|
TextBox{parent=net_c_2,x=1,y=9,width=19,text="RTU Gateway Timeout"}
|
||||||
local rtu_timeout = NumberField{parent=net_c_2,x=21,y=9,width=7,default=ini_cfg.RTU_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local rtu_timeout = NumberField{parent=net_c_2,x=21,y=9,width=7,default=ini_cfg.RTU_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=10,height=1,width=19,text="Coordinator Timeout"}
|
TextBox{parent=net_c_2,x=1,y=10,width=19,text="Coordinator Timeout"}
|
||||||
local crd_timeout = NumberField{parent=net_c_2,x=21,y=10,width=7,default=ini_cfg.CRD_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local crd_timeout = NumberField{parent=net_c_2,x=21,y=10,width=7,default=ini_cfg.CRD_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=1,y=11,height=1,width=14,text="Pocket Timeout"}
|
TextBox{parent=net_c_2,x=1,y=11,width=14,text="Pocket Timeout"}
|
||||||
local pkt_timeout = NumberField{parent=net_c_2,x=21,y=11,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
local pkt_timeout = NumberField{parent=net_c_2,x=21,y=11,width=7,default=ini_cfg.PKT_Timeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_2,x=29,y=8,height=4,width=7,text="seconds\nseconds\nseconds\nseconds",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_2,x=29,y=8,height=4,width=7,text="seconds\nseconds\nseconds\nseconds",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local ct_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local ct_err = TextBox{parent=net_c_2,x=8,y=14,width=35,text="Please set all connection timeouts.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_timeouts()
|
local function submit_timeouts()
|
||||||
local plc_cto, rtu_cto, crd_cto, pkt_cto = tonumber(plc_timeout.get_value()), tonumber(rtu_timeout.get_value()), tonumber(crd_timeout.get_value()), tonumber(pkt_timeout.get_value())
|
local plc_cto, rtu_cto, crd_cto, pkt_cto = tonumber(plc_timeout.get_value()), tonumber(rtu_timeout.get_value()), tonumber(crd_timeout.get_value()), tonumber(pkt_timeout.get_value())
|
||||||
@@ -680,13 +681,13 @@ local function config_view(display)
|
|||||||
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=1,y=14,text="\x1b Back",callback=function()net_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=net_c_2,x=44,y=14,text="Next \x1a",callback=submit_timeouts,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_3,x=1,y=1,height=1,text="Please set the trusted range below."}
|
TextBox{parent=net_c_3,x=1,y=1,text="Please set the trusted range below."}
|
||||||
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=3,height=3,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||||
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_3,x=1,y=7,height=2,text="This is optional. You can disable this functionality by setting the value to 0.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
local range = NumberField{parent=net_c_3,x=1,y=10,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local tr_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local tr_err = TextBox{parent=net_c_3,x=8,y=14,width=35,text="Please set the trusted range.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_tr()
|
local function submit_tr()
|
||||||
local range_val = tonumber(range.get_value())
|
local range_val = tonumber(range.get_value())
|
||||||
@@ -703,7 +704,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
TextBox{parent=net_c_4,x=1,y=1,height=2,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
|
||||||
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
TextBox{parent=net_c_4,x=1,y=4,height=6,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=net_c_4,x=1,y=11,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_4,x=1,y=11,text="Facility Auth Key"}
|
||||||
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
local key, _, censor = TextField{parent=net_c_4,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||||
@@ -713,7 +714,7 @@ local function config_view(display)
|
|||||||
hide_key.set_value(true)
|
hide_key.set_value(true)
|
||||||
censor_key(true)
|
censor_key(true)
|
||||||
|
|
||||||
local key_err = TextBox{parent=net_c_4,x=8,y=14,height=1,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local key_err = TextBox{parent=net_c_4,x=8,y=14,width=35,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_auth()
|
local function submit_auth()
|
||||||
local v = key.get_value()
|
local v = key.get_value()
|
||||||
@@ -733,20 +734,20 @@ local function config_view(display)
|
|||||||
|
|
||||||
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
local log_c_1 = Div{parent=log_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=log_cfg,x=1,y=2,height=1,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
TextBox{parent=log_cfg,x=1,y=2,text=" Logging Configuration",fg_bg=cpair(colors.black,colors.pink)}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=1,height=1,text="Please configure logging below."}
|
TextBox{parent=log_c_1,x=1,y=1,text="Please configure logging below."}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=3,height=1,text="Log File Mode"}
|
TextBox{parent=log_c_1,x=1,y=3,text="Log File Mode"}
|
||||||
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
|
||||||
|
|
||||||
TextBox{parent=log_c_1,x=1,y=7,height=1,text="Log File Path"}
|
TextBox{parent=log_c_1,x=1,y=7,text="Log File Path"}
|
||||||
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
|
||||||
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=log_c_1,x=3,y=11,height=2,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
local path_err = TextBox{parent=log_c_1,x=8,y=14,width=35,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||||
|
|
||||||
local function submit_log()
|
local function submit_log()
|
||||||
if path.get_value() ~= "" then
|
if path.get_value() ~= "" then
|
||||||
@@ -774,17 +775,17 @@ local function config_view(display)
|
|||||||
|
|
||||||
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
TextBox{parent=clr_cfg,x=1,y=2,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
TextBox{parent=clr_c_1,x=1,y=7,text="Front Panel Theme"}
|
||||||
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
TextBox{parent=clr_c_2,x=21,y=7,text="Preview"}
|
||||||
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
@@ -811,7 +812,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
TextBox{parent=clr_c_2,x=1,y=7,width=10,text="Color Mode"}
|
||||||
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
@@ -865,7 +866,7 @@ local function config_view(display)
|
|||||||
clr_pane.set_value(1)
|
clr_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=clr_c_3,x=1,y=1,text="Settings saved!"}
|
||||||
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
PushButton{parent=clr_c_3,x=1,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
||||||
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -884,7 +885,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
local sum_pane = MultiPane{parent=summary,x=1,y=4,panes={sum_c_1,sum_c_2,sum_c_3,sum_c_4}}
|
||||||
|
|
||||||
TextBox{parent=summary,x=1,y=2,height=1,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
TextBox{parent=summary,x=1,y=2,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
|
||||||
|
|
||||||
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
@@ -961,7 +962,7 @@ local function config_view(display)
|
|||||||
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.show_key_btn = PushButton{parent=sum_c_1,x=8,y=14,min_width=17,text="Unhide Auth Key",callback=function()tool_ctl.show_auth_key()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
tool_ctl.settings_apply = PushButton{parent=sum_c_1,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
TextBox{parent=sum_c_2,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=sum_c_2,x=1,y=1,text="Settings saved!"}
|
||||||
|
|
||||||
local function go_home()
|
local function go_home()
|
||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
@@ -994,15 +995,15 @@ local function config_view(display)
|
|||||||
|
|
||||||
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
local cl = Div{parent=changelog,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=changelog,x=1,y=2,height=1,text=" Config Change Log",fg_bg=bw_fg_bg}
|
TextBox{parent=changelog,x=1,y=2,text=" Config Change Log",fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=49,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
|
||||||
|
|
||||||
for _, change in ipairs(changes) do
|
for _, change in ipairs(changes) do
|
||||||
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=c_log,text=change[1],fg_bg=bw_fg_bg}
|
||||||
for _, v in ipairs(change[2]) do
|
for _, v in ipairs(change[2]) do
|
||||||
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
|
||||||
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=1,text="- ",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1013,8 +1014,8 @@ local function config_view(display)
|
|||||||
|
|
||||||
local i_err = Div{parent=import_err,x=2,y=4,width=49}
|
local i_err = Div{parent=import_err,x=2,y=4,width=49}
|
||||||
|
|
||||||
TextBox{parent=import_err,x=1,y=2,height=1,text=" Import Error",fg_bg=cpair(colors.black,colors.red)}
|
TextBox{parent=import_err,x=1,y=2,text=" Import Error",fg_bg=cpair(colors.black,colors.red)}
|
||||||
TextBox{parent=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"}
|
TextBox{parent=i_err,x=1,y=1,text="There is a problem with your config.lua file:"}
|
||||||
|
|
||||||
local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""}
|
local import_err_msg = TextBox{parent=i_err,x=1,y=3,height=6,text=""}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA
|
|||||||
|
|
||||||
-- 7.14 kJ per blade for 1 mB of fissile fuel<br>
|
-- 7.14 kJ per blade for 1 mB of fissile fuel<br>
|
||||||
-- 2856 FE per blade per 1 mB, 285.6 FE per blade per 0.1 mB (minimum)
|
-- 2856 FE per blade per 1 mB, 285.6 FE per blade per 0.1 mB (minimum)
|
||||||
local POWER_PER_BLADE = util.joules_to_fe(7140)
|
local POWER_PER_BLADE = util.joules_to_fe_rf(7140)
|
||||||
|
|
||||||
local FLOW_STABILITY_DELAY_S = const.FLOW_STABILITY_DELAY_MS / 1000
|
local FLOW_STABILITY_DELAY_S = const.FLOW_STABILITY_DELAY_MS / 1000
|
||||||
|
|
||||||
@@ -332,9 +332,9 @@ function facility.new(config, cooling_conf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if has_data then
|
if has_data then
|
||||||
local energy = util.joules_to_fe(db.tanks.energy)
|
local energy = util.joules_to_fe_rf(db.tanks.energy)
|
||||||
local input = util.joules_to_fe(db.state.last_input)
|
local input = util.joules_to_fe_rf(db.state.last_input)
|
||||||
local output = util.joules_to_fe(db.state.last_output)
|
local output = util.joules_to_fe_rf(db.state.last_output)
|
||||||
|
|
||||||
if self.im_stat_init then
|
if self.im_stat_init then
|
||||||
self.avg_charge.record(energy, charge_update)
|
self.avg_charge.record(energy, charge_update)
|
||||||
@@ -1283,7 +1283,7 @@ function facility.new(config, cooling_conf)
|
|||||||
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
||||||
|
|
||||||
local fe_per_ms = self.avg_net.compute()
|
local fe_per_ms = self.avg_net.compute()
|
||||||
local remaining = util.joules_to_fe(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
local remaining = util.joules_to_fe_rf(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
||||||
status.power[4] = remaining / fe_per_ms
|
status.power[4] = remaining / fe_per_ms
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -31,18 +31,18 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local ps_prefix = "pdg_" .. id .. "_"
|
local ps_prefix = "pdg_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,fg_bg=s_hi_box}
|
||||||
local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,fg_bg=s_hi_box}
|
||||||
pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value)
|
pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=10,y=2,text="FW:",width=3}
|
||||||
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
||||||
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4}
|
||||||
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -31,22 +31,22 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local ps_prefix = "rtu_" .. id .. "_"
|
local ps_prefix = "rtu_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,fg_bg=s_hi_box}
|
||||||
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,fg_bg=s_hi_box}
|
||||||
rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value)
|
rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1}
|
TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7}
|
||||||
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.fp.label_d_fg}
|
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.fp.label_d_fg}
|
||||||
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=21,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=21,y=2,text="FW:",width=3}
|
||||||
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4}
|
||||||
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ local function init(panel)
|
|||||||
local label_fg = style.fp.label_fg
|
local label_fg = style.fp.label_fg
|
||||||
local label_d_fg = style.fp.label_d_fg
|
local label_d_fg = style.fp.label_d_fg
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
|
|
||||||
@@ -66,15 +66,15 @@ local function init(panel)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp.disabled_fg}
|
TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=style.fp.disabled_fg}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@@ -92,23 +92,23 @@ local function init(panel)
|
|||||||
local ps_prefix = "plc_" .. i .. "_"
|
local ps_prefix = "plc_" .. i .. "_"
|
||||||
local plc_entry = Div{parent=plc_list,height=3,fg_bg=s_hi_bright}
|
local plc_entry = Div{parent=plc_list,height=3,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=plc_entry,x=1,y=1,text="",width=8,fg_bg=s_hi_box}
|
||||||
TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=ALIGN.CENTER,width=8,fg_bg=s_hi_box}
|
||||||
TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
TextBox{parent=plc_entry,x=1,y=3,text="",width=8,fg_bg=s_hi_box}
|
||||||
|
|
||||||
local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=cpair(colors.green_hc,colors.green_off)}
|
local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=cpair(colors.green_hc,colors.green_off)}
|
||||||
conn.register(databus.ps, ps_prefix .. "conn", conn.update)
|
conn.register(databus.ps, ps_prefix .. "conn", conn.update)
|
||||||
|
|
||||||
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=label_d_fg}
|
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,fg_bg=label_d_fg}
|
||||||
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3}
|
||||||
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4}
|
||||||
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=label_fg}
|
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=label_fg}
|
||||||
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
||||||
|
|
||||||
@@ -129,17 +129,17 @@ local function init(panel)
|
|||||||
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green_hc,colors.green_off)}
|
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green_hc,colors.green_off)}
|
||||||
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=label_d_fg}
|
TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,fg_bg=label_d_fg}
|
||||||
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=label_d_fg}
|
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,fg_bg=label_d_fg}
|
||||||
crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value)
|
crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3}
|
||||||
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4}
|
||||||
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
||||||
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facility, fp_ok)
|
function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@@ -57,8 +58,8 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
local self = {
|
local self = {
|
||||||
units = facility.get_units(),
|
units = facility.get_units(),
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
establish_time = util.time_s(),
|
establish_time = util.time_s(),
|
||||||
@@ -182,13 +183,11 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
---@param pkt mgmt_frame|crdn_frame
|
---@param pkt mgmt_frame|crdn_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
|||||||
@@ -48,12 +48,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param reactor_id integer reactor ID
|
---@param reactor_id integer reactor ID
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, fp_ok)
|
function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue, timeout, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@@ -66,8 +67,8 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
ramping_rate = false,
|
ramping_rate = false,
|
||||||
auto_lock = false,
|
auto_lock = false,
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
received_struct = false,
|
received_struct = false,
|
||||||
received_status_cache = false,
|
received_status_cache = false,
|
||||||
@@ -190,71 +191,111 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
-- copy in the RPS status
|
-- copy in the RPS status
|
||||||
---@param rps_status table
|
---@param rps_status table
|
||||||
local function _copy_rps_status(rps_status)
|
local function _copy_rps_status(rps_status)
|
||||||
self.sDB.rps_tripped = rps_status[1]
|
local rps = self.sDB.rps_status
|
||||||
self.sDB.rps_trip_cause = rps_status[2]
|
|
||||||
self.sDB.rps_status.high_dmg = rps_status[3]
|
self.sDB.rps_tripped = rps_status[1]
|
||||||
self.sDB.rps_status.high_temp = rps_status[4]
|
self.sDB.rps_trip_cause = rps_status[2]
|
||||||
self.sDB.rps_status.low_cool = rps_status[5]
|
|
||||||
self.sDB.rps_status.ex_waste = rps_status[6]
|
rps.high_dmg = rps_status[3]
|
||||||
self.sDB.rps_status.ex_hcool = rps_status[7]
|
rps.high_temp = rps_status[4]
|
||||||
self.sDB.rps_status.no_fuel = rps_status[8]
|
rps.low_cool = rps_status[5]
|
||||||
self.sDB.rps_status.fault = rps_status[9]
|
rps.ex_waste = rps_status[6]
|
||||||
self.sDB.rps_status.timeout = rps_status[10]
|
rps.ex_hcool = rps_status[7]
|
||||||
self.sDB.rps_status.manual = rps_status[11]
|
rps.no_fuel = rps_status[8]
|
||||||
self.sDB.rps_status.automatic = rps_status[12]
|
rps.fault = rps_status[9]
|
||||||
self.sDB.rps_status.sys_fail = rps_status[13]
|
rps.timeout = rps_status[10]
|
||||||
self.sDB.rps_status.force_dis = rps_status[14]
|
rps.manual = rps_status[11]
|
||||||
|
rps.automatic = rps_status[12]
|
||||||
|
rps.sys_fail = rps_status[13]
|
||||||
|
rps.force_dis = rps_status[14]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy in the reactor status
|
-- copy in the reactor status
|
||||||
---@param mek_data table
|
---@param mek_data table
|
||||||
local function _copy_status(mek_data)
|
local function _copy_status(mek_data)
|
||||||
|
local stat = self.sDB.mek_status
|
||||||
|
local struct = self.sDB.mek_struct
|
||||||
|
|
||||||
-- copy status information
|
-- copy status information
|
||||||
self.sDB.mek_status.status = mek_data[1]
|
stat.status = mek_data[1]
|
||||||
self.sDB.mek_status.burn_rate = mek_data[2]
|
stat.burn_rate = mek_data[2]
|
||||||
self.sDB.mek_status.act_burn_rate = mek_data[3]
|
stat.act_burn_rate = mek_data[3]
|
||||||
self.sDB.mek_status.temp = mek_data[4]
|
stat.temp = mek_data[4]
|
||||||
self.sDB.mek_status.damage = mek_data[5]
|
stat.damage = mek_data[5]
|
||||||
self.sDB.mek_status.boil_eff = mek_data[6]
|
stat.boil_eff = mek_data[6]
|
||||||
self.sDB.mek_status.env_loss = mek_data[7]
|
stat.env_loss = mek_data[7]
|
||||||
|
|
||||||
-- copy container information
|
-- copy container information
|
||||||
self.sDB.mek_status.fuel = mek_data[8]
|
stat.fuel = mek_data[8]
|
||||||
self.sDB.mek_status.fuel_fill = mek_data[9]
|
stat.fuel_fill = mek_data[9]
|
||||||
self.sDB.mek_status.waste = mek_data[10]
|
stat.waste = mek_data[10]
|
||||||
self.sDB.mek_status.waste_fill = mek_data[11]
|
stat.waste_fill = mek_data[11]
|
||||||
self.sDB.mek_status.ccool_type = mek_data[12]
|
stat.ccool_type = mek_data[12]
|
||||||
self.sDB.mek_status.ccool_amnt = mek_data[13]
|
stat.ccool_amnt = mek_data[13]
|
||||||
self.sDB.mek_status.ccool_fill = mek_data[14]
|
stat.ccool_fill = mek_data[14]
|
||||||
self.sDB.mek_status.hcool_type = mek_data[15]
|
stat.hcool_type = mek_data[15]
|
||||||
self.sDB.mek_status.hcool_amnt = mek_data[16]
|
stat.hcool_amnt = mek_data[16]
|
||||||
self.sDB.mek_status.hcool_fill = mek_data[17]
|
stat.hcool_fill = mek_data[17]
|
||||||
|
|
||||||
-- update computable fields if we have our structure
|
-- update computable fields if we have our structure
|
||||||
if self.received_struct then
|
if self.received_struct then
|
||||||
self.sDB.mek_status.fuel_need = self.sDB.mek_struct.fuel_cap - self.sDB.mek_status.fuel_fill
|
stat.fuel_need = struct.fuel_cap - stat.fuel_fill
|
||||||
self.sDB.mek_status.waste_need = self.sDB.mek_struct.waste_cap - self.sDB.mek_status.waste_fill
|
stat.waste_need = struct.waste_cap - stat.waste_fill
|
||||||
self.sDB.mek_status.cool_need = self.sDB.mek_struct.ccool_cap - self.sDB.mek_status.ccool_fill
|
stat.cool_need = struct.ccool_cap - stat.ccool_fill
|
||||||
self.sDB.mek_status.hcool_need = self.sDB.mek_struct.hcool_cap - self.sDB.mek_status.hcool_fill
|
stat.hcool_need = struct.hcool_cap - stat.hcool_fill
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy in the reactor structure
|
-- copy in the reactor structure
|
||||||
---@param mek_data table
|
---@param mek_data table
|
||||||
local function _copy_struct(mek_data)
|
local function _copy_struct(mek_data)
|
||||||
self.sDB.mek_struct.length = mek_data[1]
|
local struct = self.sDB.mek_struct
|
||||||
self.sDB.mek_struct.width = mek_data[2]
|
|
||||||
self.sDB.mek_struct.height = mek_data[3]
|
struct.length = mek_data[1]
|
||||||
self.sDB.mek_struct.min_pos = mek_data[4]
|
struct.width = mek_data[2]
|
||||||
self.sDB.mek_struct.max_pos = mek_data[5]
|
struct.height = mek_data[3]
|
||||||
self.sDB.mek_struct.heat_cap = mek_data[6]
|
struct.min_pos = mek_data[4]
|
||||||
self.sDB.mek_struct.fuel_asm = mek_data[7]
|
struct.max_pos = mek_data[5]
|
||||||
self.sDB.mek_struct.fuel_sa = mek_data[8]
|
struct.heat_cap = mek_data[6]
|
||||||
self.sDB.mek_struct.fuel_cap = mek_data[9]
|
struct.fuel_asm = mek_data[7]
|
||||||
self.sDB.mek_struct.waste_cap = mek_data[10]
|
struct.fuel_sa = mek_data[8]
|
||||||
self.sDB.mek_struct.ccool_cap = mek_data[11]
|
struct.fuel_cap = mek_data[9]
|
||||||
self.sDB.mek_struct.hcool_cap = mek_data[12]
|
struct.waste_cap = mek_data[10]
|
||||||
self.sDB.mek_struct.max_burn = mek_data[13]
|
struct.ccool_cap = mek_data[11]
|
||||||
|
struct.hcool_cap = mek_data[12]
|
||||||
|
struct.max_burn = mek_data[13]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle a reactor status packet
|
||||||
|
---@param pkt rplc_frame
|
||||||
|
local function _handle_status(pkt)
|
||||||
|
local valid = (type(pkt.data[1]) == "number") and (type(pkt.data[2]) == "boolean") and
|
||||||
|
(type(pkt.data[3]) == "boolean") and (type(pkt.data[4]) == "boolean") and
|
||||||
|
(type(pkt.data[5]) == "number")
|
||||||
|
|
||||||
|
if valid then
|
||||||
|
self.sDB.last_status_update = pkt.data[1]
|
||||||
|
self.sDB.control_state = pkt.data[2]
|
||||||
|
self.sDB.no_reactor = pkt.data[3]
|
||||||
|
self.sDB.formed = pkt.data[4]
|
||||||
|
self.sDB.auto_ack_token = pkt.data[5]
|
||||||
|
|
||||||
|
if (not self.sDB.no_reactor) and self.sDB.formed and (type(pkt.data[6]) == "number") then
|
||||||
|
self.sDB.mek_status.heating_rate = pkt.data[6] or 0.0
|
||||||
|
|
||||||
|
-- attempt to read mek_data table
|
||||||
|
if type(pkt.data[7]) == "table" then
|
||||||
|
if #pkt.data[7] == 17 then
|
||||||
|
_copy_status(pkt.data[7])
|
||||||
|
self.received_status_cache = true
|
||||||
|
else
|
||||||
|
log.error(log_header .. "RPLC status packet reactor data length mismatch")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(log_header .. "RPLC status packet invalid")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mark this PLC session as closed, stop watchdog
|
-- mark this PLC session as closed, stop watchdog
|
||||||
@@ -309,13 +350,11 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
---@param pkt mgmt_frame|rplc_frame
|
---@param pkt mgmt_frame|rplc_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- process packet
|
-- process packet
|
||||||
@@ -334,48 +373,17 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
if pkt.type == RPLC_TYPE.STATUS then
|
if pkt.type == RPLC_TYPE.STATUS then
|
||||||
-- status packet received, update data
|
-- status packet received, update data
|
||||||
if pkt.length >= 5 then
|
if pkt.length >= 5 then
|
||||||
if (type(pkt.data[1]) == "number") and (type(pkt.data[2]) == "boolean") and (type(pkt.data[3]) == "boolean") and
|
_handle_status(pkt)
|
||||||
(type(pkt.data[4]) == "boolean") and (type(pkt.data[5]) == "number") then
|
|
||||||
self.sDB.last_status_update = pkt.data[1]
|
|
||||||
self.sDB.control_state = pkt.data[2]
|
|
||||||
self.sDB.no_reactor = pkt.data[3]
|
|
||||||
self.sDB.formed = pkt.data[4]
|
|
||||||
self.sDB.auto_ack_token = pkt.data[5]
|
|
||||||
|
|
||||||
if (not self.sDB.no_reactor) and self.sDB.formed and (type(pkt.data[6]) == "number") then
|
|
||||||
self.sDB.mek_status.heating_rate = pkt.data[6] or 0.0
|
|
||||||
|
|
||||||
-- attempt to read mek_data table
|
|
||||||
if type(pkt.data[7]) == "table" then
|
|
||||||
local status = pcall(_copy_status, pkt.data[7])
|
|
||||||
if status then
|
|
||||||
-- copied in status data OK
|
|
||||||
self.received_status_cache = true
|
|
||||||
else
|
|
||||||
-- error copying status data
|
|
||||||
log.error(log_header .. "failed to parse status packet data")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(log_header .. "RPLC status packet invalid")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "RPLC status packet length mismatch")
|
log.debug(log_header .. "RPLC status packet length mismatch")
|
||||||
end
|
end
|
||||||
elseif pkt.type == RPLC_TYPE.MEK_STRUCT then
|
elseif pkt.type == RPLC_TYPE.MEK_STRUCT then
|
||||||
-- received reactor structure, record it
|
-- received reactor structure, record it
|
||||||
if pkt.length == 14 then
|
if pkt.length == 13 then
|
||||||
local status = pcall(_copy_struct, pkt.data)
|
_copy_struct(pkt.data)
|
||||||
if status then
|
_compute_op_temps()
|
||||||
-- copied in structure data OK
|
self.received_struct = true
|
||||||
_compute_op_temps()
|
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
||||||
self.received_struct = true
|
|
||||||
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
|
||||||
else
|
|
||||||
-- error copying structure data
|
|
||||||
log.error(log_header .. "failed to parse struct packet data")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "RPLC struct packet length mismatch")
|
log.debug(log_header .. "RPLC struct packet length mismatch")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -30,12 +30,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility, fp_ok)
|
function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@@ -43,8 +44,8 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
|
|||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@@ -93,13 +94,11 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
|
|||||||
---@param pkt mgmt_frame
|
---@param pkt mgmt_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
|||||||
@@ -34,13 +34,14 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param advertisement table RTU device advertisement
|
---@param advertisement table RTU device advertisement
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement, facility, fp_ok)
|
function rtu.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, advertisement, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@@ -51,8 +52,8 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
advert = advertisement,
|
advert = advertisement,
|
||||||
fac_units = facility.get_units(),
|
fac_units = facility.get_units(),
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@@ -240,13 +241,11 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
---@param pkt modbus_frame|mgmt_frame
|
---@param pkt modbus_frame|mgmt_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
|||||||
@@ -273,11 +273,12 @@ end
|
|||||||
|
|
||||||
-- establish a new PLC session
|
-- establish a new PLC session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer PLC computer ID
|
||||||
---@param for_reactor integer
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
---@param version string
|
---@param for_reactor integer unit ID
|
||||||
|
---@param version string PLC version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
function svsessions.establish_plc_session(source_addr, i_seq_num, for_reactor, version)
|
||||||
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.config.UnitCount then
|
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.config.UnitCount then
|
||||||
---@class plc_session_struct
|
---@class plc_session_struct
|
||||||
local plc_s = {
|
local plc_s = {
|
||||||
@@ -294,7 +295,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
|||||||
|
|
||||||
local id = self.next_ids.plc
|
local id = self.next_ids.plc
|
||||||
|
|
||||||
plc_s.instance = plc.new_session(id, source_addr, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
plc_s.instance = plc.new_session(id, source_addr, i_seq_num, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
||||||
table.insert(self.sessions.plc, plc_s)
|
table.insert(self.sessions.plc, plc_s)
|
||||||
|
|
||||||
local units = self.facility.get_units()
|
local units = self.facility.get_units()
|
||||||
@@ -320,13 +321,14 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- establish a new RTU session
|
-- establish a new RTU gateway session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer RTU gateway computer ID
|
||||||
---@param advertisement table
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
---@param version string
|
---@param advertisement table RTU capability advertisement
|
||||||
|
---@param version string RTU gateway version
|
||||||
---@return integer session_id
|
---@return integer session_id
|
||||||
function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
function svsessions.establish_rtu_session(source_addr, i_seq_num, advertisement, version)
|
||||||
---@class rtu_session_struct
|
---@class rtu_session_struct
|
||||||
local rtu_s = {
|
local rtu_s = {
|
||||||
s_type = "rtu",
|
s_type = "rtu",
|
||||||
@@ -341,7 +343,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
|||||||
|
|
||||||
local id = self.next_ids.rtu
|
local id = self.next_ids.rtu
|
||||||
|
|
||||||
rtu_s.instance = rtu.new_session(id, source_addr, rtu_s.in_queue, rtu_s.out_queue, self.config.RTU_Timeout, advertisement, self.facility, self.fp_ok)
|
rtu_s.instance = rtu.new_session(id, source_addr, i_seq_num, rtu_s.in_queue, rtu_s.out_queue, self.config.RTU_Timeout, advertisement, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.rtu, rtu_s)
|
table.insert(self.sessions.rtu, rtu_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
@@ -362,10 +364,11 @@ end
|
|||||||
|
|
||||||
-- establish a new coordinator session
|
-- establish a new coordinator session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer coordinator computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string coordinator version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_crd_session(source_addr, version)
|
function svsessions.establish_crd_session(source_addr, i_seq_num, version)
|
||||||
if svsessions.get_crd_session() == nil then
|
if svsessions.get_crd_session() == nil then
|
||||||
---@class crd_session_struct
|
---@class crd_session_struct
|
||||||
local crd_s = {
|
local crd_s = {
|
||||||
@@ -381,7 +384,7 @@ function svsessions.establish_crd_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_ids.crd
|
local id = self.next_ids.crd
|
||||||
|
|
||||||
crd_s.instance = coordinator.new_session(id, source_addr, crd_s.in_queue, crd_s.out_queue, self.config.CRD_Timeout, self.facility, self.fp_ok)
|
crd_s.instance = coordinator.new_session(id, source_addr, i_seq_num, crd_s.in_queue, crd_s.out_queue, self.config.CRD_Timeout, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.crd, crd_s)
|
table.insert(self.sessions.crd, crd_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
@@ -406,10 +409,11 @@ end
|
|||||||
|
|
||||||
-- establish a new pocket diagnostics session
|
-- establish a new pocket diagnostics session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer pocket computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string pocket version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_pdg_session(source_addr, version)
|
function svsessions.establish_pdg_session(source_addr, i_seq_num, version)
|
||||||
---@class pdg_session_struct
|
---@class pdg_session_struct
|
||||||
local pdg_s = {
|
local pdg_s = {
|
||||||
s_type = "pkt",
|
s_type = "pkt",
|
||||||
@@ -424,7 +428,7 @@ function svsessions.establish_pdg_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_ids.pdg
|
local id = self.next_ids.pdg
|
||||||
|
|
||||||
pdg_s.instance = pocket.new_session(id, source_addr, pdg_s.in_queue, pdg_s.out_queue, self.config.PKT_Timeout, self.facility, self.fp_ok)
|
pdg_s.instance = pocket.new_session(id, source_addr, i_seq_num, pdg_s.in_queue, pdg_s.out_queue, self.config.PKT_Timeout, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.pdg, pdg_s)
|
table.insert(self.sessions.pdg, pdg_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v1.3.11"
|
local SUPERVISOR_VERSION = "v1.4.2"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@@ -214,7 +214,7 @@ local function main()
|
|||||||
elseif event == "modem_message" then
|
elseif event == "modem_message" then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
superv_comms.handle_packet(packet)
|
if packet then superv_comms.handle_packet(packet) end
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
||||||
event == "double_click" then
|
event == "double_click" then
|
||||||
-- handle a mouse event
|
-- handle a mouse event
|
||||||
|
|||||||
@@ -191,283 +191,282 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- handle a packet
|
-- handle a packet
|
||||||
---@param packet modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil
|
---@param packet modbus_frame|rplc_frame|mgmt_frame|crdn_frame
|
||||||
function public.handle_packet(packet)
|
function public.handle_packet(packet)
|
||||||
if packet ~= nil then
|
local l_chan = packet.scada_frame.local_channel()
|
||||||
local l_chan = packet.scada_frame.local_channel()
|
local r_chan = packet.scada_frame.remote_channel()
|
||||||
local r_chan = packet.scada_frame.remote_channel()
|
local src_addr = packet.scada_frame.src_addr()
|
||||||
local src_addr = packet.scada_frame.src_addr()
|
local protocol = packet.scada_frame.protocol()
|
||||||
local protocol = packet.scada_frame.protocol()
|
local i_seq_num = packet.scada_frame.seq_num()
|
||||||
|
|
||||||
if l_chan ~= config.SVR_Channel then
|
if l_chan ~= config.SVR_Channel then
|
||||||
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
||||||
elseif r_chan == config.PLC_Channel then
|
elseif r_chan == config.PLC_Channel then
|
||||||
-- look for an associated session
|
-- look for an associated session
|
||||||
local session = svsessions.find_plc_session(src_addr)
|
local session = svsessions.find_plc_session(src_addr)
|
||||||
|
|
||||||
if protocol == PROTOCOL.RPLC then
|
if protocol == PROTOCOL.RPLC then
|
||||||
---@cast packet rplc_frame
|
---@cast packet rplc_frame
|
||||||
-- reactor PLC packet
|
-- reactor PLC packet
|
||||||
if session ~= nil then
|
if session ~= nil then
|
||||||
-- pass the packet onto the session handler
|
-- pass the packet onto the session handler
|
||||||
session.in_queue.push_packet(packet)
|
session.in_queue.push_packet(packet)
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug("discarding RPLC packet without a known session")
|
|
||||||
end
|
|
||||||
elseif protocol == PROTOCOL.SCADA_MGMT then
|
|
||||||
---@cast packet mgmt_frame
|
|
||||||
-- SCADA management packet
|
|
||||||
if session ~= nil then
|
|
||||||
-- pass the packet onto the session handler
|
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
|
||||||
-- establish a new session
|
|
||||||
local last_ack = self.last_est_acks[src_addr]
|
|
||||||
|
|
||||||
-- validate packet and continue
|
|
||||||
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
|
||||||
local comms_v = packet.data[1]
|
|
||||||
local firmware_v = packet.data[2]
|
|
||||||
local dev_type = packet.data[3]
|
|
||||||
|
|
||||||
if comms_v ~= comms.version then
|
|
||||||
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
|
||||||
log.info(util.c("dropping PLC establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
|
||||||
elseif dev_type == DEVICE_TYPE.PLC then
|
|
||||||
-- PLC linking request
|
|
||||||
if packet.length == 4 and type(packet.data[4]) == "number" then
|
|
||||||
local reactor_id = packet.data[4]
|
|
||||||
local plc_id = svsessions.establish_plc_session(src_addr, reactor_id, firmware_v)
|
|
||||||
|
|
||||||
if plc_id == false then
|
|
||||||
-- reactor already has a PLC assigned
|
|
||||||
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
|
||||||
log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
|
||||||
else
|
|
||||||
-- got an ID; assigned to a reactor successfully
|
|
||||||
println(util.c("PLC (", firmware_v, ") [@", src_addr, "] \xbb reactor ", reactor_id, " connected"))
|
|
||||||
log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", src_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type")
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC channel"))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug("invalid establish packet (on PLC channel)")
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug(util.c("discarding PLC SCADA_MGMT packet without a known session from computer ", src_addr))
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug(util.c("illegal packet type ", protocol, " on PLC channel"))
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug("discarding RPLC packet without a known session")
|
||||||
end
|
end
|
||||||
elseif r_chan == config.RTU_Channel then
|
elseif protocol == PROTOCOL.SCADA_MGMT then
|
||||||
-- look for an associated session
|
---@cast packet mgmt_frame
|
||||||
local session = svsessions.find_rtu_session(src_addr)
|
-- SCADA management packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
|
-- establish a new session
|
||||||
|
local last_ack = self.last_est_acks[src_addr]
|
||||||
|
|
||||||
if protocol == PROTOCOL.MODBUS_TCP then
|
-- validate packet and continue
|
||||||
---@cast packet modbus_frame
|
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
||||||
-- MODBUS response
|
local comms_v = packet.data[1]
|
||||||
if session ~= nil then
|
local firmware_v = packet.data[2]
|
||||||
-- pass the packet onto the session handler
|
local dev_type = packet.data[3]
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug("discarding MODBUS_TCP packet without a known session")
|
|
||||||
end
|
|
||||||
elseif protocol == PROTOCOL.SCADA_MGMT then
|
|
||||||
---@cast packet mgmt_frame
|
|
||||||
-- SCADA management packet
|
|
||||||
if session ~= nil then
|
|
||||||
-- pass the packet onto the session handler
|
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
|
||||||
-- establish a new session
|
|
||||||
local last_ack = self.last_est_acks[src_addr]
|
|
||||||
|
|
||||||
-- validate packet and continue
|
if comms_v ~= comms.version then
|
||||||
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
||||||
local comms_v = packet.data[1]
|
log.info(util.c("dropping PLC establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
||||||
local firmware_v = packet.data[2]
|
|
||||||
local dev_type = packet.data[3]
|
|
||||||
|
|
||||||
if comms_v ~= comms.version then
|
|
||||||
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
|
||||||
log.info(util.c("dropping RTU establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
|
||||||
elseif dev_type == DEVICE_TYPE.RTU then
|
|
||||||
if packet.length == 4 then
|
|
||||||
-- this is an RTU advertisement for a new session
|
|
||||||
local rtu_advert = packet.data[4]
|
|
||||||
local s_id = svsessions.establish_rtu_session(src_addr, rtu_advert, firmware_v)
|
|
||||||
|
|
||||||
println(util.c("RTU (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
|
||||||
log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
|
||||||
else
|
|
||||||
log.debug("RTU_ESTABLISH: packet length mismatch")
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU channel"))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
log.debug("invalid establish packet (on RTU channel)")
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug(util.c("discarding RTU SCADA_MGMT packet without a known session from computer ", src_addr))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(util.c("illegal packet type ", protocol, " on RTU channel"))
|
|
||||||
end
|
|
||||||
elseif r_chan == config.CRD_Channel then
|
|
||||||
-- look for an associated session
|
|
||||||
local session = svsessions.find_crd_session(src_addr)
|
|
||||||
|
|
||||||
if protocol == PROTOCOL.SCADA_MGMT then
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
---@cast packet mgmt_frame
|
elseif dev_type == DEVICE_TYPE.PLC then
|
||||||
-- SCADA management packet
|
-- PLC linking request
|
||||||
if session ~= nil then
|
if packet.length == 4 and type(packet.data[4]) == "number" then
|
||||||
-- pass the packet onto the session handler
|
local reactor_id = packet.data[4]
|
||||||
session.in_queue.push_packet(packet)
|
local plc_id = svsessions.establish_plc_session(src_addr, i_seq_num, reactor_id, firmware_v)
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
|
||||||
-- establish a new session
|
|
||||||
local last_ack = self.last_est_acks[src_addr]
|
|
||||||
|
|
||||||
-- validate packet and continue
|
if plc_id == false then
|
||||||
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
-- reactor already has a PLC assigned
|
||||||
local comms_v = packet.data[1]
|
|
||||||
local firmware_v = packet.data[2]
|
|
||||||
local dev_type = packet.data[3]
|
|
||||||
|
|
||||||
if comms_v ~= comms.version then
|
|
||||||
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
|
||||||
log.info(util.c("dropping coordinator establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
|
||||||
elseif dev_type == DEVICE_TYPE.CRD then
|
|
||||||
-- this is an attempt to establish a new coordinator session
|
|
||||||
local s_id = svsessions.establish_crd_session(src_addr, firmware_v)
|
|
||||||
|
|
||||||
if s_id ~= false then
|
|
||||||
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
|
||||||
log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { config.UnitCount, cooling_conf })
|
|
||||||
else
|
|
||||||
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
||||||
log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator")
|
log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
||||||
|
else
|
||||||
|
-- got an ID; assigned to a reactor successfully
|
||||||
|
println(util.c("PLC (", firmware_v, ") [@", src_addr, "] \xbb reactor ", reactor_id, " connected"))
|
||||||
|
log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", src_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug(util.c("illegal establish packet for device ", dev_type, " on coordinator channel"))
|
log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type")
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("CRD_ESTABLISH: establish packet length mismatch")
|
log.debug(util.c("illegal establish packet for device ", dev_type, " on PLC channel"))
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- any other packet should be session related, discard it
|
log.debug("invalid establish packet (on PLC channel)")
|
||||||
log.debug(util.c("discarding coordinator SCADA_MGMT packet without a known session from computer ", src_addr))
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
end
|
|
||||||
elseif protocol == PROTOCOL.SCADA_CRDN then
|
|
||||||
---@cast packet crdn_frame
|
|
||||||
-- coordinator packet
|
|
||||||
if session ~= nil then
|
|
||||||
-- pass the packet onto the session handler
|
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug(util.c("discarding coordinator SCADA_CRDN packet without a known session from computer ", src_addr))
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug(util.c("illegal packet type ", protocol, " on coordinator channel"))
|
-- any other packet should be session related, discard it
|
||||||
end
|
log.debug(util.c("discarding PLC SCADA_MGMT packet without a known session from computer ", src_addr))
|
||||||
elseif r_chan == config.PKT_Channel then
|
|
||||||
-- look for an associated session
|
|
||||||
local session = svsessions.find_pdg_session(src_addr)
|
|
||||||
|
|
||||||
if protocol == PROTOCOL.SCADA_MGMT then
|
|
||||||
---@cast packet mgmt_frame
|
|
||||||
-- SCADA management packet
|
|
||||||
if session ~= nil then
|
|
||||||
-- pass the packet onto the session handler
|
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
|
||||||
-- establish a new session
|
|
||||||
local last_ack = self.last_est_acks[src_addr]
|
|
||||||
|
|
||||||
-- validate packet and continue
|
|
||||||
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
|
||||||
local comms_v = packet.data[1]
|
|
||||||
local firmware_v = packet.data[2]
|
|
||||||
local dev_type = packet.data[3]
|
|
||||||
|
|
||||||
if comms_v ~= comms.version then
|
|
||||||
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
|
||||||
log.info(util.c("dropping PDG establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
|
||||||
end
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
|
||||||
elseif dev_type == DEVICE_TYPE.PKT then
|
|
||||||
-- this is an attempt to establish a new pocket diagnostic session
|
|
||||||
local s_id = svsessions.establish_pdg_session(src_addr, firmware_v)
|
|
||||||
|
|
||||||
println(util.c("PKT (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
|
||||||
log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
|
||||||
else
|
|
||||||
log.debug(util.c("illegal establish packet for device ", dev_type, " on pocket channel"))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug("PDG_ESTABLISH: establish packet length mismatch")
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug(util.c("discarding pocket SCADA_MGMT packet without a known session from computer ", src_addr))
|
|
||||||
end
|
|
||||||
elseif protocol == PROTOCOL.SCADA_CRDN then
|
|
||||||
---@cast packet crdn_frame
|
|
||||||
-- coordinator packet
|
|
||||||
if session ~= nil then
|
|
||||||
-- pass the packet onto the session handler
|
|
||||||
session.in_queue.push_packet(packet)
|
|
||||||
else
|
|
||||||
-- any other packet should be session related, discard it
|
|
||||||
log.debug(util.c("discarding pocket SCADA_CRDN packet without a known session from computer ", src_addr))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(util.c("illegal packet type ", protocol, " on pocket channel"))
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("received packet for unknown channel " .. r_chan, true)
|
log.debug(util.c("illegal packet type ", protocol, " on PLC channel"))
|
||||||
end
|
end
|
||||||
|
elseif r_chan == config.RTU_Channel then
|
||||||
|
-- look for an associated session
|
||||||
|
local session = svsessions.find_rtu_session(src_addr)
|
||||||
|
|
||||||
|
if protocol == PROTOCOL.MODBUS_TCP then
|
||||||
|
---@cast packet modbus_frame
|
||||||
|
-- MODBUS response
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug("discarding MODBUS_TCP packet without a known session")
|
||||||
|
end
|
||||||
|
elseif protocol == PROTOCOL.SCADA_MGMT then
|
||||||
|
---@cast packet mgmt_frame
|
||||||
|
-- SCADA management packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
|
-- establish a new session
|
||||||
|
local last_ack = self.last_est_acks[src_addr]
|
||||||
|
|
||||||
|
-- validate packet and continue
|
||||||
|
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
||||||
|
local comms_v = packet.data[1]
|
||||||
|
local firmware_v = packet.data[2]
|
||||||
|
local dev_type = packet.data[3]
|
||||||
|
|
||||||
|
if comms_v ~= comms.version then
|
||||||
|
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
||||||
|
log.info(util.c("dropping RTU establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
|
elseif dev_type == DEVICE_TYPE.RTU then
|
||||||
|
if packet.length == 4 then
|
||||||
|
-- this is an RTU advertisement for a new session
|
||||||
|
local rtu_advert = packet.data[4]
|
||||||
|
local s_id = svsessions.establish_rtu_session(src_addr, i_seq_num, rtu_advert, firmware_v)
|
||||||
|
|
||||||
|
println(util.c("RTU (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
|
log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
||||||
|
else
|
||||||
|
log.debug("RTU_ESTABLISH: packet length mismatch")
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal establish packet for device ", dev_type, " on RTU channel"))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("invalid establish packet (on RTU channel)")
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug(util.c("discarding RTU SCADA_MGMT packet without a known session from computer ", src_addr))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal packet type ", protocol, " on RTU channel"))
|
||||||
|
end
|
||||||
|
elseif r_chan == config.CRD_Channel then
|
||||||
|
-- look for an associated session
|
||||||
|
local session = svsessions.find_crd_session(src_addr)
|
||||||
|
|
||||||
|
if protocol == PROTOCOL.SCADA_MGMT then
|
||||||
|
---@cast packet mgmt_frame
|
||||||
|
-- SCADA management packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
|
-- establish a new session
|
||||||
|
local last_ack = self.last_est_acks[src_addr]
|
||||||
|
|
||||||
|
-- validate packet and continue
|
||||||
|
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
||||||
|
local comms_v = packet.data[1]
|
||||||
|
local firmware_v = packet.data[2]
|
||||||
|
local dev_type = packet.data[3]
|
||||||
|
|
||||||
|
if comms_v ~= comms.version then
|
||||||
|
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
||||||
|
log.info(util.c("dropping coordinator establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
|
elseif dev_type == DEVICE_TYPE.CRD then
|
||||||
|
-- this is an attempt to establish a new coordinator session
|
||||||
|
local s_id = svsessions.establish_crd_session(src_addr, i_seq_num, firmware_v)
|
||||||
|
|
||||||
|
if s_id ~= false then
|
||||||
|
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
|
log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { config.UnitCount, cooling_conf })
|
||||||
|
else
|
||||||
|
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
||||||
|
log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator")
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal establish packet for device ", dev_type, " on coordinator channel"))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("CRD_ESTABLISH: establish packet length mismatch")
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug(util.c("discarding coordinator SCADA_MGMT packet without a known session from computer ", src_addr))
|
||||||
|
end
|
||||||
|
elseif protocol == PROTOCOL.SCADA_CRDN then
|
||||||
|
---@cast packet crdn_frame
|
||||||
|
-- coordinator packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug(util.c("discarding coordinator SCADA_CRDN packet without a known session from computer ", src_addr))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal packet type ", protocol, " on coordinator channel"))
|
||||||
|
end
|
||||||
|
elseif r_chan == config.PKT_Channel then
|
||||||
|
-- look for an associated session
|
||||||
|
local session = svsessions.find_pdg_session(src_addr)
|
||||||
|
|
||||||
|
if protocol == PROTOCOL.SCADA_MGMT then
|
||||||
|
---@cast packet mgmt_frame
|
||||||
|
-- SCADA management packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
|
-- establish a new session
|
||||||
|
local last_ack = self.last_est_acks[src_addr]
|
||||||
|
|
||||||
|
-- validate packet and continue
|
||||||
|
if packet.length >= 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
||||||
|
local comms_v = packet.data[1]
|
||||||
|
local firmware_v = packet.data[2]
|
||||||
|
local dev_type = packet.data[3]
|
||||||
|
|
||||||
|
if comms_v ~= comms.version then
|
||||||
|
if last_ack ~= ESTABLISH_ACK.BAD_VERSION then
|
||||||
|
log.info(util.c("dropping PDG establish packet with incorrect comms version v", comms_v, " (expected v", comms.version, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
|
elseif dev_type == DEVICE_TYPE.PKT then
|
||||||
|
-- this is an attempt to establish a new pocket diagnostic session
|
||||||
|
local s_id = svsessions.establish_pdg_session(src_addr, i_seq_num, firmware_v)
|
||||||
|
|
||||||
|
println(util.c("PKT (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
|
log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal establish packet for device ", dev_type, " on pocket channel"))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("PDG_ESTABLISH: establish packet length mismatch")
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug(util.c("discarding pocket SCADA_MGMT packet without a known session from computer ", src_addr))
|
||||||
|
end
|
||||||
|
elseif protocol == PROTOCOL.SCADA_CRDN then
|
||||||
|
---@cast packet crdn_frame
|
||||||
|
-- coordinator packet
|
||||||
|
if session ~= nil then
|
||||||
|
-- pass the packet onto the session handler
|
||||||
|
session.in_queue.push_packet(packet)
|
||||||
|
else
|
||||||
|
-- any other packet should be session related, discard it
|
||||||
|
log.debug(util.c("discarding pocket SCADA_CRDN packet without a known session from computer ", src_addr))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("illegal packet type ", protocol, " on pocket channel"))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("received packet for unknown channel " .. r_chan, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
ReactorDamage = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorDamage, tier = PRIO.EMERGENCY },
|
ReactorDamage = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorDamage, tier = PRIO.EMERGENCY },
|
||||||
-- reactor >1200K
|
-- reactor >1200K
|
||||||
ReactorOverTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorOverTemp, tier = PRIO.URGENT },
|
ReactorOverTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorOverTemp, tier = PRIO.URGENT },
|
||||||
-- reactor >=1150K
|
-- reactor >= computed high temp limit
|
||||||
ReactorHighTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 1, id = ALARM.ReactorHighTemp, tier = PRIO.TIMELY },
|
ReactorHighTemp = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 1, id = ALARM.ReactorHighTemp, tier = PRIO.TIMELY },
|
||||||
-- waste = 100%
|
-- waste = 100%
|
||||||
ReactorWasteLeak = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorWasteLeak, tier = PRIO.EMERGENCY },
|
ReactorWasteLeak = { state = AISTATE.INACTIVE, trip_time = 0, hold_time = 0, id = ALARM.ReactorWasteLeak, tier = PRIO.EMERGENCY },
|
||||||
@@ -976,7 +976,9 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
self.db.control.ready,
|
self.db.control.ready,
|
||||||
self.db.control.degraded,
|
self.db.control.degraded,
|
||||||
self.db.control.waste_mode,
|
self.db.control.waste_mode,
|
||||||
self.waste_product
|
self.waste_product,
|
||||||
|
self.last_rate_change_ms,
|
||||||
|
self.turbine_flow_stable
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -39,21 +39,6 @@ local ALARM_LIMS = const.ALARM_LIMITS
|
|||||||
---@class unit_logic_extension
|
---@class unit_logic_extension
|
||||||
local logic = {}
|
local logic = {}
|
||||||
|
|
||||||
-- compute Mekanism's rotation rate for a turbine
|
|
||||||
---@param turbine turbinev_session_db
|
|
||||||
local function turbine_rotation(turbine)
|
|
||||||
local build = turbine.build
|
|
||||||
|
|
||||||
local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK
|
|
||||||
local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol
|
|
||||||
local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW
|
|
||||||
|
|
||||||
local max_rate = math.min(disp_rate, vent_rate)
|
|
||||||
local flow = math.min(max_rate, turbine.tanks.steam.amount)
|
|
||||||
|
|
||||||
return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate
|
|
||||||
end
|
|
||||||
|
|
||||||
-- update the annunciator
|
-- update the annunciator
|
||||||
---@param self _unit_self
|
---@param self _unit_self
|
||||||
function logic.update_annunciator(self)
|
function logic.update_annunciator(self)
|
||||||
@@ -333,7 +318,7 @@ function logic.update_annunciator(self)
|
|||||||
local last = self.turbine_stability_data[i]
|
local last = self.turbine_stability_data[i]
|
||||||
|
|
||||||
if (not self.turbine_flow_stable) and (turbine.state.steam_input_rate > 0) then
|
if (not self.turbine_flow_stable) and (turbine.state.steam_input_rate > 0) then
|
||||||
local rotation = turbine_rotation(turbine)
|
local rotation = util.turbine_rotation(turbine)
|
||||||
local rotation_stable = false
|
local rotation_stable = false
|
||||||
|
|
||||||
-- see if data updated, and if so, check rotation speed change
|
-- see if data updated, and if so, check rotation speed change
|
||||||
|
|||||||
Reference in New Issue
Block a user