mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
commit
b1da76c2f6
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
|
||||
# --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'
|
||||
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
|
||||
|
2
.github/workflows/manifest.yml
vendored
2
.github/workflows/manifest.yml
vendored
@ -61,7 +61,7 @@ jobs:
|
||||
- name: Generate manifest for devel
|
||||
id: manifest-devel
|
||||
if: ${{ (success() || failure()) && steps.checkout-devel.outcome == 'success' }}
|
||||
run: python imgen.py
|
||||
run: python build/imgen.py
|
||||
- name: Save devel's manifest
|
||||
if: ${{ (success() || failure()) && steps.manifest-devel.outcome == 'success' }}
|
||||
run: mv install_manifest.json deploy/manifests/devel
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
_notes/
|
||||
_*/
|
||||
/*program.sh
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
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
|
||||
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 print(message) term.write(tostring(message)) end
|
||||
|
||||
local CCMSI_VERSION = "v1.14"
|
||||
local CCMSI_VERSION = "v1.16"
|
||||
|
||||
local install_dir = "/.install-cache"
|
||||
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
||||
@ -120,8 +120,29 @@ local function write_install_manifest(manifest, dependencies)
|
||||
imfile.close()
|
||||
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
|
||||
local function gen_tree(manifest)
|
||||
local function gen_tree(manifest, log)
|
||||
local function _tree_add(tree, split)
|
||||
if #split > 1 then
|
||||
local name = table.remove(split, 1)
|
||||
@ -131,13 +152,14 @@ local function gen_tree(manifest)
|
||||
return nil
|
||||
end
|
||||
|
||||
local list, tree = {}, {}
|
||||
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
|
||||
@ -159,7 +181,7 @@ local function _clean_dir(dir, tree)
|
||||
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
|
||||
elseif (not _in_array(val, tree)) and (val ~= "config.lua" ) then ---@todo remove config.lua on full release
|
||||
fs.delete(path)
|
||||
println("deleted "..path)
|
||||
end
|
||||
@ -168,11 +190,16 @@ end
|
||||
|
||||
-- go through app/common directories to delete unused files
|
||||
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, "ccmsi.lua")
|
||||
table.insert(tree, "log.txt") ---@fixme fix after migration to settings files?
|
||||
|
||||
local ls = fs.list("/")
|
||||
for _, val in pairs(ls) do
|
||||
@ -244,7 +271,6 @@ else
|
||||
end
|
||||
|
||||
-- run selected mode
|
||||
|
||||
if mode == "check" then
|
||||
local ok, manifest = get_remote_manifest()
|
||||
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")
|
||||
|
||||
if dl == nil then
|
||||
red();println("HTTP Error "..err)
|
||||
red();println("HTTP Error: "..err)
|
||||
println("Installer download failed.");white()
|
||||
else
|
||||
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.lockbox.changed = show_pkg_change("lockbox", ver.lockbox)
|
||||
|
||||
-- ask for confirmation
|
||||
if not ask_y_n("Continue", false) then return end
|
||||
|
||||
--------------------------
|
||||
-- START INSTALL/UPDATE --
|
||||
--------------------------
|
||||
@ -372,11 +395,32 @@ elseif mode == "install" or mode == "update" then
|
||||
|
||||
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
|
||||
local size = size_list[dependency]
|
||||
space_required = space_required + size
|
||||
any_change = any_change or not unchanged(dependency)
|
||||
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
|
||||
if space_available < space_required then
|
||||
single_file_mode = true
|
||||
@ -392,16 +436,6 @@ elseif mode == "install" or mode == "update" then
|
||||
|
||||
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 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]
|
||||
for _, file in pairs(files) do
|
||||
println("GET "..file)
|
||||
local dl, err = http.get(repo_path..file)
|
||||
|
||||
if dl == nil then
|
||||
red();println("HTTP Error "..err)
|
||||
if not http_get_file(file, install_dir.."/") then
|
||||
red();println("failed to download "..file)
|
||||
success = false
|
||||
break
|
||||
else
|
||||
local handle = fs.open(install_dir.."/"..file, "w")
|
||||
handle.write(dl.readAll())
|
||||
handle.close()
|
||||
end
|
||||
end
|
||||
end
|
||||
if not success then break end
|
||||
end
|
||||
|
||||
-- copy in downloaded files (installation)
|
||||
@ -478,19 +507,14 @@ elseif mode == "install" or mode == "update" then
|
||||
local files = file_list[dependency]
|
||||
for _, file in pairs(files) do
|
||||
println("GET "..file)
|
||||
local dl, err = http.get(repo_path..file)
|
||||
|
||||
if dl == nil then
|
||||
red();println("HTTP Error "..err)
|
||||
if not http_get_file(file, "/") then
|
||||
red();println("failed to download "..file)
|
||||
success = false
|
||||
break
|
||||
else
|
||||
local handle = fs.open("/"..file, "w")
|
||||
handle.write(dl.readAll())
|
||||
handle.close()
|
||||
end
|
||||
end
|
||||
end
|
||||
if not success then break end
|
||||
end
|
||||
|
||||
if success then
|
||||
@ -534,36 +558,8 @@ elseif mode == "uninstall" then
|
||||
|
||||
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
|
||||
lgray()
|
||||
for _, dependency in pairs(dependencies) do
|
||||
local files = file_list[dependency]
|
||||
for _, file in pairs(files) do
|
||||
@ -582,8 +578,23 @@ elseif mode == "uninstall" then
|
||||
end
|
||||
end
|
||||
|
||||
if fs.exists(legacy_config_file) then
|
||||
fs.delete(legacy_config_file);println("deleted "..legacy_config_file)
|
||||
-- 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 fs.exists(settings_file) then
|
||||
|
@ -305,7 +305,7 @@ local function config_view(display)
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -377,24 +377,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}}
|
||||
|
||||
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=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}
|
||||
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}
|
||||
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}
|
||||
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 svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
||||
@ -408,18 +408,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=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=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}
|
||||
|
||||
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}
|
||||
|
||||
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 svr_cto, api_cto = tonumber(svr_timeout.get_value()), tonumber(api_timeout.get_value())
|
||||
@ -433,13 +433,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=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=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 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 range_val = tonumber(range.get_value())
|
||||
@ -457,7 +457,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=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 function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||
@ -467,7 +467,7 @@ local function config_view(display)
|
||||
hide_key.set_value(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 v = key.get_value()
|
||||
@ -505,12 +505,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}}
|
||||
|
||||
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=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_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}
|
||||
@ -534,11 +534,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."}
|
||||
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=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 count = tonumber(num_units.get_value())
|
||||
@ -571,7 +571,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}}
|
||||
|
||||
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."}
|
||||
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)}
|
||||
@ -592,7 +592,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 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()
|
||||
if tmp_cfg.MainDisplay == nil then
|
||||
@ -652,13 +652,13 @@ local function config_view(display)
|
||||
if value == "0" or value == nil then mon_unit.set_value(0) 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}
|
||||
|
||||
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}
|
||||
|
||||
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
|
||||
---@param iface string
|
||||
@ -720,7 +720,7 @@ local function config_view(display)
|
||||
|
||||
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=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."}
|
||||
@ -729,7 +729,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}
|
||||
|
||||
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 vol = tonumber(s_vol.get_value())
|
||||
@ -749,14 +749,14 @@ local function config_view(display)
|
||||
|
||||
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=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}
|
||||
|
||||
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=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||
|
||||
local function submit_ui_opts()
|
||||
@ -774,20 +774,20 @@ local function config_view(display)
|
||||
|
||||
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}
|
||||
|
||||
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 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 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
|
||||
@ -815,20 +815,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}}
|
||||
|
||||
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=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}
|
||||
|
||||
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}
|
||||
|
||||
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)}
|
||||
_ = 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)}
|
||||
@ -855,7 +855,7 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
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}
|
||||
@ -911,7 +911,7 @@ local function config_view(display)
|
||||
clr_pane.set_value(1)
|
||||
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=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||
|
||||
@ -930,7 +930,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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -998,7 +998,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.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()
|
||||
main_pane.set_value(1)
|
||||
@ -1033,15 +1033,15 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
|
||||
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
|
||||
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)}
|
||||
end
|
||||
end
|
||||
@ -1137,12 +1137,12 @@ local function config_view(display)
|
||||
fac_config_list.remove_all()
|
||||
|
||||
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
|
||||
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"))
|
||||
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
|
||||
|
||||
@ -1178,13 +1178,13 @@ local function config_view(display)
|
||||
|
||||
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,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,height=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="\x1a "..tmp_cfg.UnitCount.." Unit View Monitor"..util.trinary(plural,"s","")}
|
||||
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,text="\x1a 1 Main View Monitor"}
|
||||
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
|
||||
TextBox{parent=mon_reqs,x=1,y=1,height=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="\x1a 1 Flow View Monitor"}
|
||||
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
|
||||
|
||||
@ -1286,8 +1286,8 @@ local function config_view(display)
|
||||
|
||||
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=8,y=1,height=1,text=iface}
|
||||
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,text=iface}
|
||||
|
||||
local w, h = ppm.monitor_block_size(dev.getSize())
|
||||
|
||||
@ -1296,7 +1296,7 @@ local function config_view(display)
|
||||
tool_ctl.gen_mon_list()
|
||||
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}
|
||||
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)}
|
||||
|
||||
@ -1315,15 +1315,15 @@ local function config_view(display)
|
||||
for i = 1, #dc_list do
|
||||
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=8,y=1,height=1,text="disconnected",fg_bg=cpair(colors.red,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,text="disconnected",fg_bg=cpair(colors.red,colors.white)}
|
||||
|
||||
local function unset_mon()
|
||||
purge_assignments(dc_list[i][2])
|
||||
tool_ctl.gen_mon_list()
|
||||
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=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
|
||||
|
@ -232,8 +232,8 @@ function coordinator.comms(version, nic, sv_watchdog)
|
||||
local self = {
|
||||
sv_linked = false,
|
||||
sv_addr = comms.BROADCAST,
|
||||
sv_seq_num = 0,
|
||||
sv_r_seq_num = nil,
|
||||
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, ---@type nil|integer
|
||||
sv_config_err = false,
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||
last_api_est_acks = {},
|
||||
@ -492,7 +492,7 @@ function coordinator.comms(version, nic, sv_watchdog)
|
||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
||||
elseif dev_type == DEVICE_TYPE.PKT then
|
||||
-- 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))
|
||||
|
||||
local conf = iocontrol.get_db().facility.conf
|
||||
@ -515,15 +515,15 @@ function coordinator.comms(version, nic, sv_watchdog)
|
||||
elseif r_chan == config.SVR_Channel then
|
||||
-- check sequence number
|
||||
if self.sv_r_seq_num == nil then
|
||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
||||
elseif self.sv_linked and ((self.sv_r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
||||
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
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())
|
||||
return false
|
||||
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?")
|
||||
return false
|
||||
else
|
||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
||||
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog on valid sequence number
|
||||
@ -706,7 +706,6 @@ function coordinator.comms(version, nic, sv_watchdog)
|
||||
|
||||
self.sv_addr = src_addr
|
||||
self.sv_linked = true
|
||||
self.sv_r_seq_num = nil
|
||||
self.sv_config_err = false
|
||||
|
||||
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
|
||||
|
@ -89,10 +89,11 @@ end
|
||||
|
||||
-- establish a new API session
|
||||
---@nodiscard
|
||||
---@param source_addr integer
|
||||
---@param version string
|
||||
---@param source_addr integer pocket computer ID
|
||||
---@param i_seq_num integer initial (most recent) sequence number
|
||||
---@param version string pocket version
|
||||
---@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
|
||||
local pkt_s = {
|
||||
open = true,
|
||||
@ -105,7 +106,7 @@ function apisessions.establish_session(source_addr, version)
|
||||
|
||||
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)
|
||||
|
||||
local mt = {
|
||||
|
@ -32,16 +32,17 @@ local PERIODICS = {
|
||||
---@nodiscard
|
||||
---@param id integer session ID
|
||||
---@param s_addr integer device source address
|
||||
---@param i_seq_num integer initial sequence number
|
||||
---@param in_queue mqueue in message queue
|
||||
---@param out_queue mqueue out message queue
|
||||
---@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 self = {
|
||||
-- connection properties
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||
r_seq_num = i_seq_num + 1,
|
||||
connected = true,
|
||||
conn_watchdog = util.new_watchdog(timeout),
|
||||
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
|
||||
local function _handle_packet(pkt)
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
||||
if self.r_seq_num ~= 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())
|
||||
return
|
||||
else
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog
|
||||
|
@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
|
||||
local sounder = require("coordinator.sounder")
|
||||
local threads = require("coordinator.threads")
|
||||
|
||||
local COORDINATOR_VERSION = "v1.4.7"
|
||||
local COORDINATOR_VERSION = "v1.5.1"
|
||||
|
||||
local CHUNK_LOAD_DELAY_S = 30.0
|
||||
|
||||
@ -151,8 +151,8 @@ local function main()
|
||||
|
||||
-- core coordinator devices
|
||||
crd_dev = {
|
||||
speaker = ppm.get_device("speaker"),
|
||||
modem = ppm.get_wireless_modem()
|
||||
modem = ppm.get_wireless_modem(),
|
||||
speaker = ppm.get_device("speaker")
|
||||
},
|
||||
|
||||
-- system objects
|
||||
|
@ -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)
|
||||
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="W",x=3,y=5,height=1,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="C",x=28,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,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,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 water = VerticalBar{parent=boiler,x=3,y=1,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
||||
|
@ -42,8 +42,8 @@ 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
|
||||
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=title,alignment=ALIGN.CENTER,width=33,height=1,x=1,y=2,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,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}
|
||||
|
||||
@ -87,7 +87,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 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)
|
||||
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
||||
@ -99,7 +99,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)
|
||||
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)
|
||||
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 .. "_"
|
||||
|
||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,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)}
|
||||
TextBox{parent=entry,x=1,y=3,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,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||
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)
|
||||
|
||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
||||
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
||||
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,fg_bg=label_fg}
|
||||
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}
|
||||
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_color", pkt_rtt.recolor)
|
||||
|
||||
|
@ -105,11 +105,11 @@ local function new_view(root, x, y)
|
||||
fac_rad_h.register(facility.ps, "as_radiation", fac_rad_h.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}
|
||||
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}
|
||||
rtu_count.register(facility.ps, "rtu_count", rtu_count.update)
|
||||
|
||||
@ -190,7 +190,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 = 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}
|
||||
|
||||
@ -249,8 +249,8 @@ local function new_view(root, x, y)
|
||||
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 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_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_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,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_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
|
||||
@ -320,7 +320,7 @@ local function new_view(root, x, y)
|
||||
for i = 1, facility.num_units do
|
||||
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 waste_m = StateIndicator{parent=waste_status,x=17,y=i,states=style.waste.states_abbrv,value=1,min_width=6}
|
||||
|
||||
@ -332,8 +332,8 @@ local function new_view(root, x, y)
|
||||
|
||||
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="WASTE PRODUCTION",alignment=ALIGN.CENTER,width=21,height=1,x=1,y=2,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,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 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}
|
||||
|
||||
TextBox{parent=reactor_fills,text="FUEL",x=2,y=1,height=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="HCOOL",x=2,y=4,height=1,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="FUEL",x=2,y=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,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 ccool = HorizontalBar{parent=reactor_fills,x=8,y=2,show_percent=true,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=14}
|
||||
|
@ -37,8 +37,8 @@ local function new_view(root, x, y, ps)
|
||||
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}
|
||||
|
||||
TextBox{parent=turbine,text="S",x=2,y=5,height=1,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="S",x=2,y=5,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)
|
||||
energy.register(ps, "energy_fill", energy.update)
|
||||
|
@ -71,7 +71,7 @@ local function init(parent, id)
|
||||
local b_ps = unit.boiler_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 --
|
||||
@ -81,20 +81,20 @@ local function init(parent, id)
|
||||
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)
|
||||
|
||||
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}
|
||||
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}
|
||||
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="C",x=4,y=22,width=1,height=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=25,width=1,height=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="W",x=10,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,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,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,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 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)
|
||||
|
||||
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 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)
|
||||
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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 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_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_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,alignment=ALIGN.CENTER,fg_bg=gry_wht}
|
||||
|
||||
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)
|
||||
@ -205,7 +205,7 @@ local function init(parent, id)
|
||||
|
||||
-- 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_annunc = Div{parent=rps,width=31,height=10,x=2,y=1}
|
||||
|
||||
@ -233,7 +233,7 @@ local function init(parent, id)
|
||||
|
||||
-- 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_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}
|
||||
@ -265,11 +265,11 @@ local function init(parent, id)
|
||||
if unit.num_boilers > 0 then
|
||||
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}
|
||||
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}
|
||||
b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update)
|
||||
end
|
||||
@ -281,11 +281,11 @@ local function init(parent, id)
|
||||
_add_space()
|
||||
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}
|
||||
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}
|
||||
b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update)
|
||||
end
|
||||
@ -294,19 +294,19 @@ local function init(parent, id)
|
||||
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update)
|
||||
|
||||
@ -315,19 +315,19 @@ local function init(parent, id)
|
||||
_add_space()
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update)
|
||||
end
|
||||
@ -335,19 +335,19 @@ local function init(parent, id)
|
||||
if unit.num_turbines > 2 then
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update)
|
||||
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)
|
||||
|
||||
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_div = Div{parent=waste_proc,x=2,y=1,width=31,height=1}
|
||||
|
||||
@ -474,15 +474,15 @@ local function init(parent, id)
|
||||
|
||||
-- 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,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,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
||||
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,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 --
|
||||
--------------------------------
|
||||
|
||||
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_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()
|
||||
|
||||
TextBox{parent=auto_div,text="Prio. Group",height=1,width=11,fg_bg=style.label}
|
||||
local auto_grp = TextBox{parent=auto_div,text="Manual",height=1,width=11,fg_bg=s_field}
|
||||
TextBox{parent=auto_div,text="Prio. Group",width=11,fg_bg=style.label}
|
||||
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)
|
||||
|
||||
|
@ -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}
|
||||
TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=ALIGN.CENTER,height=1}
|
||||
TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,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}
|
||||
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 = {}
|
||||
|
||||
@ -113,8 +113,8 @@ local function make(parent, x, y, wide, unit)
|
||||
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}
|
||||
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=ALIGN.CENTER,height=1}
|
||||
TextBox{parent=boiler,y=3,text=util.trinary(unit.num_boilers>1,"BOILERS","BOILER"),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}
|
||||
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}
|
||||
|
||||
@ -132,13 +132,13 @@ local function make(parent, x, y, wide, unit)
|
||||
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}
|
||||
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=ALIGN.CENTER,height=1}
|
||||
TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),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}
|
||||
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
|
||||
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}
|
||||
state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update)
|
||||
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}
|
||||
|
||||
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 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)
|
||||
@ -181,8 +181,8 @@ local function make(parent, x, y, wide, unit)
|
||||
|
||||
local function _machine(mx, my, name)
|
||||
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+1,text=name,alignment=ALIGN.CENTER,fg_bg=style.theme.header,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}
|
||||
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}
|
||||
@ -209,7 +209,7 @@ local function make(parent, x, y, wide, unit)
|
||||
_machine(_wide(97, 83), 4, "PRC [Po] \x1a");
|
||||
_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_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}
|
||||
|
@ -44,7 +44,7 @@ local function make(parent, x, y, unit)
|
||||
local root = Div{parent=parent,x=x,y=y,width=80,height=height}
|
||||
|
||||
-- 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 --
|
||||
|
@ -49,9 +49,9 @@ local function init(main)
|
||||
local tank_list = facility.tank_list
|
||||
|
||||
-- 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"
|
||||
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)
|
||||
|
||||
@ -265,7 +265,7 @@ local function init(main)
|
||||
if tank_defs[i] > 0 then
|
||||
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 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}
|
||||
|
||||
TextBox{parent=tank,text=" ",height=1,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=" ",x=1,y=1,fg_bg=style.lg_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 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_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}
|
||||
|
||||
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_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}
|
||||
|
||||
TextBox{parent=sps,text=" ",width=24,height=1,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=" ",width=24,x=1,y=1,fg_bg=style.lg_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}
|
||||
|
||||
@ -353,12 +353,12 @@ local function init(main)
|
||||
|
||||
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}
|
||||
|
||||
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}
|
||||
|
||||
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 --
|
||||
----------------
|
||||
|
||||
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 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)
|
||||
|
||||
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 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}
|
||||
@ -383,7 +383,7 @@ local function init(main)
|
||||
po.register(facility.ps, "po_rate", po.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 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 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}
|
||||
|
||||
@ -110,7 +110,7 @@ local function init(panel, num_units)
|
||||
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -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 fw_v = TextBox{parent=about,x=1,y=1,text="FW: 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,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}
|
||||
|
||||
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)
|
||||
|
@ -29,10 +29,10 @@ local function init(main)
|
||||
local units = iocontrol.get_db().units
|
||||
|
||||
-- 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}
|
||||
-- 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)
|
||||
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)")
|
||||
|
||||
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
|
||||
|
||||
|
@ -125,7 +125,7 @@ local function config_view(display)
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -173,11 +173,11 @@ local function config_view(display)
|
||||
|
||||
local ui_c_1 = Div{parent=ui_cfg,x=2,y=4,width=24}
|
||||
|
||||
TextBox{parent=ui_cfg,x=1,y=2,height=1,text=" Pocket UI",fg_bg=cpair(colors.black,colors.lime)}
|
||||
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 use the options below to customize formats."}
|
||||
|
||||
TextBox{parent=ui_c_1,x=1,y=5,height=1,text="Temperature Scale"}
|
||||
TextBox{parent=ui_c_1,x=1,y=5,text="Temperature Scale"}
|
||||
local temp_scale = RadioButton{parent=ui_c_1,x=1,y=6,default=ini_cfg.TempScale,options=types.TEMP_SCALE_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||
|
||||
local function submit_ui_opts()
|
||||
@ -199,24 +199,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}}
|
||||
|
||||
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=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}
|
||||
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}
|
||||
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}
|
||||
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 svr_c, crd_c, pkt_c = tonumber(svr_chan.get_value()), tonumber(crd_chan.get_value()), tonumber(pkt_chan.get_value())
|
||||
@ -230,15 +230,15 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
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=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}
|
||||
|
||||
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 timeout_val = tonumber(timeout.get_value())
|
||||
@ -252,13 +252,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=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=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 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 range_val = tonumber(range.get_value())
|
||||
@ -275,7 +275,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=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 function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||
@ -288,7 +288,7 @@ local function config_view(display)
|
||||
hide_key.set_value(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 v = key.get_value()
|
||||
@ -307,20 +307,20 @@ local function config_view(display)
|
||||
|
||||
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}
|
||||
|
||||
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 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}
|
||||
|
||||
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()
|
||||
if path.get_value() ~= "" then
|
||||
@ -350,7 +350,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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -405,7 +405,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.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()
|
||||
main_pane.set_value(1)
|
||||
@ -436,15 +436,15 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
|
||||
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
|
||||
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)}
|
||||
end
|
||||
end
|
||||
|
@ -342,10 +342,8 @@ function iocontrol.report_link_state(state, sv_addr, api_addr)
|
||||
io.ps.publish("crd_conn_quality", 0)
|
||||
end
|
||||
|
||||
if state == LINK_STATE.LINKED then
|
||||
io.ps.publish("sv_addr", sv_addr)
|
||||
io.ps.publish("api_addr", api_addr)
|
||||
end
|
||||
if sv_addr then io.ps.publish("sv_addr", sv_addr) end
|
||||
if api_addr then io.ps.publish("api_addr", api_addr) end
|
||||
end
|
||||
|
||||
-- determine supervisor connection quality (trip time)
|
||||
@ -525,7 +523,10 @@ function iocontrol.record_unit_data(data)
|
||||
reactor_state = 6 -- SCRAM
|
||||
rps_status = 2
|
||||
end
|
||||
else rps_status = 4 end
|
||||
else
|
||||
rps_status = 4
|
||||
reactor_state = 4
|
||||
end
|
||||
|
||||
-- update reactor/control status
|
||||
if unit.reactor_data.mek_status.status then
|
||||
|
@ -78,15 +78,16 @@ end
|
||||
---@enum POCKET_APP_ID
|
||||
local APP_ID = {
|
||||
ROOT = 1,
|
||||
LOADER = 2,
|
||||
-- main app pages
|
||||
UNITS = 2,
|
||||
GUIDE = 3,
|
||||
ABOUT = 4,
|
||||
UNITS = 3,
|
||||
GUIDE = 4,
|
||||
ABOUT = 5,
|
||||
-- diag app page
|
||||
ALARMS = 5,
|
||||
ALARMS = 6,
|
||||
-- other
|
||||
DUMMY = 6,
|
||||
NUM_APPS = 6
|
||||
DUMMY = 7,
|
||||
NUM_APPS = 7
|
||||
}
|
||||
|
||||
pocket.APP_ID = APP_ID
|
||||
@ -98,9 +99,9 @@ pocket.APP_ID = APP_ID
|
||||
---@field switcher function|nil function to switch between children
|
||||
---@field tasks table tasks to run while viewing this page
|
||||
|
||||
-- allocate the page navigation system
|
||||
---@param render_queue mqueue
|
||||
function pocket.init_nav(render_queue)
|
||||
-- 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
|
||||
@ -108,6 +109,7 @@ function pocket.init_nav(render_queue)
|
||||
containers = {},
|
||||
help_map = {},
|
||||
help_return = nil,
|
||||
loader_return = nil,
|
||||
cur_app = APP_ID.ROOT
|
||||
}
|
||||
|
||||
@ -143,10 +145,13 @@ function pocket.init_nav(render_queue)
|
||||
app.load = function () app.loaded = true end
|
||||
app.unload = function () app.loaded = false end
|
||||
|
||||
-- check which connections this requires
|
||||
-- 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)
|
||||
@ -254,7 +259,14 @@ function pocket.init_nav(render_queue)
|
||||
|
||||
local app = self.apps[app_id] ---@type pocket_app
|
||||
if app then
|
||||
if not app.loaded then render_queue.push_data(MQ__RENDER_DATA.LOAD_APP, app_id) end
|
||||
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)
|
||||
@ -267,6 +279,13 @@ function pocket.init_nav(render_queue)
|
||||
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)
|
||||
@ -351,15 +370,15 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
sv = {
|
||||
linked = false,
|
||||
addr = comms.BROADCAST,
|
||||
seq_num = 0,
|
||||
r_seq_num = nil, ---@type nil|integer
|
||||
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
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||
},
|
||||
api = {
|
||||
linked = false,
|
||||
addr = comms.BROADCAST,
|
||||
seq_num = 0,
|
||||
r_seq_num = nil, ---@type nil|integer
|
||||
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
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||
},
|
||||
establish_delay_counter = 0
|
||||
@ -585,8 +604,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
elseif r_chan == config.CRD_Channel then
|
||||
-- check sequence number
|
||||
if self.api.r_seq_num == nil then
|
||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
||||
elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
||||
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
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())
|
||||
return
|
||||
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
||||
@ -594,7 +613,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
"); channel in use by another system?")
|
||||
return
|
||||
else
|
||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
||||
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog on valid sequence number
|
||||
@ -705,8 +724,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
elseif r_chan == config.SVR_Channel then
|
||||
-- check sequence number
|
||||
if self.sv.r_seq_num == nil then
|
||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
||||
elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
||||
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
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())
|
||||
return
|
||||
elseif self.sv.linked and (src_addr ~= self.sv.addr) then
|
||||
@ -714,7 +733,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
"); channel in use by another system?")
|
||||
return
|
||||
else
|
||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
||||
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog on valid sequence number
|
||||
@ -844,6 +863,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
||||
---@nodiscard
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -20,7 +20,7 @@ local pocket = require("pocket.pocket")
|
||||
local renderer = require("pocket.renderer")
|
||||
local threads = require("pocket.threads")
|
||||
|
||||
local POCKET_VERSION = "v0.10.0-alpha"
|
||||
local POCKET_VERSION = "v0.11.2-alpha"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
@ -123,7 +123,7 @@ local function main()
|
||||
-- setup system
|
||||
----------------------------------------
|
||||
|
||||
smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render)
|
||||
smem_sys.nav = pocket.init_nav(__shared_memory)
|
||||
|
||||
-- message authentication init
|
||||
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
||||
|
@ -32,7 +32,7 @@ local function create_pages(root)
|
||||
|
||||
local alarm_test = Div{parent=root,x=1,y=1}
|
||||
|
||||
local alarm_app = db.nav.register_app(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)
|
||||
page.tasks = { db.diag.tone_test.get_tone_states }
|
||||
@ -46,13 +46,13 @@ local function create_pages(root)
|
||||
|
||||
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)}
|
||||
|
||||
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 = {}
|
||||
test_btns[1] = SwitchButton{parent=tones,text="TEST 1",min_width=8,active_fg_bg=c_wht_gray,callback=ttest.test_1}
|
||||
@ -75,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)}
|
||||
|
||||
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 = {}
|
||||
alarm_btns[1] = Checkbox{parent=alarms,label="BREACH",min_width=15,box_fg_bg=c_red_gray,callback=ttest.test_breach}
|
||||
@ -102,7 +102,7 @@ local function create_pages(root)
|
||||
|
||||
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_2 = IndicatorLight{parent=states,label="2",colors=c_blue_gray}
|
||||
local t_3 = IndicatorLight{parent=states,label="3",colors=c_blue_gray}
|
||||
|
@ -46,7 +46,7 @@ local function new_view(root)
|
||||
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...",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.cyan,colors._INHERIT)}
|
||||
|
||||
local load_pane = MultiPane{parent=main,x=1,y=1,panes={load_div,main}}
|
||||
@ -93,7 +93,7 @@ local function new_view(root)
|
||||
---@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",height=1,alignment=ALIGN.CENTER}
|
||||
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}
|
||||
@ -101,7 +101,7 @@ local function new_view(root)
|
||||
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",height=1,alignment=ALIGN.CENTER}
|
||||
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)}
|
||||
|
||||
@ -159,7 +159,7 @@ local function new_view(root)
|
||||
|
||||
util.nop()
|
||||
|
||||
TextBox{parent=use,y=1,text="System Usage",height=1,alignment=ALIGN.CENTER}
|
||||
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()
|
||||
@ -168,7 +168,7 @@ local function new_view(root)
|
||||
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",height=1,alignment=ALIGN.CENTER}
|
||||
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)
|
||||
@ -182,20 +182,21 @@ local function new_view(root)
|
||||
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",height=1,alignment=ALIGN.CENTER}
|
||||
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,dis_fg_bg=btn_disable,callback=function()end}.disable()
|
||||
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",height=1,alignment=ALIGN.CENTER}
|
||||
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()
|
||||
@ -204,10 +205,10 @@ local function new_view(root)
|
||||
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",height=1,alignment=ALIGN.CENTER}
|
||||
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, 120)
|
||||
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}
|
||||
|
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
|
@ -44,7 +44,7 @@ local function create_pages(root)
|
||||
|
||||
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_active = cpair(colors.white, colors.black)
|
||||
@ -59,36 +59,36 @@ local function create_pages(root)
|
||||
local config = pocket.config
|
||||
|
||||
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}
|
||||
|
||||
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
|
||||
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()
|
||||
TextBox{parent=nt_div,x=2,text="Supervisor Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||
local sv = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=nt_div,x=2,text="Supervisor Address",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
local sv = TextBox{parent=nt_div,x=2,text="",alignment=ALIGN.LEFT}
|
||||
|
||||
nt_div.line_break()
|
||||
TextBox{parent=nt_div,x=2,text="Coordinator Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||
local coord = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=nt_div,x=2,text="Coordinator Address",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
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)
|
||||
coord.register(db.ps, "api_addr", function (addr) coord.set_value(util.c(addr, ":", config.CRD_Channel)) end)
|
||||
|
||||
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")
|
||||
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
|
||||
|
||||
--#region Firmware Versions
|
||||
|
||||
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}
|
||||
|
||||
@ -96,44 +96,44 @@ local function create_pages(root)
|
||||
|
||||
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=db.version,height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=fw_list,x=2,text="Pocket Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
TextBox{parent=fw_list,x=2,text=db.version,alignment=ALIGN.LEFT}
|
||||
|
||||
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,height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=fw_list,x=2,text="Comms Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
TextBox{parent=fw_list,x=2,text=comms.version,alignment=ALIGN.LEFT}
|
||||
|
||||
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=comms.api_version,height=1,alignment=ALIGN.LEFT}
|
||||
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,alignment=ALIGN.LEFT}
|
||||
|
||||
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=util.version,height=1,alignment=ALIGN.LEFT}
|
||||
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,alignment=ALIGN.LEFT}
|
||||
|
||||
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=core.version,height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=fw_list,x=2,text="Graphics Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
TextBox{parent=fw_list,x=2,text=core.version,alignment=ALIGN.LEFT}
|
||||
|
||||
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,height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=fw_list,x=2,text="Lockbox Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
TextBox{parent=fw_list,x=2,text=lockbox.version,alignment=ALIGN.LEFT}
|
||||
|
||||
--#endregion
|
||||
|
||||
--#region Host Versions
|
||||
|
||||
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}
|
||||
|
||||
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=_VERSION,height=1,alignment=ALIGN.LEFT}
|
||||
TextBox{parent=hw_div,x=2,text="Lua Version",alignment=ALIGN.LEFT,fg_bg=label}
|
||||
TextBox{parent=hw_div,x=2,text=_VERSION,alignment=ALIGN.LEFT}
|
||||
|
||||
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}
|
||||
|
||||
--#endregion
|
||||
|
@ -58,7 +58,7 @@ local function new_view(root)
|
||||
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...",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}}
|
||||
@ -144,12 +144,12 @@ local function new_view(root)
|
||||
local u_page = app.new_page(nil, i)
|
||||
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=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")
|
||||
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 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 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}
|
||||
@ -196,7 +196,7 @@ local function new_view(root)
|
||||
alm_page.tasks = { update }
|
||||
nav_links[i].alarm = alm_page.nav_to
|
||||
|
||||
TextBox{parent=alm_div,y=1,text="Status Info Display",height=1,alignment=ALIGN.CENTER}
|
||||
TextBox{parent=alm_div,y=1,text="Status Info Display",alignment=ALIGN.CENTER}
|
||||
|
||||
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)}
|
||||
|
||||
@ -206,7 +206,7 @@ local function new_view(root)
|
||||
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,height=1,text=entry.text}
|
||||
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)}
|
||||
@ -216,7 +216,7 @@ local function new_view(root)
|
||||
local fg_bg = nil
|
||||
if item.color then fg_bg = cpair(item.color, colors.black) end
|
||||
|
||||
text = TextBox{parent=div,x=3,height=1,text=item.text,fg_bg=fg_bg}
|
||||
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)}
|
||||
@ -239,7 +239,7 @@ local function new_view(root)
|
||||
rps_page.tasks = { update }
|
||||
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}
|
||||
r_trip.register(u_ps, "U_RPS", r_trip.update)
|
||||
@ -290,7 +290,7 @@ local function new_view(root)
|
||||
|
||||
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_cflow = IconIndicator{parent=rcs_div,label="RCS Flow Lo",states=yel_ind_s}
|
||||
@ -309,7 +309,7 @@ local function new_view(root)
|
||||
c_mwrf.register(u_ps, "MaxWaterReturnFeed", c_mwrf.update)
|
||||
|
||||
-- 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_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}
|
||||
@ -319,7 +319,7 @@ local function new_view(root)
|
||||
c_sfm.register(u_ps, "SteamFeedMismatch", c_sfm.update)
|
||||
|
||||
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
|
||||
local wll = IconIndicator{parent=rcs_div,label="Boiler Water Lo",states=red_ind_s}
|
||||
|
@ -23,16 +23,16 @@ local function init(parent, y, is_api)
|
||||
local root = Div{parent=parent,x=1,y=1}
|
||||
|
||||
-- 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
|
||||
|
||||
if is_api then
|
||||
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
|
||||
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
|
||||
|
||||
return root
|
||||
|
@ -28,7 +28,7 @@ doc("TurbineTripAlarm", "Turbine Trip", "A turbine stopped rotating, likely due
|
||||
|
||||
docs.annunc = {
|
||||
unit = {
|
||||
main_section = {}, rps_section = {}, rcs_section = {}
|
||||
main_section = {}, rps_section = {}, rcs_section = {}, fac_section = {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,15 +78,35 @@ doc("TurbineOverSpeed", "Turbine Over Speed", "The turbine is at steam capacity,
|
||||
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.")
|
||||
@ -99,8 +119,11 @@ 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.")
|
||||
|
@ -2,39 +2,38 @@
|
||||
-- Pocket GUI Root
|
||||
--
|
||||
|
||||
local util = require("scada-common.util")
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local iocontrol = require("pocket.iocontrol")
|
||||
local pocket = require("pocket.pocket")
|
||||
local iocontrol = require("pocket.iocontrol")
|
||||
local pocket = require("pocket.pocket")
|
||||
|
||||
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 sys_apps = require("pocket.ui.apps.sys_apps")
|
||||
local unit_app = require("pocket.ui.apps.unit")
|
||||
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 conn_waiting = require("pocket.ui.components.conn_waiting")
|
||||
local home_page = require("pocket.ui.pages.home_page")
|
||||
|
||||
local home_page = require("pocket.ui.pages.home_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 MultiPane = require("graphics.elements.multipane")
|
||||
local TextBox = require("graphics.elements.textbox")
|
||||
|
||||
local Div = require("graphics.elements.div")
|
||||
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 Sidebar = require("graphics.elements.controls.sidebar")
|
||||
local PushButton = require("graphics.elements.controls.push_button")
|
||||
local Sidebar = require("graphics.elements.controls.sidebar")
|
||||
|
||||
local SignalBar = require("graphics.elements.indicators.signal")
|
||||
local SignalBar = require("graphics.elements.indicators.signal")
|
||||
|
||||
local ALIGN = core.ALIGN
|
||||
local cpair = core.cpair
|
||||
|
||||
local LINK_STATE = iocontrol.LINK_STATE
|
||||
|
||||
local APP_ID = pocket.APP_ID
|
||||
|
||||
-- create new main view
|
||||
@ -42,48 +41,34 @@ local APP_ID = pocket.APP_ID
|
||||
local function init(main)
|
||||
local db = iocontrol.get_db()
|
||||
|
||||
-- window header message
|
||||
TextBox{parent=main,y=1,text="EARLY ACCESS ALPHA S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
||||
-- window header message and connection status
|
||||
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 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("crd_conn_quality", crd_conn.set_value)
|
||||
|
||||
--#region root panel panes (connection screens + main screen)
|
||||
|
||||
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 start_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)
|
||||
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 root_pane = MultiPane{parent=main,x=1,y=2,panes={start_pane,main_pane}}
|
||||
|
||||
local page_div = Div{parent=main_pane,x=4,y=1}
|
||||
|
||||
-- create all the apps & pages
|
||||
home_page(page_div)
|
||||
|
||||
unit_app(page_div)
|
||||
guide_app(page_div)
|
||||
loader_app(page_div)
|
||||
sys_apps(page_div)
|
||||
diag_apps(page_div)
|
||||
dummy_app(page_div)
|
||||
|
||||
-- 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()})
|
||||
@ -93,7 +78,8 @@ local function init(main)
|
||||
|
||||
db.nav.open_app(APP_ID.ROOT)
|
||||
|
||||
--#endregion
|
||||
-- done with initial render, lets go!
|
||||
root_pane.set_value(2)
|
||||
end
|
||||
|
||||
return init
|
||||
|
@ -25,13 +25,13 @@ return function (data, base_page, title, items, scroll_height)
|
||||
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,height=1,alignment=ALIGN.CENTER}
|
||||
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,height=1,alignment=ALIGN.CENTER}
|
||||
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)}
|
||||
|
@ -54,7 +54,7 @@ local function new_view(root)
|
||||
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}
|
||||
|
||||
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=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}
|
||||
|
@ -43,7 +43,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update)
|
||||
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,height=1}
|
||||
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)
|
||||
|
||||
@ -52,17 +52,17 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update)
|
||||
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,height=1,fg_bg=label}
|
||||
TextBox{parent=blr_div,text="W",x=3,y=3,width=1,height=1,fg_bg=label}
|
||||
TextBox{parent=blr_div,text="S",x=19,y=3,width=1,height=1,fg_bg=label}
|
||||
TextBox{parent=blr_div,text="C",x=21,y=3,width=1,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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}
|
||||
|
||||
@ -74,7 +74,7 @@ return function (app, u_page, panes, blr_pane, b_id, ps, update)
|
||||
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,height=1,fg_bg=label}
|
||||
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)
|
||||
@ -88,41 +88,41 @@ return function (app, u_page, panes, blr_pane, b_id, ps, 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",height=1,alignment=ALIGN.CENTER}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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)
|
||||
|
@ -43,7 +43,7 @@ return function (app, u_page, panes, page_div, u_ps, update)
|
||||
local rct_page = app.new_page(u_page, #panes)
|
||||
rct_page.tasks = { update }
|
||||
|
||||
TextBox{parent=rct_div,y=1,text="Reactor",width=8,height=1}
|
||||
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)
|
||||
|
||||
@ -52,10 +52,10 @@ return function (app, u_page, panes, page_div, u_ps, update)
|
||||
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,height=1,fg_bg=label}
|
||||
TextBox{parent=rct_div,text="C",x=3,y=3,width=1,height=1,fg_bg=label}
|
||||
TextBox{parent=rct_div,text="H",x=19,y=3,width=1,height=1,fg_bg=label}
|
||||
TextBox{parent=rct_div,text="W",x=21,y=3,width=1,height=1,fg_bg=label}
|
||||
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)
|
||||
@ -78,9 +78,9 @@ return function (app, u_page, panes, page_div, u_ps, update)
|
||||
end
|
||||
end)
|
||||
|
||||
TextBox{parent=rct_div,text="Burn Rate",x=5,y=4,width=13,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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}
|
||||
|
||||
@ -99,9 +99,9 @@ return function (app, u_page, panes, page_div, u_ps, 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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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)
|
||||
@ -116,38 +116,38 @@ return function (app, u_page, panes, page_div, u_ps, 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",height=1,alignment=ALIGN.CENTER}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
TextBox{parent=rct_ext_div,text="Env. Loss",x=1,y=16,width=9,height=1,fg_bg=label}
|
||||
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}
|
||||
|
||||
|
@ -45,24 +45,24 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, update)
|
||||
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,height=1}
|
||||
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,height=1,fg_bg=label}
|
||||
TextBox{parent=tbn_div,text="E",x=21,y=3,width=1,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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="",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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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(util.joules_to_fe(val)) end)
|
||||
@ -88,23 +88,23 @@ return function (app, u_page, panes, tbn_pane, u_id, t_id, ps, 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",height=1,alignment=ALIGN.CENTER}
|
||||
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,height=1,fg_bg=label}
|
||||
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,height=1,fg_bg=label}
|
||||
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="",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", charge_amnt.update)
|
||||
|
||||
TextBox{parent=tbn_ext_div,text="Rotation Rate",x=1,y=9,width=13,height=1,fg_bg=label}
|
||||
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 ()
|
||||
|
@ -168,7 +168,7 @@ local function config_view(display)
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -233,9 +233,9 @@ local function config_view(display)
|
||||
|
||||
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,height=1,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)}
|
||||
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,height=1,text="Would you like to set this PLC as networked?"}
|
||||
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)}
|
||||
@ -248,13 +248,13 @@ local function config_view(display)
|
||||
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=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 #"}
|
||||
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,height=1,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||
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}
|
||||
|
||||
local function submit_id()
|
||||
local unit_id = tonumber(u_id.get_value())
|
||||
@ -285,10 +285,10 @@ local function config_view(display)
|
||||
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"}
|
||||
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,height=1,text="Bundled Redstone Configuration"}
|
||||
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)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
|
||||
@ -312,19 +312,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}}
|
||||
|
||||
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=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}
|
||||
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"}
|
||||
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,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 svr_c = tonumber(svr_chan.get_value())
|
||||
@ -346,16 +346,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=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}
|
||||
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"}
|
||||
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,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 timeout_val = tonumber(timeout.get_value())
|
||||
@ -380,7 +380,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=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 function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||
@ -390,7 +390,7 @@ local function config_view(display)
|
||||
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 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()
|
||||
@ -410,20 +410,20 @@ local function config_view(display)
|
||||
|
||||
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}
|
||||
|
||||
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 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 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
|
||||
@ -455,17 +455,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}}
|
||||
|
||||
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=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}
|
||||
|
||||
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)}
|
||||
_ = 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)}
|
||||
@ -492,7 +492,7 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
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}
|
||||
@ -546,7 +546,7 @@ local function config_view(display)
|
||||
clr_pane.set_value(1)
|
||||
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=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||
|
||||
@ -565,7 +565,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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -627,7 +627,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.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()
|
||||
main_pane.set_value(1)
|
||||
@ -660,15 +660,15 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
|
||||
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
|
||||
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)}
|
||||
end
|
||||
end
|
||||
|
@ -40,7 +40,7 @@ local function init(panel)
|
||||
|
||||
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)
|
||||
|
||||
--
|
||||
@ -115,7 +115,7 @@ local function init(panel)
|
||||
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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
|
||||
@ -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 fw_v = TextBox{parent=about,x=1,y=1,text="FW: 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,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}
|
||||
|
||||
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)
|
||||
|
@ -524,8 +524,8 @@ end
|
||||
function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
local self = {
|
||||
sv_addr = comms.BROADCAST,
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
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
|
||||
scrammed = false,
|
||||
linked = false,
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||
@ -571,33 +571,17 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
self.seq_num = self.seq_num + 1
|
||||
end
|
||||
|
||||
-- variable reactor status information, excluding heating rate
|
||||
-- dynamic reactor status information, excluding heating rate
|
||||
---@return table data_table, boolean faulted
|
||||
local function _reactor_status()
|
||||
local function _get_reactor_status()
|
||||
local fuel = nil
|
||||
local waste = nil
|
||||
local coolant = nil
|
||||
local hcoolant = nil
|
||||
|
||||
local data_table = {
|
||||
false, -- getStatus
|
||||
0, -- getBurnRate
|
||||
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 data_table = {}
|
||||
|
||||
reactor.__p_disable_afc()
|
||||
|
||||
local tasks = {
|
||||
function () data_table[1] = reactor.getStatus() end,
|
||||
@ -637,30 +621,32 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
data_table[16] = hcoolant.amount
|
||||
end
|
||||
|
||||
reactor.__p_enable_afc()
|
||||
|
||||
return data_table, reactor.__p_is_faulted()
|
||||
end
|
||||
|
||||
-- update the status cache if changed
|
||||
---@return boolean changed
|
||||
local function _update_status_cache()
|
||||
local status, faulted = _reactor_status()
|
||||
local status, faulted = _get_reactor_status()
|
||||
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
|
||||
if status[i] ~= self.status_cache[i] then
|
||||
changed = true
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
changed = true
|
||||
end
|
||||
else
|
||||
changed = true
|
||||
end
|
||||
|
||||
if changed and not faulted then
|
||||
self.status_cache = status
|
||||
if changed then
|
||||
self.status_cache = status
|
||||
end
|
||||
end
|
||||
|
||||
return changed
|
||||
@ -679,9 +665,11 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
_send(msg_type, { status })
|
||||
end
|
||||
|
||||
-- send structure properties (these should not change, server will cache these)
|
||||
-- send static structure properties, cached by server
|
||||
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 = {
|
||||
function () mek_data[1] = reactor.getLength() end,
|
||||
@ -705,6 +693,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
||||
self.resend_build = false
|
||||
end
|
||||
|
||||
reactor.__p_enable_afc()
|
||||
end
|
||||
|
||||
-- PUBLIC FUNCTIONS --
|
||||
@ -835,8 +825,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
if l_chan == config.PLC_Channel then
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = packet.scada_frame.seq_num()
|
||||
elseif self.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
||||
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
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())
|
||||
return
|
||||
elseif self.linked and (src_addr ~= self.sv_addr) then
|
||||
@ -844,7 +834,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
"); channel in use by another system?")
|
||||
return
|
||||
else
|
||||
self.r_seq_num = packet.scada_frame.seq_num()
|
||||
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed the watchdog first so it doesn't uhh...eat our packets :)
|
||||
@ -1030,10 +1020,9 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||
println_ts("linked!")
|
||||
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.linked = true
|
||||
self.r_seq_num = nil
|
||||
self.status_cache = nil
|
||||
|
||||
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 threads = require("reactor-plc.threads")
|
||||
|
||||
local R_PLC_VERSION = "v1.7.11"
|
||||
local R_PLC_VERSION = "v1.8.0"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
@ -264,7 +264,7 @@ local function config_view(display)
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -344,7 +344,7 @@ local function config_view(display)
|
||||
|
||||
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=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}
|
||||
|
||||
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 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}}
|
||||
|
||||
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=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}
|
||||
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}
|
||||
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 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=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}
|
||||
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"}
|
||||
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,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 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=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 function censor_key(enable) censor(tri(enable, "*", nil)) end
|
||||
@ -455,7 +455,7 @@ local function config_view(display)
|
||||
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 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()
|
||||
@ -475,20 +475,20 @@ local function config_view(display)
|
||||
|
||||
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}
|
||||
|
||||
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 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 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
|
||||
@ -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}}
|
||||
|
||||
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=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}
|
||||
|
||||
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)}
|
||||
_ = 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)}
|
||||
@ -553,7 +553,7 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
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)
|
||||
|
||||
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=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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -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.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)}
|
||||
|
||||
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}
|
||||
|
||||
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)}
|
||||
|
||||
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()
|
||||
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."}
|
||||
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}
|
||||
@ -752,15 +752,15 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
|
||||
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
|
||||
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)}
|
||||
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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -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=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)}
|
||||
|
||||
@ -934,8 +934,8 @@ local function config_view(display)
|
||||
---@cast entry ppm_entry
|
||||
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)}
|
||||
TextBox{parent=line,x=11,y=1,height=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=1,text=name,fg_bg=cpair(colors.black,bkg)}
|
||||
TextBox{parent=line,x=11,y=2,text=entry.type,fg_bg=cpair(colors.gray,bkg)}
|
||||
|
||||
alternate = not alternate
|
||||
end
|
||||
@ -948,7 +948,7 @@ local function config_view(display)
|
||||
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_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)
|
||||
|
||||
local function submit_manual_peri()
|
||||
@ -988,7 +988,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_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)
|
||||
|
||||
local function back_from_peri_opts()
|
||||
@ -1073,7 +1073,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=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=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 +1095,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}}
|
||||
|
||||
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 function rs_revert()
|
||||
@ -1125,7 +1125,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."}
|
||||
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)}
|
||||
|
||||
@ -1186,8 +1186,8 @@ local function config_view(display)
|
||||
-- add entries to redstone option list
|
||||
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)}
|
||||
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=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,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,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
||||
|
||||
for i = 1, rsio.NUM_PORTS do
|
||||
local p = PORT_DESC_MAP[i][1]
|
||||
@ -1197,8 +1197,8 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
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=22,y=1,height=1,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,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,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,colors.white)}
|
||||
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}
|
||||
@ -1212,10 +1212,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"}
|
||||
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}
|
||||
|
||||
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}
|
||||
|
||||
local function set_bundled(bundled)
|
||||
@ -1229,7 +1229,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.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)
|
||||
|
||||
local function back_from_rs_opts()
|
||||
@ -1285,7 +1285,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=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=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 +1386,9 @@ local function config_view(display)
|
||||
end
|
||||
|
||||
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=2,height=1,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=1,text="@ "..def.name,fg_bg=cpair(colors.black,colors.white)}
|
||||
TextBox{parent=line,x=1,y=2,text=status,fg_bg=cpair(color,colors.white)}
|
||||
TextBox{parent=line,x=1,y=3,text=desc,fg_bg=cpair(colors.gray,colors.white)}
|
||||
end
|
||||
|
||||
rs_import_list.remove_all()
|
||||
@ -1407,10 +1407,10 @@ local function config_view(display)
|
||||
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
||||
|
||||
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=2,y=1,width=14,height=1,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=40,y=1,height=1,text=unit,fg_bg=cpair(colors.gray,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,text=name}
|
||||
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,text=unit,fg_bg=cpair(colors.gray,colors.white)}
|
||||
end
|
||||
end
|
||||
|
||||
@ -1545,9 +1545,9 @@ local function config_view(display)
|
||||
end
|
||||
|
||||
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=2,height=1,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=1,text="@ "..def.name,fg_bg=cpair(colors.black,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,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)}
|
||||
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 +1613,10 @@ local function config_view(display)
|
||||
if def.color ~= nil then conn = def.side .. "/" .. rsio.color_name(def.color) end
|
||||
|
||||
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=2,y=1,width=14,height=1,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=33,y=1,width=1,height=1,text=unit,fg_bg=cpair(colors.gray,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,text=name}
|
||||
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,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=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
|
||||
|
@ -35,7 +35,7 @@ local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC
|
||||
local function init(panel, units)
|
||||
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
|
||||
@ -98,9 +98,9 @@ local function init(panel, units)
|
||||
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
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 fw_v = TextBox{parent=about,x=1,y=1,text="FW: 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,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}
|
||||
|
||||
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)
|
||||
@ -126,7 +126,7 @@ local function init(panel, units)
|
||||
|
||||
-- show routine statuses
|
||||
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}
|
||||
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
|
||||
end
|
||||
@ -144,13 +144,13 @@ local function init(panel, units)
|
||||
|
||||
-- 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 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)
|
||||
|
||||
-- assignment (unit # or facility)
|
||||
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
|
||||
|
||||
|
11
rtu/rtu.lua
11
rtu/rtu.lua
@ -284,8 +284,8 @@ end
|
||||
function rtu.comms(version, nic, conn_watchdog)
|
||||
local self = {
|
||||
sv_addr = comms.BROADCAST,
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
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
|
||||
txn_id = 0,
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||
}
|
||||
@ -442,8 +442,8 @@ function rtu.comms(version, nic, conn_watchdog)
|
||||
if l_chan == config.RTU_Channel then
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = packet.scada_frame.seq_num()
|
||||
elseif rtu_state.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
||||
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
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())
|
||||
return
|
||||
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?")
|
||||
return
|
||||
else
|
||||
self.r_seq_num = packet.scada_frame.seq_num()
|
||||
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog on valid sequence number
|
||||
@ -556,7 +556,6 @@ function rtu.comms(version, nic, conn_watchdog)
|
||||
-- establish allowed
|
||||
rtu_state.linked = true
|
||||
self.sv_addr = packet.scada_frame.src_addr()
|
||||
self.r_seq_num = nil
|
||||
println_ts("supervisor connection established")
|
||||
log.info("supervisor connection established")
|
||||
else
|
||||
|
@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||
|
||||
local RTU_VERSION = "v1.9.6"
|
||||
local RTU_VERSION = "v1.10.1"
|
||||
|
||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||
@ -93,14 +93,6 @@ local function main()
|
||||
network.init_mac(config.AuthKey)
|
||||
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
|
||||
audio.generate_tones()
|
||||
|
||||
@ -116,14 +108,15 @@ local function main()
|
||||
|
||||
-- RTU gateway devices (not RTU units)
|
||||
rtu_dev = {
|
||||
modem = ppm.get_wireless_modem(),
|
||||
sounders = {}
|
||||
},
|
||||
|
||||
-- system objects
|
||||
rtu_sys = {
|
||||
nic = network.nic(modem),
|
||||
rtu_comms = nil, ---@type rtu_comms
|
||||
conn_watchdog = nil, ---@type watchdog
|
||||
nic = nil, ---@type nic
|
||||
rtu_comms = nil, ---@type rtu_comms
|
||||
conn_watchdog = nil, ---@type watchdog
|
||||
units = {}
|
||||
},
|
||||
|
||||
@ -134,8 +127,9 @@ local function main()
|
||||
}
|
||||
|
||||
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
|
||||
@ -501,8 +495,6 @@ local function main()
|
||||
-- start system
|
||||
----------------------------------------
|
||||
|
||||
local rtu_state = __shared_memory.rtu_state
|
||||
|
||||
log.debug("boot> running sys_config()")
|
||||
|
||||
if sys_config() then
|
||||
@ -517,23 +509,33 @@ local function main()
|
||||
log.info("startup> running in headless mode without front panel")
|
||||
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
|
||||
local speakers = ppm.get_all_devices("speaker")
|
||||
for _, s in pairs(speakers) do
|
||||
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))
|
||||
end
|
||||
|
||||
databus.tx_hw_spkr_count(#__shared_memory.rtu_dev.sounders)
|
||||
databus.tx_hw_spkr_count(#smem_dev.sounders)
|
||||
|
||||
-- start connection watchdog
|
||||
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
||||
log.debug("startup> conn watchdog started")
|
||||
|
||||
-- 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)
|
||||
log.debug("startup> comms init")
|
||||
|
||||
|
@ -17,7 +17,7 @@ local max_distance = nil
|
||||
local comms = {}
|
||||
|
||||
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
||||
comms.version = "2.5.2"
|
||||
comms.version = "3.0.0"
|
||||
comms.api_version = "0.0.3"
|
||||
|
||||
---@enum PROTOCOL
|
||||
@ -240,6 +240,8 @@ function comms.scada_packet()
|
||||
---@nodiscard
|
||||
function public.modem_event() return self.modem_msg_in end
|
||||
---@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
|
||||
|
||||
---@nodiscard
|
||||
@ -278,7 +280,7 @@ function comms.authd_packet()
|
||||
src_addr = comms.BROADCAST,
|
||||
dest_addr = comms.BROADCAST,
|
||||
mac = "",
|
||||
payload = ""
|
||||
payload = {}
|
||||
}
|
||||
|
||||
---@class authd_packet
|
||||
@ -286,14 +288,13 @@ function comms.authd_packet()
|
||||
|
||||
-- make an authenticated SCADA packet
|
||||
---@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)
|
||||
self.valid = true
|
||||
self.src_addr = s_packet.src_addr()
|
||||
self.dest_addr = s_packet.dest_addr()
|
||||
self.payload = textutils.serialize(s_packet.raw_sendable(), { allow_repetitions = true, compact = true })
|
||||
self.mac = mac(self.payload)
|
||||
self.raw = { self.src_addr, self.dest_addr, self.mac, self.payload }
|
||||
self.mac = mac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||
self.raw = { self.src_addr, self.dest_addr, self.mac, s_packet.raw_sendable() }
|
||||
end
|
||||
|
||||
-- parse in a modem message as an authenticated SCADA packet
|
||||
@ -330,14 +331,14 @@ function comms.authd_packet()
|
||||
self.src_addr = nil
|
||||
self.dest_addr = nil
|
||||
self.mac = ""
|
||||
self.payload = ""
|
||||
self.payload = {}
|
||||
end
|
||||
|
||||
-- check if this packet is destined for this device
|
||||
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
|
||||
type(self.mac) == "string" and type(self.payload) == "string"
|
||||
type(self.mac) == "string" and type(self.payload) == "table"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -52,7 +52,6 @@ local alarms = {}
|
||||
|
||||
-- unit alarms
|
||||
|
||||
alarms.HIGH_TEMP = 1150 -- temp >= 1150K
|
||||
alarms.HIGH_WASTE = 0.85 -- fill > 85%
|
||||
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
|
||||
function network.nic(modem)
|
||||
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 = {}
|
||||
}
|
||||
|
||||
@ -114,7 +114,7 @@ function network.nic(modem)
|
||||
modem.open(channel)
|
||||
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
|
||||
if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end
|
||||
end
|
||||
@ -175,7 +175,7 @@ function network.nic(modem)
|
||||
---@param packet scada_packet packet
|
||||
function public.transmit(dest_channel, local_channel, packet)
|
||||
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
|
||||
-- local start = util.time_ms()
|
||||
@ -184,7 +184,7 @@ function network.nic(modem)
|
||||
---@cast tx_packet authd_packet
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
if a_packet.is_valid() then
|
||||
-- local start = util.time_ms()
|
||||
local packet_hmac = a_packet.mac()
|
||||
local msg = a_packet.data()
|
||||
local computed_hmac = compute_hmac(msg)
|
||||
s_packet.receive(side, sender, reply_to, a_packet.data(), distance)
|
||||
|
||||
if packet_hmac == computed_hmac then
|
||||
-- log.debug("crypto.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
||||
s_packet.receive(side, sender, reply_to, textutils.unserialize(msg), distance)
|
||||
s_packet.stamp_authenticated()
|
||||
else
|
||||
-- log.debug("crypto.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
||||
if s_packet.is_valid() then
|
||||
-- local start = util.time_ms()
|
||||
local computed_hmac = compute_hmac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||
|
||||
if a_packet.mac() == computed_hmac then
|
||||
-- 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
|
||||
else
|
||||
|
@ -24,7 +24,7 @@ local t_pack = table.pack
|
||||
local util = {}
|
||||
|
||||
-- scada-common version
|
||||
util.version = "1.3.1"
|
||||
util.version = "1.4.0"
|
||||
|
||||
util.TICK_TIME_S = 0.05
|
||||
util.TICK_TIME_MS = 50
|
||||
|
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
|
||||
|
||||
local BOOTLOADER_VERSION = "1.0"
|
||||
|
||||
println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
||||
println("BOOT> SCANNING FOR APPLICATIONS...")
|
||||
print("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
||||
print("BOOT> SCANNING FOR APPLICATIONS...")
|
||||
|
||||
local exit_code
|
||||
|
||||
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")
|
||||
elseif fs.exists("rtu/startup.lua") then
|
||||
println("BOOT> EXEC RTU STARTUP")
|
||||
print("BOOT> EXEC RTU STARTUP")
|
||||
exit_code = shell.execute("rtu/startup")
|
||||
elseif fs.exists("supervisor/startup.lua") then
|
||||
println("BOOT> EXEC SUPERVISOR STARTUP")
|
||||
print("BOOT> EXEC SUPERVISOR STARTUP")
|
||||
exit_code = shell.execute("supervisor/startup")
|
||||
elseif fs.exists("coordinator/startup.lua") then
|
||||
println("BOOT> EXEC COORDINATOR STARTUP")
|
||||
print("BOOT> EXEC COORDINATOR STARTUP")
|
||||
exit_code = shell.execute("coordinator/startup")
|
||||
elseif fs.exists("pocket/startup.lua") then
|
||||
println("BOOT> EXEC POCKET STARTUP")
|
||||
print("BOOT> EXEC POCKET STARTUP")
|
||||
exit_code = shell.execute("pocket/startup")
|
||||
else
|
||||
println("BOOT> NO SCADA STARTUP FOUND")
|
||||
println("BOOT> EXIT")
|
||||
print("BOOT> NO SCADA STARTUP FOUND")
|
||||
print("BOOT> EXIT")
|
||||
return false
|
||||
end
|
||||
|
||||
if not exit_code then println("BOOT> APPLICATION CRASHED") end
|
||||
if not exit_code then print("BOOT> APPLICATION CRASHED") end
|
||||
|
||||
return exit_code
|
||||
|
@ -159,7 +159,7 @@ local function config_view(display)
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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}
|
||||
|
||||
@ -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}}
|
||||
|
||||
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."}
|
||||
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 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}
|
||||
|
||||
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
|
||||
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 }
|
||||
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 any_missing = false
|
||||
@ -345,15 +345,15 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
TextBox{parent=div,x=1,y=1,width=33,height=1,text="Unit "..i.." will be connected to..."}
|
||||
TextBox{parent=div,x=6,y=2,width=3,height=1,text="..."}
|
||||
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,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 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 }
|
||||
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)
|
||||
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=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=12,y=3,height=1,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg}
|
||||
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,text="Facility Tanks Unit Tanks",fg_bg=g_lg_fg_bg}
|
||||
|
||||
--#region Tank Layout Visualizer
|
||||
|
||||
@ -416,25 +416,25 @@ local function config_view(display)
|
||||
-- draw unit tanks and their pipes
|
||||
for i = 1, 4 do
|
||||
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}
|
||||
local label = TextBox{parent=line,x=7,y=1,width=7,height=1,text="Tank ?"}
|
||||
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,text="Tank ?"}
|
||||
tool_ctl.vis_utanks[i] = { line = line, label = label }
|
||||
end
|
||||
|
||||
-- draw facility tank connections
|
||||
|
||||
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] = {
|
||||
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
|
||||
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_direct = TextBox{parent=line,x=9,y=2,width=4,height=1,text="\x8c\x8c\x8c\x8c",fg_bg=pipe_cpair}
|
||||
local label = TextBox{parent=line,x=1,y=2,width=7,height=1,text=""}
|
||||
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,text=""}
|
||||
tool_ctl.vis_ftanks[i] = { line = line, pipe_conn = pipe_conn, pipe_chain = pipe_chain, pipe_direct = pipe_direct, label = label }
|
||||
end
|
||||
|
||||
@ -608,32 +608,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}}
|
||||
|
||||
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=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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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}
|
||||
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 svr_c, plc_c, rtu_c = tonumber(svr_chan.get_value()), tonumber(plc_chan.get_value()), tonumber(rtu_chan.get_value())
|
||||
@ -649,24 +649,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=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=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}
|
||||
|
||||
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}
|
||||
|
||||
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}
|
||||
|
||||
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}
|
||||
|
||||
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 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 +680,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=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=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 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 range_val = tonumber(range.get_value())
|
||||
@ -703,7 +703,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=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 function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
||||
@ -713,7 +713,7 @@ local function config_view(display)
|
||||
hide_key.set_value(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 v = key.get_value()
|
||||
@ -733,20 +733,20 @@ local function config_view(display)
|
||||
|
||||
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}
|
||||
|
||||
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 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 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
|
||||
@ -774,17 +774,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}}
|
||||
|
||||
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=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}
|
||||
|
||||
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)}
|
||||
_ = 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)}
|
||||
@ -811,7 +811,7 @@ local function config_view(display)
|
||||
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}
|
||||
|
||||
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 +865,7 @@ local function config_view(display)
|
||||
clr_pane.set_value(1)
|
||||
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=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 +884,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}}
|
||||
|
||||
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)}
|
||||
|
||||
@ -961,7 +961,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.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()
|
||||
main_pane.set_value(1)
|
||||
@ -994,15 +994,15 @@ local function config_view(display)
|
||||
|
||||
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)}
|
||||
|
||||
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
|
||||
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)}
|
||||
end
|
||||
end
|
||||
@ -1013,8 +1013,8 @@ local function config_view(display)
|
||||
|
||||
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=i_err,x=1,y=1,height=1,text="There is a problem with your config.lua file:"}
|
||||
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,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=""}
|
||||
|
||||
|
@ -31,18 +31,18 @@ local function init(parent, id)
|
||||
|
||||
local ps_prefix = "pdg_" .. id .. "_"
|
||||
|
||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,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)}
|
||||
TextBox{parent=entry,x=1,y=3,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,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||
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)
|
||||
|
||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
||||
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
||||
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,fg_bg=label_fg}
|
||||
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}
|
||||
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_color", pdg_rtt.recolor)
|
||||
|
||||
|
@ -31,22 +31,22 @@ local function init(parent, id)
|
||||
|
||||
local ps_prefix = "rtu_" .. id .. "_"
|
||||
|
||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,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)}
|
||||
TextBox{parent=entry,x=1,y=3,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,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||
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)
|
||||
|
||||
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}
|
||||
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}
|
||||
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||
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,fg_bg=label_fg}
|
||||
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}
|
||||
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_color", rtu_rtt.recolor)
|
||||
|
||||
|
@ -40,7 +40,7 @@ local function init(panel)
|
||||
local label_fg = style.fp.label_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}
|
||||
|
||||
@ -66,15 +66,15 @@ local function init(panel)
|
||||
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
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
|
||||
--
|
||||
|
||||
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 comms_v = TextBox{parent=about,x=1,y=2,text="NT: 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}
|
||||
|
||||
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)
|
||||
@ -92,23 +92,23 @@ local function init(panel)
|
||||
local ps_prefix = "plc_" .. i .. "_"
|
||||
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=2,text="UNIT "..i,alignment=ALIGN.CENTER,width=8,height=1,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=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,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)}
|
||||
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)
|
||||
|
||||
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1}
|
||||
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||
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,fg_bg=label_fg}
|
||||
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}
|
||||
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_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)}
|
||||
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}
|
||||
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,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,fg_bg=label_d_fg}
|
||||
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}
|
||||
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||
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,fg_bg=label_fg}
|
||||
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}
|
||||
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_color", crd_rtt.recolor)
|
||||
|
||||
|
@ -43,12 +43,13 @@ local PERIODICS = {
|
||||
---@nodiscard
|
||||
---@param id integer session ID
|
||||
---@param s_addr integer device source address
|
||||
---@param i_seq_num integer initial sequence number
|
||||
---@param in_queue mqueue in message queue
|
||||
---@param out_queue mqueue out message queue
|
||||
---@param timeout number communications timeout
|
||||
---@param facility facility facility data table
|
||||
---@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
|
||||
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 = {
|
||||
units = facility.get_units(),
|
||||
-- connection properties
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||
r_seq_num = i_seq_num + 1,
|
||||
connected = true,
|
||||
conn_watchdog = util.new_watchdog(timeout),
|
||||
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
|
||||
local function _handle_packet(pkt)
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
||||
if self.r_seq_num ~= 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())
|
||||
return
|
||||
else
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog
|
||||
|
@ -48,12 +48,13 @@ local PERIODICS = {
|
||||
---@nodiscard
|
||||
---@param id integer session ID
|
||||
---@param s_addr integer device source address
|
||||
---@param i_seq_num integer initial sequence number
|
||||
---@param reactor_id integer reactor ID
|
||||
---@param in_queue mqueue in message queue
|
||||
---@param out_queue mqueue out message queue
|
||||
---@param timeout number communications timeout
|
||||
---@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
|
||||
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,
|
||||
auto_lock = false,
|
||||
-- connection properties
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||
r_seq_num = i_seq_num + 1,
|
||||
connected = true,
|
||||
received_struct = 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
|
||||
---@param rps_status table
|
||||
local function _copy_rps_status(rps_status)
|
||||
self.sDB.rps_tripped = rps_status[1]
|
||||
self.sDB.rps_trip_cause = rps_status[2]
|
||||
self.sDB.rps_status.high_dmg = rps_status[3]
|
||||
self.sDB.rps_status.high_temp = rps_status[4]
|
||||
self.sDB.rps_status.low_cool = rps_status[5]
|
||||
self.sDB.rps_status.ex_waste = rps_status[6]
|
||||
self.sDB.rps_status.ex_hcool = rps_status[7]
|
||||
self.sDB.rps_status.no_fuel = rps_status[8]
|
||||
self.sDB.rps_status.fault = rps_status[9]
|
||||
self.sDB.rps_status.timeout = rps_status[10]
|
||||
self.sDB.rps_status.manual = rps_status[11]
|
||||
self.sDB.rps_status.automatic = rps_status[12]
|
||||
self.sDB.rps_status.sys_fail = rps_status[13]
|
||||
self.sDB.rps_status.force_dis = rps_status[14]
|
||||
local rps = self.sDB.rps_status
|
||||
|
||||
self.sDB.rps_tripped = rps_status[1]
|
||||
self.sDB.rps_trip_cause = rps_status[2]
|
||||
|
||||
rps.high_dmg = rps_status[3]
|
||||
rps.high_temp = rps_status[4]
|
||||
rps.low_cool = rps_status[5]
|
||||
rps.ex_waste = rps_status[6]
|
||||
rps.ex_hcool = rps_status[7]
|
||||
rps.no_fuel = rps_status[8]
|
||||
rps.fault = rps_status[9]
|
||||
rps.timeout = rps_status[10]
|
||||
rps.manual = rps_status[11]
|
||||
rps.automatic = rps_status[12]
|
||||
rps.sys_fail = rps_status[13]
|
||||
rps.force_dis = rps_status[14]
|
||||
end
|
||||
|
||||
-- copy in the reactor status
|
||||
---@param mek_data table
|
||||
local function _copy_status(mek_data)
|
||||
local stat = self.sDB.mek_status
|
||||
local struct = self.sDB.mek_struct
|
||||
|
||||
-- copy status information
|
||||
self.sDB.mek_status.status = mek_data[1]
|
||||
self.sDB.mek_status.burn_rate = mek_data[2]
|
||||
self.sDB.mek_status.act_burn_rate = mek_data[3]
|
||||
self.sDB.mek_status.temp = mek_data[4]
|
||||
self.sDB.mek_status.damage = mek_data[5]
|
||||
self.sDB.mek_status.boil_eff = mek_data[6]
|
||||
self.sDB.mek_status.env_loss = mek_data[7]
|
||||
stat.status = mek_data[1]
|
||||
stat.burn_rate = mek_data[2]
|
||||
stat.act_burn_rate = mek_data[3]
|
||||
stat.temp = mek_data[4]
|
||||
stat.damage = mek_data[5]
|
||||
stat.boil_eff = mek_data[6]
|
||||
stat.env_loss = mek_data[7]
|
||||
|
||||
-- copy container information
|
||||
self.sDB.mek_status.fuel = mek_data[8]
|
||||
self.sDB.mek_status.fuel_fill = mek_data[9]
|
||||
self.sDB.mek_status.waste = mek_data[10]
|
||||
self.sDB.mek_status.waste_fill = mek_data[11]
|
||||
self.sDB.mek_status.ccool_type = mek_data[12]
|
||||
self.sDB.mek_status.ccool_amnt = mek_data[13]
|
||||
self.sDB.mek_status.ccool_fill = mek_data[14]
|
||||
self.sDB.mek_status.hcool_type = mek_data[15]
|
||||
self.sDB.mek_status.hcool_amnt = mek_data[16]
|
||||
self.sDB.mek_status.hcool_fill = mek_data[17]
|
||||
stat.fuel = mek_data[8]
|
||||
stat.fuel_fill = mek_data[9]
|
||||
stat.waste = mek_data[10]
|
||||
stat.waste_fill = mek_data[11]
|
||||
stat.ccool_type = mek_data[12]
|
||||
stat.ccool_amnt = mek_data[13]
|
||||
stat.ccool_fill = mek_data[14]
|
||||
stat.hcool_type = mek_data[15]
|
||||
stat.hcool_amnt = mek_data[16]
|
||||
stat.hcool_fill = mek_data[17]
|
||||
|
||||
-- update computable fields if we have our structure
|
||||
if self.received_struct then
|
||||
self.sDB.mek_status.fuel_need = self.sDB.mek_struct.fuel_cap - self.sDB.mek_status.fuel_fill
|
||||
self.sDB.mek_status.waste_need = self.sDB.mek_struct.waste_cap - self.sDB.mek_status.waste_fill
|
||||
self.sDB.mek_status.cool_need = self.sDB.mek_struct.ccool_cap - self.sDB.mek_status.ccool_fill
|
||||
self.sDB.mek_status.hcool_need = self.sDB.mek_struct.hcool_cap - self.sDB.mek_status.hcool_fill
|
||||
stat.fuel_need = struct.fuel_cap - stat.fuel_fill
|
||||
stat.waste_need = struct.waste_cap - stat.waste_fill
|
||||
stat.cool_need = struct.ccool_cap - stat.ccool_fill
|
||||
stat.hcool_need = struct.hcool_cap - stat.hcool_fill
|
||||
end
|
||||
end
|
||||
|
||||
-- copy in the reactor structure
|
||||
---@param mek_data table
|
||||
local function _copy_struct(mek_data)
|
||||
self.sDB.mek_struct.length = mek_data[1]
|
||||
self.sDB.mek_struct.width = mek_data[2]
|
||||
self.sDB.mek_struct.height = mek_data[3]
|
||||
self.sDB.mek_struct.min_pos = mek_data[4]
|
||||
self.sDB.mek_struct.max_pos = mek_data[5]
|
||||
self.sDB.mek_struct.heat_cap = mek_data[6]
|
||||
self.sDB.mek_struct.fuel_asm = mek_data[7]
|
||||
self.sDB.mek_struct.fuel_sa = mek_data[8]
|
||||
self.sDB.mek_struct.fuel_cap = mek_data[9]
|
||||
self.sDB.mek_struct.waste_cap = mek_data[10]
|
||||
self.sDB.mek_struct.ccool_cap = mek_data[11]
|
||||
self.sDB.mek_struct.hcool_cap = mek_data[12]
|
||||
self.sDB.mek_struct.max_burn = mek_data[13]
|
||||
local struct = self.sDB.mek_struct
|
||||
|
||||
struct.length = mek_data[1]
|
||||
struct.width = mek_data[2]
|
||||
struct.height = mek_data[3]
|
||||
struct.min_pos = mek_data[4]
|
||||
struct.max_pos = mek_data[5]
|
||||
struct.heat_cap = mek_data[6]
|
||||
struct.fuel_asm = mek_data[7]
|
||||
struct.fuel_sa = mek_data[8]
|
||||
struct.fuel_cap = mek_data[9]
|
||||
struct.waste_cap = mek_data[10]
|
||||
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
|
||||
|
||||
-- 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
|
||||
local function _handle_packet(pkt)
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
||||
if self.r_seq_num ~= 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())
|
||||
return
|
||||
else
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- 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
|
||||
-- status packet received, update data
|
||||
if pkt.length >= 5 then
|
||||
if (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") 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
|
||||
_handle_status(pkt)
|
||||
else
|
||||
log.debug(log_header .. "RPLC status packet length mismatch")
|
||||
end
|
||||
elseif pkt.type == RPLC_TYPE.MEK_STRUCT then
|
||||
-- received reactor structure, record it
|
||||
if pkt.length == 14 then
|
||||
local status = pcall(_copy_struct, pkt.data)
|
||||
if status then
|
||||
-- copied in structure data OK
|
||||
_compute_op_temps()
|
||||
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
|
||||
if pkt.length == 13 then
|
||||
_copy_struct(pkt.data)
|
||||
_compute_op_temps()
|
||||
self.received_struct = true
|
||||
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
||||
else
|
||||
log.debug(log_header .. "RPLC struct packet length mismatch")
|
||||
end
|
||||
|
@ -30,12 +30,13 @@ local PERIODICS = {
|
||||
---@nodiscard
|
||||
---@param id integer session ID
|
||||
---@param s_addr integer device source address
|
||||
---@param i_seq_num integer initial sequence number
|
||||
---@param in_queue mqueue in message queue
|
||||
---@param out_queue mqueue out message queue
|
||||
---@param timeout number communications timeout
|
||||
---@param facility facility facility data table
|
||||
---@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
|
||||
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 = {
|
||||
-- connection properties
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||
r_seq_num = i_seq_num + 1,
|
||||
connected = true,
|
||||
conn_watchdog = util.new_watchdog(timeout),
|
||||
last_rtt = 0,
|
||||
@ -93,13 +94,11 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
|
||||
---@param pkt mgmt_frame
|
||||
local function _handle_packet(pkt)
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
||||
if self.r_seq_num ~= 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())
|
||||
return
|
||||
else
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog
|
||||
|
@ -34,13 +34,14 @@ local PERIODICS = {
|
||||
---@nodiscard
|
||||
---@param id integer session ID
|
||||
---@param s_addr integer device source address
|
||||
---@param i_seq_num integer initial sequence number
|
||||
---@param in_queue mqueue in message queue
|
||||
---@param out_queue mqueue out message queue
|
||||
---@param timeout number communications timeout
|
||||
---@param advertisement table RTU device advertisement
|
||||
---@param facility facility facility data table
|
||||
---@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
|
||||
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,
|
||||
fac_units = facility.get_units(),
|
||||
-- connection properties
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||
r_seq_num = i_seq_num + 1,
|
||||
connected = true,
|
||||
conn_watchdog = util.new_watchdog(timeout),
|
||||
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
|
||||
local function _handle_packet(pkt)
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
||||
if self.r_seq_num ~= 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())
|
||||
return
|
||||
else
|
||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
||||
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||
end
|
||||
|
||||
-- feed watchdog
|
||||
|
@ -273,11 +273,12 @@ end
|
||||
|
||||
-- establish a new PLC session
|
||||
---@nodiscard
|
||||
---@param source_addr integer
|
||||
---@param for_reactor integer
|
||||
---@param version string
|
||||
---@param source_addr integer PLC computer ID
|
||||
---@param i_seq_num integer initial (most recent) sequence number
|
||||
---@param for_reactor integer unit ID
|
||||
---@param version string PLC version
|
||||
---@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
|
||||
---@class plc_session_struct
|
||||
local plc_s = {
|
||||
@ -294,7 +295,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
||||
|
||||
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)
|
||||
|
||||
local units = self.facility.get_units()
|
||||
@ -320,13 +321,14 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
||||
end
|
||||
end
|
||||
|
||||
-- establish a new RTU session
|
||||
-- establish a new RTU gateway session
|
||||
---@nodiscard
|
||||
---@param source_addr integer
|
||||
---@param advertisement table
|
||||
---@param version string
|
||||
---@param source_addr integer RTU gateway computer ID
|
||||
---@param i_seq_num integer initial (most recent) sequence number
|
||||
---@param advertisement table RTU capability advertisement
|
||||
---@param version string RTU gateway version
|
||||
---@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
|
||||
local rtu_s = {
|
||||
s_type = "rtu",
|
||||
@ -341,7 +343,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
||||
|
||||
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)
|
||||
|
||||
local mt = {
|
||||
@ -362,10 +364,11 @@ end
|
||||
|
||||
-- establish a new coordinator session
|
||||
---@nodiscard
|
||||
---@param source_addr integer
|
||||
---@param version string
|
||||
---@param source_addr integer coordinator computer ID
|
||||
---@param i_seq_num integer initial (most recent) sequence number
|
||||
---@param version string coordinator version
|
||||
---@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
|
||||
---@class crd_session_struct
|
||||
local crd_s = {
|
||||
@ -381,7 +384,7 @@ function svsessions.establish_crd_session(source_addr, version)
|
||||
|
||||
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)
|
||||
|
||||
local mt = {
|
||||
@ -406,10 +409,11 @@ end
|
||||
|
||||
-- establish a new pocket diagnostics session
|
||||
---@nodiscard
|
||||
---@param source_addr integer
|
||||
---@param version string
|
||||
---@param source_addr integer pocket computer ID
|
||||
---@param i_seq_num integer initial (most recent) sequence number
|
||||
---@param version string pocket version
|
||||
---@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
|
||||
local pdg_s = {
|
||||
s_type = "pkt",
|
||||
@ -424,7 +428,7 @@ function svsessions.establish_pdg_session(source_addr, version)
|
||||
|
||||
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)
|
||||
|
||||
local mt = {
|
||||
|
@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local svsessions = require("supervisor.session.svsessions")
|
||||
|
||||
local SUPERVISOR_VERSION = "v1.3.12"
|
||||
local SUPERVISOR_VERSION = "v1.4.0"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
@ -214,7 +214,7 @@ local function main()
|
||||
elseif event == "modem_message" then
|
||||
-- got a packet
|
||||
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
|
||||
event == "double_click" then
|
||||
-- handle a mouse event
|
||||
|
@ -191,283 +191,282 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
end
|
||||
|
||||
-- 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)
|
||||
if packet ~= nil then
|
||||
local l_chan = packet.scada_frame.local_channel()
|
||||
local r_chan = packet.scada_frame.remote_channel()
|
||||
local src_addr = packet.scada_frame.src_addr()
|
||||
local protocol = packet.scada_frame.protocol()
|
||||
local l_chan = packet.scada_frame.local_channel()
|
||||
local r_chan = packet.scada_frame.remote_channel()
|
||||
local src_addr = packet.scada_frame.src_addr()
|
||||
local protocol = packet.scada_frame.protocol()
|
||||
local i_seq_num = packet.scada_frame.seq_num()
|
||||
|
||||
if l_chan ~= config.SVR_Channel then
|
||||
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
||||
elseif r_chan == config.PLC_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_plc_session(src_addr)
|
||||
if l_chan ~= config.SVR_Channel then
|
||||
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
||||
elseif r_chan == config.PLC_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_plc_session(src_addr)
|
||||
|
||||
if protocol == PROTOCOL.RPLC then
|
||||
---@cast packet rplc_frame
|
||||
-- reactor PLC 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("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
|
||||
if protocol == PROTOCOL.RPLC then
|
||||
---@cast packet rplc_frame
|
||||
-- reactor PLC packet
|
||||
if session ~= nil then
|
||||
-- pass the packet onto the session handler
|
||||
session.in_queue.push_packet(packet)
|
||||
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
|
||||
elseif r_chan == config.RTU_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_rtu_session(src_addr)
|
||||
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]
|
||||
|
||||
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]
|
||||
|
||||
-- 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, 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)
|
||||
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
|
||||
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]
|
||||
_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, i_seq_num, reactor_id, firmware_v)
|
||||
|
||||
-- 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, 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 plc_id == false then
|
||||
-- reactor already has a PLC assigned
|
||||
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
|
||||
|
||||
_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(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)
|
||||
end
|
||||
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)
|
||||
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))
|
||||
log.debug("invalid establish packet (on PLC channel)")
|
||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||
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, 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"))
|
||||
-- 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
|
||||
log.debug("received packet for unknown channel " .. r_chan, true)
|
||||
log.debug(util.c("illegal packet type ", protocol, " on PLC channel"))
|
||||
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user