Merge branch 'devel' into 145-graphical-configure-utilities

This commit is contained in:
Mikayla Fischler 2023-09-03 19:38:17 -04:00
commit 1525ed9d60
83 changed files with 937 additions and 1054 deletions

View File

@ -24,6 +24,7 @@
}, },
"Lua.hint.setType": true, "Lua.hint.setType": true,
"Lua.diagnostics.disable": [ "Lua.diagnostics.disable": [
"duplicate-set-field" "duplicate-set-field",
"inject-field"
] ]
} }

View File

@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
local function println(message) print(tostring(message)) end local function println(message) print(tostring(message)) end
local function print(message) term.write(tostring(message)) end local function print(message) term.write(tostring(message)) end
local CCMSI_VERSION = "v1.9a" local CCMSI_VERSION = "v1.10"
local install_dir = "/.install-cache" local install_dir = "/.install-cache"
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/" local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
@ -172,7 +172,7 @@ local function clean(manifest)
table.insert(tree, "install_manifest.json") table.insert(tree, "install_manifest.json")
table.insert(tree, "ccmsi.lua") table.insert(tree, "ccmsi.lua")
table.insert(tree, "log.txt") table.insert(tree, "log.txt") -- this won't necessarily work correctly
lgray() lgray()
@ -205,8 +205,7 @@ if #opts == 0 or opts[1] == "help" then
lgray() lgray()
println(" install - fresh install, overwrites config") println(" install - fresh install, overwrites config")
println(" update - update files EXCEPT for config/logs") println(" update - update files EXCEPT for config/logs")
println(" remove - delete files EXCEPT for config/logs") println(" uninstall - delete files INCLUDING config/logs")
println(" purge - delete files INCLUDING config/logs")
white();println("<app>");lgray() white();println("<app>");lgray()
println(" reactor-plc - reactor PLC firmware") println(" reactor-plc - reactor PLC firmware")
println(" rtu - RTU firmware") println(" rtu - RTU firmware")
@ -218,7 +217,7 @@ if #opts == 0 or opts[1] == "help" then
lgray();println(" main (default) | latest | devel");white() lgray();println(" main (default) | latest | devel");white()
return return
else else
mode = get_opt(opts[1], { "check", "install", "update", "remove", "purge" }) mode = get_opt(opts[1], { "check", "install", "update", "uninstall" })
if mode == nil then if mode == nil then
red();println("Unrecognized mode.");white() red();println("Unrecognized mode.");white()
return return
@ -310,7 +309,7 @@ elseif mode == "install" or mode == "update" then
ver.lockbox.v_local = lmnf.versions.lockbox ver.lockbox.v_local = lmnf.versions.lockbox
if lmnf.versions[app] == nil then if lmnf.versions[app] == nil then
red();println("Another application is already installed, please purge it before installing a new application.");white() red();println("Another application is already installed, please uninstall it before installing a new application.");white()
return return
end end
end end
@ -389,9 +388,10 @@ elseif mode == "install" or mode == "update" then
-- check space constraints -- check space constraints
if space_available < space_required then if space_available < space_required then
single_file_mode = true single_file_mode = true
yellow();println("WARNING: Insufficient space available for a full download!");white() yellow();println("NOTICE: Insufficient space available for a full cached download!");white()
println("Files can be downloaded one by one, so if you are replacing a current install this will not be a problem unless installation fails.") lgray();println("Files can instead be downloaded one by one. If you are replacing a current install this may corrupt your install ONLY if it fails (such as a sudden network issue). If that occurs, you can still try again.")
if mode == "update" then println("If installation still fails, delete this device's log file or uninstall the app (not purge) and try again.") end if mode == "update" then println("If installation still fails, delete this device's log file and/or any unrelated files you have on this computer then try again.") end
white();
if not ask_y_n("Do you wish to continue", false) then if not ask_y_n("Do you wish to continue", false) then
println("Operation cancelled.") println("Operation cancelled.")
return return
@ -521,22 +521,19 @@ elseif mode == "install" or mode == "update" then
else println("Update failed, files may have been skipped.") end else println("Update failed, files may have been skipped.") end
end end
end end
elseif mode == "remove" or mode == "purge" then elseif mode == "uninstall" then
local ok, manifest = read_local_manifest() local ok, manifest = read_local_manifest()
if not ok then if not ok then
red();println("Error parsing local installation manifest.");white() red();println("Error parsing local installation manifest.");white()
return return
elseif mode == "remove" and manifest.versions[app] == nil then end
red();println(app .. " is not installed, cannot remove.");white()
if manifest.versions[app] == nil then
red();println("Error: '" .. app .. "' is not installed.")
return return
end end
orange() orange();println("Uninstalling all " .. app .. " files...")
if mode == "remove" then
println("Removing all " .. app .. " files except for config and log...")
elseif mode == "purge" then
println("Purging all " .. app .. " files including config and log...")
end
-- ask for confirmation -- ask for confirmation
if not ask_y_n("Continue", false) then return end if not ask_y_n("Continue", false) then return end
@ -550,9 +547,9 @@ elseif mode == "remove" or mode == "purge" then
table.insert(dependencies, app) table.insert(dependencies, app)
-- delete log file if purging -- delete log file
lgray() lgray()
if mode == "purge" and fs.exists(config_file) then if fs.exists(config_file) then
local log_deleted = pcall(function () local log_deleted = pcall(function ()
local config = require(app .. ".config") local config = require(app .. ".config")
if fs.exists(config.LOG_PATH) then if fs.exists(config.LOG_PATH) then
@ -568,53 +565,27 @@ elseif mode == "remove" or mode == "purge" then
end end
end end
-- delete all files except config unless purging -- delete all installed files
for _, dependency in pairs(dependencies) do for _, dependency in pairs(dependencies) do
local files = file_list[dependency] local files = file_list[dependency]
for _, file in pairs(files) do for _, file in pairs(files) do
if mode == "purge" or file ~= config_file then if fs.exists(file) then fs.delete(file);println("deleted " .. file) end
if fs.exists(file) then fs.delete(file);println("deleted " .. file) end
end
end end
-- delete folders that we should be deleteing local folder = files[1]
if mode == "purge" or dependency ~= app then while true do
local folder = files[1] local dir = fs.getDir(folder)
while true do if dir == "" or dir == ".." then break else folder = dir end
local dir = fs.getDir(folder) end
if dir == "" or dir == ".." then break else folder = dir end
end
if fs.isDir(folder) then if fs.isDir(folder) then
fs.delete(folder) fs.delete(folder)
println("deleted directory " .. folder) println("deleted directory " .. folder)
end
elseif dependency == app then
-- delete individual subdirectories so we can leave the config
for _, folder in pairs(files) do
while true do
local dir = fs.getDir(folder)
if dir == "" or dir == ".." or dir == app then break else folder = dir end
end
if folder ~= app and fs.isDir(folder) then
fs.delete(folder);println("deleted app subdirectory " .. folder)
end
end
end end
end end
-- only delete manifest if purging fs.delete("install_manifest.json")
if mode == "purge" then println("deleted install_manifest.json")
fs.delete("install_manifest.json")
println("deleted install_manifest.json")
else
-- remove all data from versions list to show nothing is installed
manifest.versions = {}
local imfile = fs.open("install_manifest.json", "w")
imfile.write(textutils.serializeJSON(manifest))
imfile.close()
end
green();println("Done!") green();println("Done!")
end end

View File

@ -17,8 +17,8 @@ local println = util.println
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local DEVICE_TYPE = comms.DEVICE_TYPE local DEVICE_TYPE = comms.DEVICE_TYPE
local ESTABLISH_ACK = comms.ESTABLISH_ACK local ESTABLISH_ACK = comms.ESTABLISH_ACK
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local SCADA_CRDN_TYPE = comms.SCADA_CRDN_TYPE local CRDN_TYPE = comms.CRDN_TYPE
local UNIT_COMMAND = comms.UNIT_COMMAND local UNIT_COMMAND = comms.UNIT_COMMAND
local FAC_COMMAND = comms.FAC_COMMAND local FAC_COMMAND = comms.FAC_COMMAND
@ -54,9 +54,9 @@ end
function coordinator.configure_monitors(num_units, disable_flow_view) function coordinator.configure_monitors(num_units, disable_flow_view)
---@class monitors_struct ---@class monitors_struct
local monitors = { local monitors = {
primary = nil, primary = nil, ---@type table|nil
primary_name = "", primary_name = "",
flow = nil, flow = nil, ---@type table|nil
flow_name = "", flow_name = "",
unit_displays = {}, unit_displays = {},
unit_name_map = {} unit_name_map = {}
@ -279,7 +279,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
apisessions.init(nic) apisessions.init(nic)
-- send a packet to the supervisor -- send a packet to the supervisor
---@param msg_type SCADA_MGMT_TYPE|SCADA_CRDN_TYPE ---@param msg_type MGMT_TYPE|CRDN_TYPE
---@param msg table ---@param msg table
local function _send_sv(protocol, msg_type, msg) local function _send_sv(protocol, msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -307,7 +307,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
local m_pkt = comms.mgmt_packet() local m_pkt = comms.mgmt_packet()
m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack }) m_pkt.make(MGMT_TYPE.ESTABLISH, { ack })
s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
nic.transmit(pkt_channel, crd_channel, s_pkt) nic.transmit(pkt_channel, crd_channel, s_pkt)
@ -316,13 +316,13 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
-- attempt connection establishment -- attempt connection establishment
local function _send_establish() local function _send_establish()
_send_sv(PROTOCOL.SCADA_MGMT, SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.CRDN }) _send_sv(PROTOCOL.SCADA_MGMT, MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.CRD })
end end
-- keep alive ack -- keep alive ack
---@param srv_time integer ---@param srv_time integer
local function _send_keep_alive_ack(srv_time) local function _send_keep_alive_ack(srv_time)
_send_sv(PROTOCOL.SCADA_MGMT, SCADA_MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() }) _send_sv(PROTOCOL.SCADA_MGMT, MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() })
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -394,20 +394,20 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
self.sv_linked = false self.sv_linked = false
self.sv_r_seq_num = nil self.sv_r_seq_num = nil
iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED) iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED)
_send_sv(PROTOCOL.SCADA_MGMT, SCADA_MGMT_TYPE.CLOSE, {}) _send_sv(PROTOCOL.SCADA_MGMT, MGMT_TYPE.CLOSE, {})
end end
-- send a facility command -- send a facility command
---@param cmd FAC_COMMAND command ---@param cmd FAC_COMMAND command
---@param option any? optional option options for the optional options (like waste mode) ---@param option any? optional option options for the optional options (like waste mode)
function public.send_fac_command(cmd, option) function public.send_fac_command(cmd, option)
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.FAC_CMD, { cmd, option }) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.FAC_CMD, { cmd, option })
end end
-- send the auto process control configuration with a start command -- send the auto process control configuration with a start command
---@param config coord_auto_config configuration ---@param config coord_auto_config configuration
function public.send_auto_start(config) function public.send_auto_start(config)
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.FAC_CMD, { _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.FAC_CMD, {
FAC_COMMAND.START, config.mode, config.burn_target, config.charge_target, config.gen_target, config.limits FAC_COMMAND.START, config.mode, config.burn_target, config.charge_target, config.gen_target, config.limits
}) })
end end
@ -417,7 +417,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
---@param unit integer unit ID ---@param unit integer unit ID
---@param option any? optional option options for the optional options (like burn rate) ---@param option any? optional option options for the optional options (like burn rate)
function public.send_unit_command(cmd, unit, option) function public.send_unit_command(cmd, unit, option)
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.UNIT_CMD, { cmd, unit, option }) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.UNIT_CMD, { cmd, unit, option })
end end
-- parse a packet -- parse a packet
@ -426,7 +426,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
---@param reply_to integer ---@param reply_to integer
---@param message any ---@param message any
---@param distance integer ---@param distance integer
---@return mgmt_frame|crdn_frame|capi_frame|nil packet ---@return mgmt_frame|crdn_frame|nil packet
function public.parse_packet(side, sender, reply_to, message, distance) function public.parse_packet(side, sender, reply_to, message, distance)
local s_pkt = nic.receive(side, sender, reply_to, message, distance) local s_pkt = nic.receive(side, sender, reply_to, message, distance)
local pkt = nil local pkt = nil
@ -444,12 +444,6 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
if crdn_pkt.decode(s_pkt) then if crdn_pkt.decode(s_pkt) then
pkt = crdn_pkt.get() pkt = crdn_pkt.get()
end end
-- get as coordinator API packet
elseif s_pkt.protocol() == PROTOCOL.COORD_API then
local capi_pkt = comms.capi_packet()
if capi_pkt.decode(s_pkt) then
pkt = capi_pkt.get()
end
else else
log.debug("attempted parse of illegal packet type " .. s_pkt.protocol(), true) log.debug("attempted parse of illegal packet type " .. s_pkt.protocol(), true)
end end
@ -459,7 +453,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
end end
-- handle a packet -- handle a packet
---@param packet mgmt_frame|crdn_frame|capi_frame|nil ---@param packet mgmt_frame|crdn_frame|nil
---@return boolean close_ui ---@return boolean close_ui
function public.handle_packet(packet) function public.handle_packet(packet)
local was_linked = self.sv_linked local was_linked = self.sv_linked
@ -475,18 +469,18 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
elseif r_chan == pkt_channel then elseif r_chan == pkt_channel then
if not self.sv_linked then if not self.sv_linked then
log.debug("discarding pocket API packet before linked to supervisor") log.debug("discarding pocket API packet before linked to supervisor")
elseif protocol == PROTOCOL.COORD_API then elseif protocol == PROTOCOL.SCADA_CRDN then
---@cast packet capi_frame ---@cast packet crdn_frame
-- look for an associated session -- look for an associated session
local session = apisessions.find_session(src_addr) local session = apisessions.find_session(src_addr)
-- API packet -- coordinator packet
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
else else
-- any other packet should be session related, discard it -- any other packet should be session related, discard it
log.debug("discarding COORD_API packet without a known session") log.debug("discarding SCADA_CRDN packet without a known session")
end end
elseif protocol == PROTOCOL.SCADA_MGMT then elseif protocol == PROTOCOL.SCADA_MGMT then
---@cast packet mgmt_frame ---@cast packet mgmt_frame
@ -497,7 +491,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- establish a new session -- establish a new session
-- validate packet and continue -- validate packet and continue
if packet.length == 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then if packet.length == 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
@ -553,7 +547,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
if protocol == PROTOCOL.SCADA_CRDN then if protocol == PROTOCOL.SCADA_CRDN then
---@cast packet crdn_frame ---@cast packet crdn_frame
if self.sv_linked then if self.sv_linked then
if packet.type == SCADA_CRDN_TYPE.INITIAL_BUILDS then if packet.type == CRDN_TYPE.INITIAL_BUILDS then
if packet.length == 2 then if packet.length == 2 then
-- record builds -- record builds
local fac_builds = iocontrol.record_facility_builds(packet.data[1]) local fac_builds = iocontrol.record_facility_builds(packet.data[1])
@ -561,31 +555,31 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
if fac_builds and unit_builds then if fac_builds and unit_builds then
-- acknowledge receipt of builds -- acknowledge receipt of builds
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.INITIAL_BUILDS, {}) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.INITIAL_BUILDS, {})
else else
log.debug("received invalid INITIAL_BUILDS packet") log.debug("received invalid INITIAL_BUILDS packet")
end end
else else
log.debug("INITIAL_BUILDS packet length mismatch") log.debug("INITIAL_BUILDS packet length mismatch")
end end
elseif packet.type == SCADA_CRDN_TYPE.FAC_BUILDS then elseif packet.type == CRDN_TYPE.FAC_BUILDS then
if packet.length == 1 then if packet.length == 1 then
-- record facility builds -- record facility builds
if iocontrol.record_facility_builds(packet.data[1]) then if iocontrol.record_facility_builds(packet.data[1]) then
-- acknowledge receipt of builds -- acknowledge receipt of builds
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.FAC_BUILDS, {}) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.FAC_BUILDS, {})
else else
log.debug("received invalid FAC_BUILDS packet") log.debug("received invalid FAC_BUILDS packet")
end end
else else
log.debug("FAC_BUILDS packet length mismatch") log.debug("FAC_BUILDS packet length mismatch")
end end
elseif packet.type == SCADA_CRDN_TYPE.FAC_STATUS then elseif packet.type == CRDN_TYPE.FAC_STATUS then
-- update facility status -- update facility status
if not iocontrol.update_facility_status(packet.data) then if not iocontrol.update_facility_status(packet.data) then
log.debug("received invalid FAC_STATUS packet") log.debug("received invalid FAC_STATUS packet")
end end
elseif packet.type == SCADA_CRDN_TYPE.FAC_CMD then elseif packet.type == CRDN_TYPE.FAC_CMD then
-- facility command acknowledgement -- facility command acknowledgement
if packet.length >= 2 then if packet.length >= 2 then
local cmd = packet.data[1] local cmd = packet.data[1]
@ -613,24 +607,24 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
else else
log.debug("SCADA_CRDN facility command ack packet length mismatch") log.debug("SCADA_CRDN facility command ack packet length mismatch")
end end
elseif packet.type == SCADA_CRDN_TYPE.UNIT_BUILDS then elseif packet.type == CRDN_TYPE.UNIT_BUILDS then
-- record builds -- record builds
if packet.length == 1 then if packet.length == 1 then
if iocontrol.record_unit_builds(packet.data[1]) then if iocontrol.record_unit_builds(packet.data[1]) then
-- acknowledge receipt of builds -- acknowledge receipt of builds
_send_sv(PROTOCOL.SCADA_CRDN, SCADA_CRDN_TYPE.UNIT_BUILDS, {}) _send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.UNIT_BUILDS, {})
else else
log.debug("received invalid UNIT_BUILDS packet") log.debug("received invalid UNIT_BUILDS packet")
end end
else else
log.debug("UNIT_BUILDS packet length mismatch") log.debug("UNIT_BUILDS packet length mismatch")
end end
elseif packet.type == SCADA_CRDN_TYPE.UNIT_STATUSES then elseif packet.type == CRDN_TYPE.UNIT_STATUSES then
-- update statuses -- update statuses
if not iocontrol.update_unit_statuses(packet.data) then if not iocontrol.update_unit_statuses(packet.data) then
log.debug("received invalid UNIT_STATUSES packet") log.debug("received invalid UNIT_STATUSES packet")
end end
elseif packet.type == SCADA_CRDN_TYPE.UNIT_CMD then elseif packet.type == CRDN_TYPE.UNIT_CMD then
-- unit command acknowledgement -- unit command acknowledgement
if packet.length == 3 then if packet.length == 3 then
local cmd = packet.data[1] local cmd = packet.data[1]
@ -672,7 +666,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
elseif protocol == PROTOCOL.SCADA_MGMT then elseif protocol == PROTOCOL.SCADA_MGMT then
---@cast packet mgmt_frame ---@cast packet mgmt_frame
if self.sv_linked then if self.sv_linked then
if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if packet.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive request received, echo back -- keep alive request received, echo back
if packet.length == 1 then if packet.length == 1 then
local timestamp = packet.data[1] local timestamp = packet.data[1]
@ -690,7 +684,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
else else
log.debug("SCADA keep alive packet length mismatch") log.debug("SCADA keep alive packet length mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then elseif packet.type == MGMT_TYPE.CLOSE then
-- handle session close -- handle session close
sv_watchdog.cancel() sv_watchdog.cancel()
self.sv_addr = comms.BROADCAST self.sv_addr = comms.BROADCAST
@ -701,7 +695,7 @@ function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pk
else else
log.debug("received unknown SCADA_MGMT packet type " .. packet.type) log.debug("received unknown SCADA_MGMT packet type " .. packet.type)
end end
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- connection with supervisor established -- connection with supervisor established
if packet.length == 2 then if packet.length == 2 then
local est_ack = packet.data[1] local est_ack = packet.data[1]

View File

@ -8,8 +8,8 @@ local iocontrol = require("coordinator.iocontrol")
local pocket = {} local pocket = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
-- local CAPI_TYPE = comms.CAPI_TYPE -- local CRDN_TYPE = comms.CRDN_TYPE
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
-- retry time constants in ms -- retry time constants in ms
-- local INITIAL_WAIT = 1500 -- local INITIAL_WAIT = 1500
@ -72,22 +72,22 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
iocontrol.fp_pkt_disconnected(id) iocontrol.fp_pkt_disconnected(id)
end end
-- send a CAPI packet -- send a CRDN packet
-----@param msg_type CAPI_TYPE -----@param msg_type CRDN_TYPE
-----@param msg table -----@param msg table
-- local function _send(msg_type, msg) -- local function _send(msg_type, msg)
-- local s_pkt = comms.scada_packet() -- local s_pkt = comms.scada_packet()
-- local c_pkt = comms.capi_packet() -- local c_pkt = comms.crdn_packet()
-- c_pkt.make(msg_type, msg) -- c_pkt.make(msg_type, msg)
-- s_pkt.make(self.seq_num, PROTOCOL.COORD_API, c_pkt.raw_sendable()) -- s_pkt.make(self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable())
-- out_queue.push_packet(s_pkt) -- out_queue.push_packet(s_pkt)
-- self.seq_num = self.seq_num + 1 -- self.seq_num = self.seq_num + 1
-- end -- end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -101,7 +101,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
end end
-- handle a packet -- handle a packet
---@param pkt mgmt_frame|capi_frame ---@param pkt mgmt_frame|crdn_frame
local function _handle_packet(pkt) local function _handle_packet(pkt)
-- check sequence number -- check sequence number
if self.r_seq_num == nil then if self.r_seq_num == nil then
@ -117,17 +117,17 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
self.conn_watchdog.feed() self.conn_watchdog.feed()
-- process packet -- process packet
if pkt.scada_frame.protocol() == PROTOCOL.COORD_API then if pkt.scada_frame.protocol() == PROTOCOL.SCADA_CRDN then
---@cast pkt capi_frame ---@cast pkt crdn_frame
-- handle packet by type -- handle packet by type
if pkt.type == nil then if pkt.type == nil then
else else
log.debug(log_header .. "handler received unsupported CAPI packet type " .. pkt.type) log.debug(log_header .. "handler received unsupported CRDN packet type " .. pkt.type)
end end
elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
if pkt.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if pkt.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive reply -- keep alive reply
if pkt.length == 2 then if pkt.length == 2 then
local srv_start = pkt.data[1] local srv_start = pkt.data[1]
@ -146,7 +146,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_header .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == SCADA_MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
_close() _close()
else else
@ -174,7 +174,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
-- close the connection -- close the connection
function public.close() function public.close()
_close() _close()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
log.info(log_header .. "session closed by server") log.info(log_header .. "session closed by server")
end end
@ -229,7 +229,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
periodics.keep_alive = periodics.keep_alive + elapsed periodics.keep_alive = periodics.keep_alive + elapsed
if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { util.time() })
periodics.keep_alive = 0 periodics.keep_alive = 0
end end

View File

@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder")
local apisessions = require("coordinator.session.apisessions") local apisessions = require("coordinator.session.apisessions")
local COORDINATOR_VERSION = "v1.0.9" local COORDINATOR_VERSION = "v1.0.12"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts

View File

@ -12,20 +12,19 @@ local VerticalBar = require("graphics.elements.indicators.vbar")
local cpair = core.cpair local cpair = core.cpair
local border = core.border local border = core.border
local text_fg_bg = style.text_colors
-- new boiler view -- new boiler view
---@param root graphics_element parent ---@param root graphics_element parent
---@param x integer top left x ---@param x integer top left x
---@param y integer top left y ---@param y integer top left y
---@param ps psil ps interface ---@param ps psil ps interface
local function new_view(root, x, y, ps) local function new_view(root, x, y, ps)
local boiler = Rectangle{parent=root,border=border(1, colors.gray, true),width=31,height=7,x=x,y=y} local boiler = Rectangle{parent=root,border=border(1,colors.gray,true),width=31,height=7,x=x,y=y}
local text_fg_bg = cpair(colors.black, colors.lightGray)
local lu_col = cpair(colors.gray, colors.gray)
local status = StateIndicator{parent=boiler,x=9,y=1,states=style.boiler.states,value=1,min_width=12} local status = StateIndicator{parent=boiler,x=9,y=1,states=style.boiler.states,value=1,min_width=12}
local temp = DataIndicator{parent=boiler,x=5,y=3,lu_colors=lu_col,label="Temp:",unit="K",format="%10.2f",value=0,width=22,fg_bg=text_fg_bg} local temp = DataIndicator{parent=boiler,x=5,y=3,lu_colors=style.lu_col,label="Temp:",unit="K",format="%10.2f",value=0,width=22,fg_bg=text_fg_bg}
local boil_r = DataIndicator{parent=boiler,x=5,y=4,lu_colors=lu_col,label="Boil:",unit="mB/t",format="%10.0f",value=0,commas=true,width=22,fg_bg=text_fg_bg} local boil_r = DataIndicator{parent=boiler,x=5,y=4,lu_colors=style.lu_col,label="Boil:",unit="mB/t",format="%10.0f",value=0,commas=true,width=22,fg_bg=text_fg_bg}
status.register(ps, "computed_status", status.update) status.register(ps, "computed_status", status.update)
temp.register(ps, "temperature", temp.update) temp.register(ps, "temperature", temp.update)

View File

@ -18,6 +18,9 @@ local border = core.border
local TEXT_ALIGN = core.TEXT_ALIGN local TEXT_ALIGN = core.TEXT_ALIGN
local text_fg_bg = style.text_colors
local lu_col = style.lu_colors
-- new induction matrix view -- new induction matrix view
---@param root graphics_element parent ---@param root graphics_element parent
---@param x integer top left x ---@param x integer top left x
@ -31,14 +34,12 @@ local function new_view(root, x, y, data, ps, id)
local matrix = Div{parent=root,fg_bg=style.root,width=33,height=24,x=x,y=y} local matrix = Div{parent=root,fg_bg=style.root,width=33,height=24,x=x,y=y}
TextBox{parent=matrix,text=" ",width=33,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=matrix,text=" ",width=33,height=1,x=1,y=1,fg_bg=style.lg_gray}
TextBox{parent=matrix,text=title,alignment=TEXT_ALIGN.CENTER,width=33,height=1,x=1,y=2,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=matrix,text=title,alignment=TEXT_ALIGN.CENTER,width=33,height=1,x=1,y=2,fg_bg=style.lg_gray}
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3} local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
local text_fg_bg = cpair(colors.black, colors.lightGray)
local label_fg_bg = cpair(colors.gray, colors.lightGray) local label_fg_bg = cpair(colors.gray, colors.lightGray)
local lu_col = cpair(colors.gray, colors.gray)
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14} local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
local energy = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg_bg} local energy = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg_bg}

View File

@ -4,6 +4,8 @@
local iocontrol = require("coordinator.iocontrol") local iocontrol = require("coordinator.iocontrol")
local style = require("coordinator.ui.style")
local core = require("graphics.core") local core = require("graphics.core")
local Div = require("graphics.elements.div") local Div = require("graphics.elements.div")
@ -15,6 +17,9 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local text_fg_bg = style.text_colors
local lg_wh = style.lg_white
-- create a pocket list entry -- create a pocket list entry
---@param parent graphics_element parent ---@param parent graphics_element parent
---@param id integer PKT session ID ---@param id integer PKT session ID
@ -23,22 +28,22 @@ local function init(parent, id)
-- root div -- root div
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true} local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=cpair(colors.black,colors.white)} local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
local ps_prefix = "pkt_" .. id .. "_" local ps_prefix = "pkt_" .. id .. "_"
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=text_fg_bg}
local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=text_fg_bg,nav_active=cpair(colors.gray,colors.black)}
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=text_fg_bg}
pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value) pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value)
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1} TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=cpair(colors.lightGray,colors.white)} local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=lg_wh}
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value) pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1} TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=cpair(colors.lightGray,colors.white)} local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_wh}
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=cpair(colors.lightGray,colors.white)} TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_wh}
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update) pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor) pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)

View File

@ -33,6 +33,8 @@ local lu_cpair = style.lu_colors
local hzd_fg_bg = style.hzd_fg_bg local hzd_fg_bg = style.hzd_fg_bg
local dis_colors = style.dis_colors local dis_colors = style.dis_colors
local gry_wht = style.gray_white
local ind_grn = style.ind_grn local ind_grn = style.ind_grn
local ind_yel = style.ind_yel local ind_yel = style.ind_yel
local ind_red = style.ind_red local ind_red = style.ind_red
@ -47,6 +49,10 @@ local period = core.flasher.PERIOD
local function new_view(root, x, y) local function new_view(root, x, y)
assert(root.get_height() >= (y + 24), "main display not of sufficient vertical resolution (add an additional row of monitors)") assert(root.get_height() >= (y + 24), "main display not of sufficient vertical resolution (add an additional row of monitors)")
local black = cpair(colors.black, colors.black)
local blk_brn = cpair(colors.black, colors.brown)
local blk_pur = cpair(colors.black, colors.purple)
local facility = iocontrol.get_db().facility local facility = iocontrol.get_db().facility
local units = iocontrol.get_db().units local units = iocontrol.get_db().units
@ -116,35 +122,35 @@ local function new_view(root, x, y)
local targets = Div{parent=proc,width=31,height=24,x=1,y=1} local targets = Div{parent=proc,width=31,height=24,x=1,y=1}
local burn_tag = Div{parent=targets,x=1,y=1,width=8,height=4,fg_bg=cpair(colors.black,colors.purple)} local burn_tag = Div{parent=targets,x=1,y=1,width=8,height=4,fg_bg=blk_pur}
TextBox{parent=burn_tag,x=2,y=2,text="Burn Target",width=7,height=2} TextBox{parent=burn_tag,x=2,y=2,text="Burn Target",width=7,height=2}
local burn_target = Div{parent=targets,x=9,y=1,width=23,height=3,fg_bg=cpair(colors.gray,colors.white)} local burn_target = Div{parent=targets,x=9,y=1,width=23,height=3,fg_bg=gry_wht}
local b_target = SpinboxNumeric{parent=burn_target,x=11,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=bw_fg_bg} local b_target = SpinboxNumeric{parent=burn_target,x=11,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
TextBox{parent=burn_target,x=18,y=2,text="mB/t"} TextBox{parent=burn_target,x=18,y=2,text="mB/t"}
local burn_sum = DataIndicator{parent=targets,x=9,y=4,label="",format="%18.1f",value=0,unit="mB/t",commas=true,lu_colors=cpair(colors.black,colors.black),width=23,fg_bg=cpair(colors.black,colors.brown)} local burn_sum = DataIndicator{parent=targets,x=9,y=4,label="",format="%18.1f",value=0,unit="mB/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
b_target.register(facility.ps, "process_burn_target", b_target.set_value) b_target.register(facility.ps, "process_burn_target", b_target.set_value)
burn_sum.register(facility.ps, "burn_sum", burn_sum.update) burn_sum.register(facility.ps, "burn_sum", burn_sum.update)
local chg_tag = Div{parent=targets,x=1,y=6,width=8,height=4,fg_bg=cpair(colors.black,colors.purple)} local chg_tag = Div{parent=targets,x=1,y=6,width=8,height=4,fg_bg=blk_pur}
TextBox{parent=chg_tag,x=2,y=2,text="Charge Target",width=7,height=2} TextBox{parent=chg_tag,x=2,y=2,text="Charge Target",width=7,height=2}
local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=cpair(colors.gray,colors.white)} local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=gry_wht}
local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=bw_fg_bg} local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
TextBox{parent=chg_target,x=18,y=2,text="MFE"} TextBox{parent=chg_target,x=18,y=2,text="MFE"}
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=cpair(colors.black,colors.black),width=23,fg_bg=cpair(colors.black,colors.brown)} local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
c_target.register(facility.ps, "process_charge_target", c_target.set_value) c_target.register(facility.ps, "process_charge_target", c_target.set_value)
cur_charge.register(facility.induction_ps_tbl[1], "energy", function (j) cur_charge.update(util.joules_to_fe(j) / 1000000) end) cur_charge.register(facility.induction_ps_tbl[1], "energy", function (j) cur_charge.update(util.joules_to_fe(j) / 1000000) end)
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=cpair(colors.black,colors.purple)} local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2} TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=cpair(colors.gray,colors.white)} local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=gry_wht}
local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=bw_fg_bg} local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
TextBox{parent=gen_target,x=18,y=2,text="kFE/t"} TextBox{parent=gen_target,x=18,y=2,text="kFE/t"}
local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=cpair(colors.black,colors.black),width=23,fg_bg=cpair(colors.black,colors.brown)} local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
g_target.register(facility.ps, "process_gen_target", g_target.set_value) g_target.register(facility.ps, "process_gen_target", g_target.set_value)
cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(util.joules_to_fe(j) / 1000)) end) cur_gen.register(facility.induction_ps_tbl[1], "last_input", function (j) cur_gen.update(util.round(util.joules_to_fe(j) / 1000)) end)
@ -159,10 +165,10 @@ local function new_view(root, x, y)
for i = 1, 4 do for i = 1, 4 do
local unit local unit
local tag_fg_bg = cpair(colors.gray,colors.white) local tag_fg_bg = gry_wht
local lim_fg_bg = cpair(colors.lightGray,colors.white) local lim_fg_bg = style.lg_white
local ctl_fg = colors.lightGray local ctl_fg = colors.lightGray
local cur_fg_bg = cpair(colors.lightGray,colors.white) local cur_fg_bg = style.lg_white
local cur_lu = colors.lightGray local cur_lu = colors.lightGray
if i <= facility.num_units then if i <= facility.num_units then
@ -170,7 +176,7 @@ local function new_view(root, x, y)
tag_fg_bg = cpair(colors.black,colors.lightBlue) tag_fg_bg = cpair(colors.black,colors.lightBlue)
lim_fg_bg = bw_fg_bg lim_fg_bg = bw_fg_bg
ctl_fg = colors.gray ctl_fg = colors.gray
cur_fg_bg = cpair(colors.black,colors.brown) cur_fg_bg = blk_brn
cur_lu = colors.black cur_lu = colors.black
end end
@ -180,7 +186,7 @@ local function new_view(root, x, y)
TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Limit",width=7,height=2} TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Limit",width=7,height=2}
local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=cpair(ctl_fg,colors.white)} local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=cpair(ctl_fg,colors.white)}
local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=lim_fg_bg} local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=lim_fg_bg}
TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,height=1} TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,height=1}
local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg} local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg}
@ -203,8 +209,8 @@ local function new_view(root, x, y)
local stat_div = Div{parent=proc,width=22,height=24,x=57,y=6} local stat_div = Div{parent=proc,width=22,height=24,x=57,y=6}
for i = 1, 4 do for i = 1, 4 do
local tag_fg_bg = cpair(colors.gray, colors.white) local tag_fg_bg = gry_wht
local ind_fg_bg = cpair(colors.lightGray, colors.white) local ind_fg_bg = style.lg_white
local ind_off = colors.lightGray local ind_off = colors.lightGray
if i <= facility.num_units then if i <= facility.num_units then
@ -241,12 +247,12 @@ local function new_view(root, x, y)
local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg} local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg}
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=bw_fg_bg} local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,height=1,alignment=TEXT_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=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.gray, colors.white)} local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=31,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=gry_wht}
stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value) stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value)
stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value) stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
local auto_controls = Div{parent=proc,x=1,y=20,width=31,height=5,fg_bg=cpair(colors.gray,colors.white)} local auto_controls = Div{parent=proc,x=1,y=20,width=31,height=5,fg_bg=gry_wht}
-- save the automatic process control configuration without starting -- save the automatic process control configuration without starting
local function _save_cfg() local function _save_cfg()
@ -321,7 +327,7 @@ local function new_view(root, x, y)
local waste_sel = Div{parent=proc,width=21,height=24,x=81,y=1} local waste_sel = Div{parent=proc,width=21,height=24,x=81,y=1}
TextBox{parent=waste_sel,text=" ",width=21,height=1,x=1,y=1,fg_bg=cpair(colors.black,colors.brown)} TextBox{parent=waste_sel,text=" ",width=21,height=1,x=1,y=1,fg_bg=blk_brn}
TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=TEXT_ALIGN.CENTER,width=21,height=1,x=1,y=2,fg_bg=cpair(colors.lightGray,colors.brown)} TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=TEXT_ALIGN.CENTER,width=21,height=1,x=1,y=2,fg_bg=cpair(colors.lightGray,colors.brown)}
local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3} local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3}

View File

@ -14,6 +14,9 @@ local StateIndicator = require("graphics.elements.indicators.state")
local cpair = core.cpair local cpair = core.cpair
local border = core.border local border = core.border
local text_fg_bg = style.text_colors
local lu_col = style.lu_colors
-- create new reactor view -- create new reactor view
---@param root graphics_element parent ---@param root graphics_element parent
---@param x integer top left x ---@param x integer top left x
@ -22,9 +25,6 @@ local border = core.border
local function new_view(root, x, y, ps) local function new_view(root, x, y, ps)
local reactor = Rectangle{parent=root,border=border(1, colors.gray, true),width=30,height=7,x=x,y=y} local reactor = Rectangle{parent=root,border=border(1, colors.gray, true),width=30,height=7,x=x,y=y}
local text_fg_bg = cpair(colors.black, colors.lightGray)
local lu_col = cpair(colors.gray, colors.gray)
local status = StateIndicator{parent=reactor,x=6,y=1,states=style.reactor.states,value=1,min_width=16} local status = StateIndicator{parent=reactor,x=6,y=1,states=style.reactor.states,value=1,min_width=16}
local core_temp = DataIndicator{parent=reactor,x=2,y=3,lu_colors=lu_col,label="Core Temp:",unit="K",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg} local core_temp = DataIndicator{parent=reactor,x=2,y=3,lu_colors=lu_col,label="Core Temp:",unit="K",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg}
local burn_r = DataIndicator{parent=reactor,x=2,y=4,lu_colors=lu_col,label="Burn Rate:",unit="mB/t",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg} local burn_r = DataIndicator{parent=reactor,x=2,y=4,lu_colors=lu_col,label="Burn Rate:",unit="mB/t",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg}

View File

@ -15,16 +15,16 @@ local VerticalBar = require("graphics.elements.indicators.vbar")
local cpair = core.cpair local cpair = core.cpair
local border = core.border local border = core.border
local text_fg_bg = style.text_colors
local lu_col = style.lu_colors
-- new turbine view -- new turbine view
---@param root graphics_element parent ---@param root graphics_element parent
---@param x integer top left x ---@param x integer top left x
---@param y integer top left y ---@param y integer top left y
---@param ps psil ps interface ---@param ps psil ps interface
local function new_view(root, x, y, ps) local function new_view(root, x, y, ps)
local turbine = Rectangle{parent=root,border=border(1, colors.gray, true),width=23,height=7,x=x,y=y} local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y}
local text_fg_bg = cpair(colors.black, colors.lightGray)
local lu_col = cpair(colors.gray, colors.gray)
local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12} local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12}
local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg_bg} local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg_bg}

View File

@ -2,6 +2,8 @@
-- Reactor Unit SCADA Coordinator GUI -- Reactor Unit SCADA Coordinator GUI
-- --
local types = require("scada-common.types")
local iocontrol = require("coordinator.iocontrol") local iocontrol = require("coordinator.iocontrol")
local style = require("coordinator.ui.style") local style = require("coordinator.ui.style")
@ -34,6 +36,9 @@ local border = core.border
local bw_fg_bg = style.bw_fg_bg local bw_fg_bg = style.bw_fg_bg
local lu_cpair = style.lu_colors local lu_cpair = style.lu_colors
local hzd_fg_bg = style.hzd_fg_bg local hzd_fg_bg = style.hzd_fg_bg
local dis_colors = style.dis_colors
local gry_wht = style.gray_white
local ind_grn = style.ind_grn local ind_grn = style.ind_grn
local ind_yel = style.ind_yel local ind_yel = style.ind_yel
@ -93,7 +98,7 @@ local function init(parent, id)
waste.register(u_ps, "waste_fill", waste.update) waste.register(u_ps, "waste_fill", waste.update)
ccool.register(u_ps, "ccool_type", function (type) ccool.register(u_ps, "ccool_type", function (type)
if type == "mekanism:sodium" then if type == types.FLUID.SODIUM then
ccool.recolor(cpair(colors.lightBlue, colors.gray)) ccool.recolor(cpair(colors.lightBlue, colors.gray))
else else
ccool.recolor(cpair(colors.blue, colors.gray)) ccool.recolor(cpair(colors.blue, colors.gray))
@ -101,7 +106,7 @@ local function init(parent, id)
end) end)
hcool.register(u_ps, "hcool_type", function (type) hcool.register(u_ps, "hcool_type", function (type)
if type == "mekanism:superheated_sodium" then if type == types.FLUID.SUPERHEATED_SODIUM then
hcool.recolor(cpair(colors.orange, colors.gray)) hcool.recolor(cpair(colors.orange, colors.gray))
else else
hcool.recolor(cpair(colors.white, colors.gray)) hcool.recolor(cpair(colors.white, colors.gray))
@ -130,7 +135,7 @@ local function init(parent, id)
local u_stat = Rectangle{parent=main,border=border(1,colors.gray,true),thin=true,width=33,height=4,x=46,y=3,fg_bg=bw_fg_bg} local u_stat = Rectangle{parent=main,border=border(1,colors.gray,true),thin=true,width=33,height=4,x=46,y=3,fg_bg=bw_fg_bg}
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=33,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=bw_fg_bg} local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=33,height=1,alignment=TEXT_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=TEXT_ALIGN.CENTER,fg_bg=cpair(colors.gray, colors.white)} local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=33,height=1,alignment=TEXT_ALIGN.CENTER,fg_bg=gry_wht}
stat_line_1.register(u_ps, "U_StatusLine1", stat_line_1.set_value) stat_line_1.register(u_ps, "U_StatusLine1", stat_line_1.set_value)
stat_line_2.register(u_ps, "U_StatusLine2", stat_line_2.set_value) stat_line_2.register(u_ps, "U_StatusLine2", stat_line_2.set_value)
@ -341,10 +346,8 @@ local function init(parent, id)
-- reactor controls -- -- reactor controls --
---------------------- ----------------------
local dis_colors = cpair(colors.white, colors.lightGray) local burn_control = Div{parent=main,x=12,y=28,width=19,height=3,fg_bg=gry_wht}
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
local burn_control = Div{parent=main,x=12,y=28,width=19,height=3,fg_bg=cpair(colors.gray,colors.white)}
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=cpair(colors.gray,colors.white),fg_bg=bw_fg_bg}
TextBox{parent=burn_control,x=9,y=2,text="mB/t"} TextBox{parent=burn_control,x=9,y=2,text="mB/t"}
local set_burn = function () unit.set_burn(burn_rate.get_value()) end local set_burn = function () unit.set_burn(burn_rate.get_value()) end
@ -480,7 +483,7 @@ local function init(parent, id)
auto_div.line_break() auto_div.line_break()
local function set_group() unit.set_group(group.get_value() - 1) end local function set_group() unit.set_group(group.get_value() - 1) end
local set_grp_btn = PushButton{parent=auto_div,text="SET",x=4,min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=cpair(colors.gray,colors.white),callback=set_group} local set_grp_btn = PushButton{parent=auto_div,text="SET",x=4,min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=gry_wht,callback=set_group}
auto_div.line_break() auto_div.line_break()

View File

@ -23,7 +23,6 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local sprintf = util.sprintf local sprintf = util.sprintf
local cpair = core.cpair
local border = core.border local border = core.border
local pipe = core.pipe local pipe = core.pipe
@ -31,6 +30,7 @@ local wh_gray = style.wh_gray
local bw_fg_bg = style.bw_fg_bg local bw_fg_bg = style.bw_fg_bg
local text_c = style.text_colors local text_c = style.text_colors
local lu_c = style.lu_colors local lu_c = style.lu_colors
local lg_gray = style.lg_gray
local ind_grn = style.ind_grn local ind_grn = style.ind_grn
local ind_wht = style.ind_wht local ind_wht = style.ind_wht
@ -64,8 +64,6 @@ local function make(parent, x, y, wide, unit)
-- bounding box div -- bounding box div
local root = Div{parent=parent,x=x,y=y,width=_wide(136, 114),height=height} local root = Div{parent=parent,x=x,y=y,width=_wide(136, 114),height=height}
local lg_gray = cpair(colors.lightGray, colors.gray)
------------------ ------------------
-- COOLING LOOP -- -- COOLING LOOP --
------------------ ------------------

View File

@ -261,7 +261,7 @@ local function init(main)
if tank_defs[i] > 0 then if tank_defs[i] > 0 then
local vy = 3 + y_ofs(i) local vy = 3 + y_ofs(i)
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1} TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=text_col,width=2,height=1}
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn} local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn}
local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht} local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht}
@ -288,7 +288,7 @@ local function init(main)
local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14} local tank = Div{parent=main,x=3,y=7+y_offset,width=20,height=14}
TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=style.lg_gray}
TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.wh_gray} TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.wh_gray}
local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12} local tank_box = Rectangle{parent=tank,border=border(1,colors.gray,true),width=20,height=12}
@ -338,7 +338,7 @@ local function init(main)
local sps = Div{parent=main,x=140,y=3,height=12} local sps = Div{parent=main,x=140,y=3,height=12}
TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=style.lg_gray}
TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=wh_gray} TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=wh_gray}
local sps_box = Rectangle{parent=sps,border=border(1,colors.gray,true),width=24,height=10} local sps_box = Rectangle{parent=sps,border=border(1,colors.gray,true),width=24,height=10}
@ -361,8 +361,14 @@ local function init(main)
-- statistics -- -- statistics --
---------------- ----------------
TextBox{parent=main,x=145,y=16,text="PROC. WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} TextBox{parent=main,x=145,y=16,text="RAW WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
local pr_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=bw_fg_bg} local raw_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg}
local sum_raw_waste = DataIndicator{parent=raw_waste,lu_colors=lu_col,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=TEXT_ALIGN.CENTER,width=19,height=1,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=bw_fg_bg}
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17} local pu = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
local po = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Po",unit="mB/t",format="%9.3f",value=0,width=17} local po = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Po",unit="mB/t",format="%9.3f",value=0,width=17}
local popl = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17} local popl = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17}
@ -371,8 +377,8 @@ local function init(main)
po.register(facility.ps, "po_rate", po.update) po.register(facility.ps, "po_rate", po.update)
popl.register(facility.ps, "po_pl_rate", popl.update) popl.register(facility.ps, "po_pl_rate", popl.update)
TextBox{parent=main,x=145,y=23,text="SPENT WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} TextBox{parent=main,x=145,y=28,text="SPENT WASTE",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
local sp_waste = Rectangle{parent=main,x=145,y=24,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg} local sp_waste = Rectangle{parent=main,x=145,y=29,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg}
local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17} local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17}
sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update) sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update)

View File

@ -2,32 +2,34 @@
-- Coordinator Front Panel GUI -- Coordinator Front Panel GUI
-- --
local types = require("scada-common.types") local types = require("scada-common.types")
local util = require("scada-common.util") local util = require("scada-common.util")
local iocontrol = require("coordinator.iocontrol") local iocontrol = require("coordinator.iocontrol")
local pgi = require("coordinator.ui.pgi") local pgi = require("coordinator.ui.pgi")
local style = require("coordinator.ui.style") local style = require("coordinator.ui.style")
local pkt_entry = require("coordinator.ui.components.pkt_entry") local pkt_entry = require("coordinator.ui.components.pkt_entry")
local core = require("graphics.core") local core = require("graphics.core")
local Div = require("graphics.elements.div") local Div = require("graphics.elements.div")
local ListBox = require("graphics.elements.listbox") local ListBox = require("graphics.elements.listbox")
local MultiPane = require("graphics.elements.multipane") local MultiPane = require("graphics.elements.multipane")
local TextBox = require("graphics.elements.textbox") local TextBox = require("graphics.elements.textbox")
local TabBar = require("graphics.elements.controls.tabbar") local TabBar = require("graphics.elements.controls.tabbar")
local LED = require("graphics.elements.indicators.led") local LED = require("graphics.elements.indicators.led")
local RGBLED = require("graphics.elements.indicators.ledrgb") local RGBLED = require("graphics.elements.indicators.ledrgb")
local TEXT_ALIGN = core.TEXT_ALIGN local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local led_grn = style.led_grn
-- create new front panel view -- create new front panel view
---@param panel graphics_element main displaybox ---@param panel graphics_element main displaybox
---@param num_units integer number of units (number of unit monitors) ---@param num_units integer number of units (number of unit monitors)
@ -47,13 +49,13 @@ local function init(panel, num_units)
local system = Div{parent=main_page,width=14,height=17,x=2,y=2} local system = Div{parent=main_page,width=14,height=17,x=2,y=2}
local status = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)} local status = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=cpair(colors.green,colors.green_off)} local heartbeat = LED{parent=system,label="HEARTBEAT",colors=led_grn}
status.update(true) status.update(true)
system.line_break() system.line_break()
heartbeat.register(ps, "heartbeat", heartbeat.update) heartbeat.register(ps, "heartbeat", heartbeat.update)
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local modem = LED{parent=system,label="MODEM",colors=led_grn}
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
network.update(types.PANEL_LINK_STATE.DISCONNECTED) network.update(types.PANEL_LINK_STATE.DISCONNECTED)
system.line_break() system.line_break()
@ -61,25 +63,25 @@ local function init(panel, num_units)
modem.register(ps, "has_modem", modem.update) modem.register(ps, "has_modem", modem.update)
network.register(ps, "link_state", network.update) network.register(ps, "link_state", network.update)
local speaker = LED{parent=system,label="SPEAKER",colors=cpair(colors.green,colors.green_off)} local speaker = LED{parent=system,label="SPEAKER",colors=led_grn}
speaker.register(ps, "has_speaker", speaker.update) speaker.register(ps, "has_speaker", speaker.update)
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
local comp_id = util.sprintf("(%d)", os.getComputerID()) local comp_id = util.sprintf("(%d)", os.getComputerID())
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp_label}
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2} local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
local main_monitor = LED{parent=monitors,label="MAIN MONITOR",colors=cpair(colors.green,colors.green_off)} local main_monitor = LED{parent=monitors,label="MAIN MONITOR",colors=led_grn}
main_monitor.register(ps, "main_monitor", main_monitor.update) main_monitor.register(ps, "main_monitor", main_monitor.update)
local flow_monitor = LED{parent=monitors,label="FLOW MONITOR",colors=cpair(colors.green,colors.green_off)} local flow_monitor = LED{parent=monitors,label="FLOW MONITOR",colors=led_grn}
flow_monitor.register(ps, "flow_monitor", flow_monitor.update) flow_monitor.register(ps, "flow_monitor", flow_monitor.update)
monitors.line_break() monitors.line_break()
for i = 1, num_units do for i = 1, num_units do
local unit_monitor = LED{parent=monitors,label="UNIT "..i.." MONITOR",colors=cpair(colors.green,colors.green_off)} local unit_monitor = LED{parent=monitors,label="UNIT "..i.." MONITOR",colors=led_grn}
unit_monitor.register(ps, "unit_monitor_" .. i, unit_monitor.update) unit_monitor.register(ps, "unit_monitor_" .. i, unit_monitor.update)
end end
@ -87,7 +89,7 @@ local function init(panel, num_units)
-- about footer -- about footer
-- --
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=cpair(colors.lightGray,colors.ivory)} local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp_label}
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
@ -101,7 +103,7 @@ local function init(panel, num_units)
-- API page -- API page
local api_page = Div{parent=page_div,x=1,y=1,hidden=true} local api_page = Div{parent=page_div,x=1,y=1,hidden=true}
local api_list = ListBox{parent=api_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=cpair(colors.black,colors.ivory),nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)} local api_list = ListBox{parent=api_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp_text,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
local _ = Div{parent=api_list,height=1,hidden=true} -- padding local _ = Div{parent=api_list,height=1,hidden=true} -- padding
-- assemble page panes -- assemble page panes
@ -111,11 +113,11 @@ local function init(panel, num_units)
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes} local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
local tabs = { local tabs = {
{ name = "CRD", color = cpair(colors.black, colors.ivory) }, { name = "CRD", color = style.fp_text },
{ name = "API", color = cpair(colors.black, colors.ivory) }, { name = "API", color = style.fp_text },
} }
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=cpair(colors.black,colors.white)} TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=style.bw_fg_bg}
-- link pocket API list management to PGI -- link pocket API list management to PGI
pgi.link_elements(api_list, pkt_entry) pgi.link_elements(api_list, pkt_entry)

View File

@ -2,8 +2,6 @@
-- Main SCADA Coordinator GUI -- Main SCADA Coordinator GUI
-- --
local util = require("scada-common.util")
local iocontrol = require("coordinator.iocontrol") local iocontrol = require("coordinator.iocontrol")
local style = require("coordinator.ui.style") local style = require("coordinator.ui.style")
@ -20,8 +18,6 @@ local DataIndicator = require("graphics.elements.indicators.data")
local TEXT_ALIGN = core.TEXT_ALIGN local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair
-- create new main view -- create new main view
---@param main graphics_element main displaybox ---@param main graphics_element main displaybox
local function init(main) local function init(main)
@ -30,7 +26,7 @@ local function init(main)
-- window header message -- window header message
local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=cpair(colors.lightGray, colors.white),width=12,fg_bg=style.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=style.header}
-- max length example: "01:23:45 AM - Wednesday, September 28 2022" -- max length example: "01:23:45 AM - Wednesday, September 28 2022"
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=TEXT_ALIGN.RIGHT,width=42,height=1,fg_bg=style.header} local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=TEXT_ALIGN.RIGHT,width=42,height=1,fg_bg=style.header}
@ -77,7 +73,7 @@ local function init(main)
assert(cnc_bottom_align_start >= cnc_y_start, "main display not of sufficient vertical resolution (add an additional row of monitors)") assert(cnc_bottom_align_start >= cnc_y_start, "main display not of sufficient vertical resolution (add an additional row of monitors)")
TextBox{parent=main,y=cnc_bottom_align_start,text=util.strrep("\x8c", header.get_width()),alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=main,y=cnc_bottom_align_start,text=string.rep("\x8c", header.get_width()),alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.lg_gray}
cnc_bottom_align_start = cnc_bottom_align_start + 2 cnc_bottom_align_start = cnc_bottom_align_start + 2

View File

@ -78,11 +78,19 @@ style.lu_colors = cpair(colors.gray, colors.gray)
style.hzd_fg_bg = style.wh_gray style.hzd_fg_bg = style.wh_gray
style.dis_colors = cpair(colors.white, colors.lightGray) style.dis_colors = cpair(colors.white, colors.lightGray)
style.lg_gray = cpair(colors.lightGray, colors.gray)
style.lg_white = cpair(colors.lightGray, colors.white)
style.gray_white = cpair(colors.gray, colors.white)
style.ind_grn = cpair(colors.green, colors.gray) style.ind_grn = cpair(colors.green, colors.gray)
style.ind_yel = cpair(colors.yellow, colors.gray) style.ind_yel = cpair(colors.yellow, colors.gray)
style.ind_red = cpair(colors.red, colors.gray) style.ind_red = cpair(colors.red, colors.gray)
style.ind_wht = style.wh_gray style.ind_wht = style.wh_gray
style.fp_text = cpair(colors.black, colors.ivory)
style.fp_label = cpair(colors.lightGray, colors.ivory)
style.led_grn = cpair(colors.green, colors.green_off)
-- UI COMPONENTS -- -- UI COMPONENTS --
style.reactor = { style.reactor = {

View File

@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {} local core = {}
core.version = "1.1.1" core.version = "1.1.2"
core.flasher = flasher core.flasher = flasher
core.events = events core.events = events
@ -35,11 +35,7 @@ core.TEXT_ALIGN = {
---@param even? boolean whether to pad width extra to account for rectangular pixels, defaults to false ---@param even? boolean whether to pad width extra to account for rectangular pixels, defaults to false
---@return graphics_border ---@return graphics_border
function core.border(width, color, even) function core.border(width, color, even)
return { return { width = width, color = color, even = even or false }
width = width,
color = color,
even = even or false -- convert nil to false
}
end end
---@class graphics_frame ---@class graphics_frame
@ -56,12 +52,7 @@ end
---@param h integer ---@param h integer
---@return graphics_frame ---@return graphics_frame
function core.gframe(x, y, w, h) function core.gframe(x, y, w, h)
return { return { x = x, y = y, w = w, h = h }
x = x,
y = y,
w = w,
h = h
}
end end
---@class cpair ---@class cpair

View File

@ -166,6 +166,31 @@ function element.new(args, child_offset_x, child_offset_y)
self.bounds.x2 = self.position.x + f.w - 1 self.bounds.x2 = self.position.x + f.w - 1
self.bounds.y1 = self.position.y self.bounds.y1 = self.position.y
self.bounds.y2 = self.position.y + f.h - 1 self.bounds.y2 = self.position.y + f.h - 1
-- alias functions
-- window set cursor position
---@param x integer
---@param y integer
function protected.w_set_cur(x, y) protected.window.setCursorPos(x, y) end
-- set background color
---@param c color
function protected.w_set_bkg(c) protected.window.setBackgroundColor(c) end
-- set foreground (text) color
---@param c color
function protected.w_set_fgd(c) protected.window.setTextColor(c) end
-- write text
---@param str string
function protected.w_write(str) protected.window.write(str) end
-- blit text
---@param str string
---@param fg string
---@param bg string
function protected.w_blit(str, fg, bg) protected.window.blit(str, fg, bg) end
end end
-- check if a coordinate relative to the parent is within the bounds of this element -- check if a coordinate relative to the parent is within the bounds of this element

View File

@ -36,49 +36,49 @@ local function waiting(args)
if state >= 0 and state < 7 then if state >= 0 and state < 7 then
-- top -- top
e.window.setCursorPos(1 + math.floor(state / 2), 1) e.w_set_cur(1 + math.floor(state / 2), 1)
if state % 2 == 0 then if state % 2 == 0 then
e.window.blit("\x8f", blit_fg, blit_bg) e.w_blit("\x8f", blit_fg, blit_bg)
else else
e.window.blit("\x8a\x85", blit_fg_2x, blit_bg_2x) e.w_blit("\x8a\x85", blit_fg_2x, blit_bg_2x)
end end
-- bottom -- bottom
e.window.setCursorPos(4 - math.ceil(state / 2), 3) e.w_set_cur(4 - math.ceil(state / 2), 3)
if state % 2 == 0 then if state % 2 == 0 then
e.window.blit("\x8f", blit_fg, blit_bg) e.w_blit("\x8f", blit_fg, blit_bg)
else else
e.window.blit("\x8a\x85", blit_fg_2x, blit_bg_2x) e.w_blit("\x8a\x85", blit_fg_2x, blit_bg_2x)
end end
else else
local st = state - 7 local st = state - 7
-- right -- right
if st % 3 == 0 then if st % 3 == 0 then
e.window.setCursorPos(4, 1 + math.floor(st / 3)) e.w_set_cur(4, 1 + math.floor(st / 3))
e.window.blit("\x83", blit_bg, blit_fg) e.w_blit("\x83", blit_bg, blit_fg)
elseif st % 3 == 1 then elseif st % 3 == 1 then
e.window.setCursorPos(4, 1 + math.floor(st / 3)) e.w_set_cur(4, 1 + math.floor(st / 3))
e.window.blit("\x8f", blit_bg, blit_fg) e.w_blit("\x8f", blit_bg, blit_fg)
e.window.setCursorPos(4, 2 + math.floor(st / 3)) e.w_set_cur(4, 2 + math.floor(st / 3))
e.window.blit("\x83", blit_fg, blit_bg) e.w_blit("\x83", blit_fg, blit_bg)
else else
e.window.setCursorPos(4, 2 + math.floor(st / 3)) e.w_set_cur(4, 2 + math.floor(st / 3))
e.window.blit("\x8f", blit_fg, blit_bg) e.w_blit("\x8f", blit_fg, blit_bg)
end end
-- left -- left
if st % 3 == 0 then if st % 3 == 0 then
e.window.setCursorPos(1, 3 - math.floor(st / 3)) e.w_set_cur(1, 3 - math.floor(st / 3))
e.window.blit("\x83", blit_fg, blit_bg) e.w_blit("\x83", blit_fg, blit_bg)
e.window.setCursorPos(1, 2 - math.floor(st / 3)) e.w_set_cur(1, 2 - math.floor(st / 3))
e.window.blit("\x8f", blit_bg, blit_fg) e.w_blit("\x8f", blit_bg, blit_fg)
elseif st % 3 == 1 then elseif st % 3 == 1 then
e.window.setCursorPos(1, 2 - math.floor(st / 3)) e.w_set_cur(1, 2 - math.floor(st / 3))
e.window.blit("\x83", blit_bg, blit_fg) e.w_blit("\x83", blit_bg, blit_fg)
else else
e.window.setCursorPos(1, 2 - math.floor(st / 3)) e.w_set_cur(1, 2 - math.floor(st / 3))
e.window.blit("\x8f", blit_fg, blit_bg) e.w_blit("\x8f", blit_fg, blit_bg)
end end
end end

View File

@ -25,8 +25,8 @@ local function colormap(args)
local e = element.new(args) local e = element.new(args)
-- draw color map -- draw color map
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit(spaces, bkg, bkg) e.w_blit(spaces, bkg, bkg)
return e.complete() return e.complete()
end end

View File

@ -36,9 +36,8 @@ local function app_button(args)
local e = element.new(args) local e = element.new(args)
-- write app title, centered -- write app title, centered
e.window.setCursorPos(1, 4) e.w_set_cur(math.floor((e.frame.w - string.len(args.title)) / 2) + 1, 4)
e.window.setCursorPos(math.floor((e.frame.w - string.len(args.title)) / 2) + 1, 4) e.w_write(args.title)
e.window.write(args.title)
-- draw the app button -- draw the app button
local function draw() local function draw()
@ -51,36 +50,36 @@ local function app_button(args)
end end
-- draw icon -- draw icon
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.setTextColor(fgd) e.w_set_fgd(fgd)
e.window.setBackgroundColor(bkg) e.w_set_bkg(bkg)
e.window.write("\x9f\x83\x83\x83") e.w_write("\x9f\x83\x83\x83")
e.window.setTextColor(bkg) e.w_set_fgd(bkg)
e.window.setBackgroundColor(fgd) e.w_set_bkg(fgd)
e.window.write("\x90") e.w_write("\x90")
e.window.setTextColor(fgd) e.w_set_fgd(fgd)
e.window.setBackgroundColor(bkg) e.w_set_bkg(bkg)
e.window.setCursorPos(1, 2) e.w_set_cur(1, 2)
e.window.write("\x95 ") e.w_write("\x95 ")
e.window.setTextColor(bkg) e.w_set_fgd(bkg)
e.window.setBackgroundColor(fgd) e.w_set_bkg(fgd)
e.window.write("\x95") e.w_write("\x95")
e.window.setCursorPos(1, 3) e.w_set_cur(1, 3)
e.window.write("\x82\x8f\x8f\x8f\x81") e.w_write("\x82\x8f\x8f\x8f\x81")
-- write the icon text -- write the icon text
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.setTextColor(fgd) e.w_set_fgd(fgd)
e.window.setBackgroundColor(bkg) e.w_set_bkg(bkg)
e.window.write(args.text) e.w_write(args.text)
end end
-- draw the app button as pressed (if active_fg_bg set) -- draw the app button as pressed (if active_fg_bg set)
local function show_pressed() local function show_pressed()
if e.enabled and args.active_fg_bg ~= nil then if e.enabled and args.active_fg_bg ~= nil then
e.value = true e.value = true
e.window.setTextColor(args.active_fg_bg.fgd) e.w_set_fgd(args.active_fg_bg.fgd)
e.window.setBackgroundColor(args.active_fg_bg.bkg) e.w_set_bkg(args.active_fg_bg.bkg)
draw() draw()
end end
end end
@ -89,8 +88,8 @@ local function app_button(args)
local function show_unpressed() local function show_unpressed()
if e.enabled and args.active_fg_bg ~= nil then if e.enabled and args.active_fg_bg ~= nil then
e.value = false e.value = false
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
draw() draw()
end end
end end

View File

@ -32,24 +32,24 @@ local function checkbox(args)
-- show the button state -- show the button state
local function draw() local function draw()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if e.value then if e.value then
-- show as selected -- show as selected
e.window.setTextColor(args.box_fg_bg.bkg) e.w_set_fgd(args.box_fg_bg.bkg)
e.window.setBackgroundColor(args.box_fg_bg.fgd) e.w_set_bkg(args.box_fg_bg.fgd)
e.window.write("\x88") e.w_write("\x88")
e.window.setTextColor(args.box_fg_bg.fgd) e.w_set_fgd(args.box_fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.write("\x95") e.w_write("\x95")
else else
-- show as unselected -- show as unselected
e.window.setTextColor(e.fg_bg.bkg) e.w_set_fgd(e.fg_bg.bkg)
e.window.setBackgroundColor(args.box_fg_bg.bkg) e.w_set_bkg(args.box_fg_bg.bkg)
e.window.write("\x88") e.w_write("\x88")
e.window.setTextColor(args.box_fg_bg.bkg) e.w_set_fgd(args.box_fg_bg.bkg)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.write("\x95") e.w_write("\x95")
end end
end end
@ -71,10 +71,10 @@ local function checkbox(args)
end end
-- write label text -- write label text
e.window.setCursorPos(3, 1) e.w_set_cur(3, 1)
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.write(args.label) e.w_write(args.label)
-- initial draw -- initial draw
draw() draw()

View File

@ -1,7 +1,6 @@
-- Hazard-bordered Button Graphics Element -- Hazard-bordered Button Graphics Element
local tcd = require("scada-common.tcd") local tcd = require("scada-common.tcd")
local util = require("scada-common.util")
local core = require("graphics.core") local core = require("graphics.core")
local element = require("graphics.element") local element = require("graphics.element")
@ -34,35 +33,35 @@ local function hazard_button(args)
local e = element.new(args) local e = element.new(args)
-- write the button text -- write the button text
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
-- draw border -- draw border
---@param accent color accent color ---@param accent color accent color
local function draw_border(accent) local function draw_border(accent)
-- top -- top
e.window.setTextColor(accent) e.w_set_fgd(accent)
e.window.setBackgroundColor(args.fg_bg.bkg) e.w_set_bkg(args.fg_bg.bkg)
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.write("\x99" .. util.strrep("\x89", args.width - 2) .. "\x99") e.w_write("\x99" .. string.rep("\x89", args.width - 2) .. "\x99")
-- center left -- center left
e.window.setCursorPos(1, 2) e.w_set_cur(1, 2)
e.window.setTextColor(args.fg_bg.bkg) e.w_set_fgd(args.fg_bg.bkg)
e.window.setBackgroundColor(accent) e.w_set_bkg(accent)
e.window.write("\x99") e.w_write("\x99")
-- center right -- center right
e.window.setTextColor(args.fg_bg.bkg) e.w_set_fgd(args.fg_bg.bkg)
e.window.setBackgroundColor(accent) e.w_set_bkg(accent)
e.window.setCursorPos(args.width, 2) e.w_set_cur(args.width, 2)
e.window.write("\x99") e.w_write("\x99")
-- bottom -- bottom
e.window.setTextColor(accent) e.w_set_fgd(accent)
e.window.setBackgroundColor(args.fg_bg.bkg) e.w_set_bkg(args.fg_bg.bkg)
e.window.setCursorPos(1, 3) e.w_set_cur(1, 3)
e.window.write("\x99" .. util.strrep("\x98", args.width - 2) .. "\x99") e.w_write("\x99" .. string.rep("\x98", args.width - 2) .. "\x99")
end end
-- on request timeout: recursively calls itself to double flash button text -- on request timeout: recursively calls itself to double flash button text
@ -73,9 +72,9 @@ local function hazard_button(args)
if n == 0 then if n == 0 then
-- go back off -- go back off
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
end end
if n >= 4 then if n >= 4 then
@ -83,18 +82,18 @@ local function hazard_button(args)
elseif n % 2 == 0 then elseif n % 2 == 0 then
-- toggle text color on after 0.25 seconds -- toggle text color on after 0.25 seconds
tcd.dispatch(0.25, function () tcd.dispatch(0.25, function ()
e.window.setTextColor(args.accent) e.w_set_fgd(args.accent)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
on_timeout(n + 1) on_timeout(n + 1)
on_timeout(n + 1) on_timeout(n + 1)
end) end)
elseif n % 1 then elseif n % 1 then
-- toggle text color off after 0.25 seconds -- toggle text color off after 0.25 seconds
tcd.dispatch(0.25, function () tcd.dispatch(0.25, function ()
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
on_timeout(n + 1) on_timeout(n + 1)
end) end)
end end
@ -102,9 +101,9 @@ local function hazard_button(args)
-- blink routine for success indication -- blink routine for success indication
local function on_success() local function on_success()
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
end end
-- blink routine for failure indication -- blink routine for failure indication
@ -115,9 +114,9 @@ local function hazard_button(args)
if n == 0 then if n == 0 then
-- go back off -- go back off
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
end end
if n >= 2 then if n >= 2 then
@ -125,17 +124,17 @@ local function hazard_button(args)
elseif n % 2 == 0 then elseif n % 2 == 0 then
-- toggle text color on after 0.5 seconds -- toggle text color on after 0.5 seconds
tcd.dispatch(0.5, function () tcd.dispatch(0.5, function ()
e.window.setTextColor(args.accent) e.w_set_fgd(args.accent)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
on_failure(n + 1) on_failure(n + 1)
end) end)
elseif n % 1 then elseif n % 1 then
-- toggle text color off after 0.25 seconds -- toggle text color off after 0.25 seconds
tcd.dispatch(0.25, function () tcd.dispatch(0.25, function ()
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
on_failure(n + 1) on_failure(n + 1)
end) end)
end end
@ -147,9 +146,9 @@ local function hazard_button(args)
if e.enabled then if e.enabled then
if core.events.was_clicked(event.type) then if core.events.was_clicked(event.type) then
-- change text color to indicate clicked -- change text color to indicate clicked
e.window.setTextColor(args.accent) e.w_set_fgd(args.accent)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
-- abort any other callbacks -- abort any other callbacks
tcd.abort(on_timeout) tcd.abort(on_timeout)
@ -182,18 +181,18 @@ local function hazard_button(args)
function e.disable() function e.disable()
if args.dis_colors then if args.dis_colors then
draw_border(args.dis_colors.color_a) draw_border(args.dis_colors.color_a)
e.window.setTextColor(args.dis_colors.color_b) e.w_set_fgd(args.dis_colors.color_b)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
end end
end end
-- show the button as enabled -- show the button as enabled
function e.enable() function e.enable()
draw_border(args.accent) draw_border(args.accent)
e.window.setTextColor(args.fg_bg.fgd) e.w_set_fgd(args.fg_bg.fgd)
e.window.setCursorPos(3, 2) e.w_set_cur(3, 2)
e.window.write(args.text) e.w_write(args.text)
end end
-- initial draw of border -- initial draw of border

View File

@ -75,19 +75,19 @@ local function multi_button(args)
for i = 1, #args.options do for i = 1, #args.options do
local opt = args.options[i] ---@type button_option local opt = args.options[i] ---@type button_option
e.window.setCursorPos(opt._start_x, 1) e.w_set_cur(opt._start_x, 1)
if e.value == i then if e.value == i then
-- show as pressed -- show as pressed
e.window.setTextColor(opt.active_fg_bg.fgd) e.w_set_fgd(opt.active_fg_bg.fgd)
e.window.setBackgroundColor(opt.active_fg_bg.bkg) e.w_set_bkg(opt.active_fg_bg.bkg)
else else
-- show as unpressed -- show as unpressed
e.window.setTextColor(opt.fg_bg.fgd) e.w_set_fgd(opt.fg_bg.fgd)
e.window.setBackgroundColor(opt.fg_bg.bkg) e.w_set_bkg(opt.fg_bg.bkg)
end end
e.window.write(util.pad(opt.text, button_width)) e.w_write(util.pad(opt.text, button_width))
end end
end end

View File

@ -48,16 +48,16 @@ local function push_button(args)
e.window.clear() e.window.clear()
-- write the button text -- write the button text
e.window.setCursorPos(h_pad, v_pad) e.w_set_cur(h_pad, v_pad)
e.window.write(args.text) e.w_write(args.text)
end end
-- draw the button as pressed (if active_fg_bg set) -- draw the button as pressed (if active_fg_bg set)
local function show_pressed() local function show_pressed()
if e.enabled and args.active_fg_bg ~= nil then if e.enabled and args.active_fg_bg ~= nil then
e.value = true e.value = true
e.window.setTextColor(args.active_fg_bg.fgd) e.w_set_fgd(args.active_fg_bg.fgd)
e.window.setBackgroundColor(args.active_fg_bg.bkg) e.w_set_bkg(args.active_fg_bg.bkg)
draw() draw()
end end
end end
@ -66,8 +66,8 @@ local function push_button(args)
local function show_unpressed() local function show_unpressed()
if e.enabled and args.active_fg_bg ~= nil then if e.enabled and args.active_fg_bg ~= nil then
e.value = false e.value = false
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
draw() draw()
end end
end end
@ -102,8 +102,8 @@ local function push_button(args)
function e.enable() function e.enable()
if args.dis_fg_bg ~= nil then if args.dis_fg_bg ~= nil then
e.value = false e.value = false
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
draw() draw()
end end
end end
@ -112,8 +112,8 @@ local function push_button(args)
function e.disable() function e.disable()
if args.dis_fg_bg ~= nil then if args.dis_fg_bg ~= nil then
e.value = false e.value = false
e.window.setTextColor(args.dis_fg_bg.fgd) e.w_set_fgd(args.dis_fg_bg.fgd)
e.window.setBackgroundColor(args.dis_fg_bg.bkg) e.w_set_bkg(args.dis_fg_bg.bkg)
draw() draw()
end end
end end

View File

@ -56,28 +56,28 @@ local function radio_button(args)
for i = 1, #args.options do for i = 1, #args.options do
local opt = args.options[i] ---@type string local opt = args.options[i] ---@type string
e.window.setCursorPos(1, i) e.w_set_cur(1, i)
if e.value == i then if e.value == i then
-- show as selected -- show as selected
e.window.setTextColor(args.radio_colors.color_a) e.w_set_fgd(args.radio_colors.color_a)
e.window.setBackgroundColor(args.radio_bg) e.w_set_bkg(args.radio_bg)
else else
-- show as unselected -- show as unselected
e.window.setTextColor(args.radio_colors.color_b) e.w_set_fgd(args.radio_colors.color_b)
e.window.setBackgroundColor(args.radio_bg) e.w_set_bkg(args.radio_bg)
end end
e.window.write("\x88") e.w_write("\x88")
e.window.setTextColor(args.radio_bg) e.w_set_fgd(args.radio_bg)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.write("\x95") e.w_write("\x95")
-- write button text -- write button text
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.write(opt) e.w_write(opt)
end end
end end

View File

@ -52,27 +52,27 @@ local function sidebar(args)
local y = ((i - 1) * 3) + 1 local y = ((i - 1) * 3) + 1
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
if pressed and i == pressed_idx then if pressed and i == pressed_idx then
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
else else
e.window.setTextColor(tab.color.fgd) e.w_set_fgd(tab.color.fgd)
e.window.setBackgroundColor(tab.color.bkg) e.w_set_bkg(tab.color.bkg)
end end
e.window.write(" ") e.w_write(" ")
e.window.setCursorPos(1, y + 1) e.w_set_cur(1, y + 1)
if e.value == i then if e.value == i then
-- show as selected -- show as selected
e.window.write(" " .. tab.char .. "\x10") e.w_write(" " .. tab.char .. "\x10")
else else
-- show as unselected -- show as unselected
e.window.write(" " .. tab.char .. " ") e.w_write(" " .. tab.char .. " ")
end end
e.window.setCursorPos(1, y + 2) e.w_set_cur(1, y + 2)
e.window.write(" ") e.w_write(" ")
end end
end end

View File

@ -58,17 +58,17 @@ local function spinbox(args)
-- draw the arrows -- draw the arrows
local function draw_arrows(color) local function draw_arrows(color)
e.window.setBackgroundColor(args.arrow_fg_bg.bkg) e.w_set_bkg(args.arrow_fg_bg.bkg)
e.window.setTextColor(color) e.w_set_fgd(color)
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.write(util.strrep("\x1e", wn_prec)) e.w_write(string.rep("\x1e", wn_prec))
e.window.setCursorPos(1, 3) e.w_set_cur(1, 3)
e.window.write(util.strrep("\x1f", wn_prec)) e.w_write(string.rep("\x1f", wn_prec))
if fr_prec > 0 then if fr_prec > 0 then
e.window.setCursorPos(1 + wn_prec, 1) e.w_set_cur(1 + wn_prec, 1)
e.window.write(" " .. util.strrep("\x1e", fr_prec)) e.w_write(" " .. string.rep("\x1e", fr_prec))
e.window.setCursorPos(1 + wn_prec, 3) e.w_set_cur(1 + wn_prec, 3)
e.window.write(" " .. util.strrep("\x1f", fr_prec)) e.w_write(" " .. string.rep("\x1f", fr_prec))
end end
end end
@ -119,10 +119,10 @@ local function spinbox(args)
end end
-- draw -- draw
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setCursorPos(1, 2) e.w_set_cur(1, 2)
e.window.write(util.sprintf(fmt, e.value)) e.w_write(util.sprintf(fmt, e.value))
end end
-- init with the default value -- init with the default value

View File

@ -47,20 +47,20 @@ local function switch_button(args)
local function draw_state() local function draw_state()
if e.value then if e.value then
-- show as pressed -- show as pressed
e.window.setTextColor(args.active_fg_bg.fgd) e.w_set_fgd(args.active_fg_bg.fgd)
e.window.setBackgroundColor(args.active_fg_bg.bkg) e.w_set_bkg(args.active_fg_bg.bkg)
else else
-- show as unpressed -- show as unpressed
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
-- clear to redraw background -- clear to redraw background
e.window.clear() e.window.clear()
-- write the button text -- write the button text
e.window.setCursorPos(h_pad, v_pad) e.w_set_cur(h_pad, v_pad)
e.window.write(args.text) e.w_write(args.text)
end end
-- initial draw -- initial draw

View File

@ -71,17 +71,17 @@ local function tabbar(args)
for i = 1, #args.tabs do for i = 1, #args.tabs do
local tab = args.tabs[i] ---@type tabbar_tab local tab = args.tabs[i] ---@type tabbar_tab
e.window.setCursorPos(tab._start_x, 1) e.w_set_cur(tab._start_x, 1)
if e.value == i then if e.value == i then
e.window.setTextColor(tab.color.fgd) e.w_set_fgd(tab.color.fgd)
e.window.setBackgroundColor(tab.color.bkg) e.w_set_bkg(tab.color.bkg)
else else
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
e.window.write(util.pad(tab.name, button_width)) e.w_write(util.pad(tab.name, button_width))
end end
end end

View File

@ -53,17 +53,17 @@ local function alarm_indicator_light(args)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if flash_on then if flash_on then
if e.value == 2 then if e.value == 2 then
e.window.blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg)
end end
else else
if e.value == 3 then if e.value == 3 then
e.window.blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg)
else else
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
end end
@ -76,7 +76,7 @@ local function alarm_indicator_light(args)
local was_off = e.value ~= 2 local was_off = e.value ~= 2
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if args.flash then if args.flash then
if was_off and (new_state == 2) then if was_off and (new_state == 2) then
@ -87,17 +87,17 @@ local function alarm_indicator_light(args)
flasher.stop(flash_callback) flasher.stop(flash_callback)
if new_state == 3 then if new_state == 3 then
e.window.blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg)
else else
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
end end
elseif new_state == 2 then elseif new_state == 2 then
e.window.blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg)
elseif new_state == 3 then elseif new_state == 3 then
e.window.blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg)
else else
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
end end
@ -107,7 +107,7 @@ local function alarm_indicator_light(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(1) e.on_update(1)
e.window.write(args.label) e.w_write(args.label)
return e.complete() return e.complete()
end end

View File

@ -47,25 +47,25 @@ local function core_map(args)
-- create coordinate grid and frame -- create coordinate grid and frame
local function draw_frame() local function draw_frame()
e.window.setTextColor(colors.white) e.w_set_fgd(colors.white)
for x = 0, (inner_width - 1) do for x = 0, (inner_width - 1) do
e.window.setCursorPos(x + start_x, 1) e.w_set_cur(x + start_x, 1)
e.window.write(util.sprintf("%X", x)) e.w_write(util.sprintf("%X", x))
end end
for y = 0, (inner_height - 1) do for y = 0, (inner_height - 1) do
e.window.setCursorPos(1, y + start_y) e.w_set_cur(1, y + start_y)
e.window.write(util.sprintf("%X", y)) e.w_write(util.sprintf("%X", y))
end end
-- even out bottom edge -- even out bottom edge
e.window.setTextColor(e.fg_bg.bkg) e.w_set_fgd(e.fg_bg.bkg)
e.window.setBackgroundColor(args.parent.get_fg_bg().bkg) e.w_set_bkg(args.parent.get_fg_bg().bkg)
e.window.setCursorPos(1, e.frame.h) e.w_set_cur(1, e.frame.h)
e.window.write(util.strrep("\x8f", e.frame.w)) e.w_write(string.rep("\x8f", e.frame.w))
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
-- draw the core -- draw the core
@ -102,13 +102,13 @@ local function core_map(args)
-- draw pattern -- draw pattern
for y = start_y, inner_height + (start_y - 1) do for y = start_y, inner_height + (start_y - 1) do
e.window.setCursorPos(start_x, y) e.w_set_cur(start_x, y)
for _ = 1, inner_width do for _ = 1, inner_width do
if alternator then if alternator then
i = i + 1 i = i + 1
e.window.blit("\x07", text_c, back_c) e.w_blit("\x07", text_c, back_c)
else else
e.window.blit("\x07", "7", "8") e.w_blit("\x07", "7", "8")
end end
alternator = not alternator alternator = not alternator

View File

@ -37,12 +37,12 @@ local function data(args)
-- label color -- label color
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_a) e.w_set_fgd(args.lu_colors.color_a)
end end
-- write label -- write label
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.write(args.label) e.w_write(args.label)
local value_color = e.fg_bg.fgd local value_color = e.fg_bg.fgd
local label_len = string.len(args.label) local label_len = string.len(args.label)
@ -60,25 +60,25 @@ local function data(args)
e.value = value e.value = value
-- clear old data and label -- clear old data and label
e.window.setCursorPos(data_start, 1) e.w_set_cur(data_start, 1)
e.window.write(util.spaces(clear_width)) e.w_write(util.spaces(clear_width))
-- write data -- write data
local data_str = util.sprintf(args.format, value) local data_str = util.sprintf(args.format, value)
e.window.setCursorPos(data_start, 1) e.w_set_cur(data_start, 1)
e.window.setTextColor(value_color) e.w_set_fgd(value_color)
if args.commas then if args.commas then
e.window.write(util.comma_format(data_str)) e.w_write(util.comma_format(data_str))
else else
e.window.write(data_str) e.w_write(data_str)
end end
-- write label -- write label
if args.unit ~= nil then if args.unit ~= nil then
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_b) e.w_set_fgd(args.lu_colors.color_b)
end end
e.window.write(" " .. args.unit) e.w_write(" " .. args.unit)
end end
end end

View File

@ -87,16 +87,16 @@ local function hbar(args)
-- draw bar -- draw bar
for y = 1, e.frame.h do for y = 1, e.frame.h do
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
-- intentionally swapped fgd/bkg since we use spaces as fill, but they are the opposite -- intentionally swapped fgd/bkg since we use spaces as fill, but they are the opposite
e.window.blit(spaces, bkg, fgd) e.w_blit(spaces, bkg, fgd)
end end
end end
-- update percentage -- update percentage
if args.show_percent then if args.show_percent then
e.window.setCursorPos(bar_width + 2, math.max(1, math.ceil(e.frame.h / 2))) e.w_set_cur(bar_width + 2, math.max(1, math.ceil(e.frame.h / 2)))
e.window.write(util.sprintf("%3.0f%%", fraction * 100)) e.w_write(util.sprintf("%3.0f%%", fraction * 100))
end end
end end

View File

@ -1,7 +1,5 @@
-- Icon Indicator Graphics Element -- Icon Indicator Graphics Element
local util = require("scada-common.util")
local element = require("graphics.element") local element = require("graphics.element")
---@class icon_sym_color ---@class icon_sym_color
@ -44,22 +42,22 @@ local function icon(args)
table.insert(state_blit_cmds, { table.insert(state_blit_cmds, {
text = " " .. sym_color.symbol .. " ", text = " " .. sym_color.symbol .. " ",
fgd = util.strrep(sym_color.color.blit_fgd, 3), fgd = string.rep(sym_color.color.blit_fgd, 3),
bkg = util.strrep(sym_color.color.blit_bkg, 3) bkg = string.rep(sym_color.color.blit_bkg, 3)
}) })
end end
-- write label and initial indicator light -- write label and initial indicator light
e.window.setCursorPos(5, 1) e.w_set_cur(5, 1)
e.window.write(args.label) e.w_write(args.label)
-- on state change -- on state change
---@param new_state integer indicator state ---@param new_state integer indicator state
function e.on_update(new_state) function e.on_update(new_state)
local blit_cmd = state_blit_cmds[new_state] local blit_cmd = state_blit_cmds[new_state]
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg) e.w_blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg)
end end
-- set indicator state -- set indicator state

View File

@ -44,12 +44,12 @@ local function indicator_led(args)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if flash_on then if flash_on then
e.window.blit("\x8c", args.colors.blit_a, e.fg_bg.blit_bkg) e.w_blit("\x8c", args.colors.blit_a, e.fg_bg.blit_bkg)
else else
e.window.blit("\x8c", args.colors.blit_b, e.fg_bg.blit_bkg) e.w_blit("\x8c", args.colors.blit_b, e.fg_bg.blit_bkg)
end end
flash_on = not flash_on flash_on = not flash_on
@ -61,8 +61,8 @@ local function indicator_led(args)
flash_on = true flash_on = true
flasher.start(flash_callback, args.period) flasher.start(flash_callback, args.period)
else else
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit("\x8c", args.colors.blit_a, e.fg_bg.blit_bkg) e.w_blit("\x8c", args.colors.blit_a, e.fg_bg.blit_bkg)
end end
end end
@ -73,8 +73,8 @@ local function indicator_led(args)
flasher.stop(flash_callback) flasher.stop(flash_callback)
end end
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit("\x8c", args.colors.blit_b, e.fg_bg.blit_bkg) e.w_blit("\x8c", args.colors.blit_b, e.fg_bg.blit_bkg)
end end
-- on state change -- on state change
@ -91,8 +91,8 @@ local function indicator_led(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(false) e.on_update(false)
if string.len(args.label) > 0 then if string.len(args.label) > 0 then
e.window.setCursorPos(3, 1) e.w_set_cur(3, 1)
e.window.write(args.label) e.w_write(args.label)
end end
return e.complete() return e.complete()

View File

@ -56,16 +56,16 @@ local function indicator_led_pair(args)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if flash_on then if flash_on then
if e.value == 2 then if e.value == 2 then
e.window.blit("\x8c", c1, e.fg_bg.blit_bkg) e.w_blit("\x8c", c1, e.fg_bg.blit_bkg)
elseif e.value == 3 then elseif e.value == 3 then
e.window.blit("\x8c", c2, e.fg_bg.blit_bkg) e.w_blit("\x8c", c2, e.fg_bg.blit_bkg)
end end
else else
e.window.blit("\x8c", co, e.fg_bg.blit_bkg) e.w_blit("\x8c", co, e.fg_bg.blit_bkg)
end end
flash_on = not flash_on flash_on = not flash_on
@ -77,7 +77,7 @@ local function indicator_led_pair(args)
local was_off = e.value <= 1 local was_off = e.value <= 1
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if args.flash then if args.flash then
if was_off and (new_state > 1) then if was_off and (new_state > 1) then
@ -87,14 +87,14 @@ local function indicator_led_pair(args)
flash_on = false flash_on = false
flasher.stop(flash_callback) flasher.stop(flash_callback)
e.window.blit("\x8c", co, e.fg_bg.blit_bkg) e.w_blit("\x8c", co, e.fg_bg.blit_bkg)
end end
elseif new_state == 2 then elseif new_state == 2 then
e.window.blit("\x8c", c1, e.fg_bg.blit_bkg) e.w_blit("\x8c", c1, e.fg_bg.blit_bkg)
elseif new_state == 3 then elseif new_state == 3 then
e.window.blit("\x8c", c2, e.fg_bg.blit_bkg) e.w_blit("\x8c", c2, e.fg_bg.blit_bkg)
else else
e.window.blit("\x8c", co, e.fg_bg.blit_bkg) e.w_blit("\x8c", co, e.fg_bg.blit_bkg)
end end
end end
@ -105,8 +105,8 @@ local function indicator_led_pair(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(1) e.on_update(1)
if string.len(args.label) > 0 then if string.len(args.label) > 0 then
e.window.setCursorPos(3, 1) e.w_set_cur(3, 1)
e.window.write(args.label) e.w_write(args.label)
end end
return e.complete() return e.complete()

View File

@ -37,9 +37,9 @@ local function indicator_led_rgb(args)
---@param new_state integer indicator state ---@param new_state integer indicator state
function e.on_update(new_state) function e.on_update(new_state)
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if type(args.colors[new_state]) == "number" then if type(args.colors[new_state]) == "number" then
e.window.blit("\x8c", colors.toBlit(args.colors[new_state]), e.fg_bg.blit_bkg) e.w_blit("\x8c", colors.toBlit(args.colors[new_state]), e.fg_bg.blit_bkg)
end end
end end
@ -50,8 +50,8 @@ local function indicator_led_rgb(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(1) e.on_update(1)
if string.len(args.label) > 0 then if string.len(args.label) > 0 then
e.window.setCursorPos(3, 1) e.w_set_cur(3, 1)
e.window.write(args.label) e.w_write(args.label)
end end
return e.complete() return e.complete()

View File

@ -44,12 +44,12 @@ local function indicator_light(args)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if flash_on then if flash_on then
e.window.blit(" \x95", "0" .. args.colors.blit_a, args.colors.blit_a .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. args.colors.blit_a, args.colors.blit_a .. e.fg_bg.blit_bkg)
else else
e.window.blit(" \x95", "0" .. args.colors.blit_b, args.colors.blit_b .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. args.colors.blit_b, args.colors.blit_b .. e.fg_bg.blit_bkg)
end end
flash_on = not flash_on flash_on = not flash_on
@ -61,8 +61,8 @@ local function indicator_light(args)
flash_on = true flash_on = true
flasher.start(flash_callback, args.period) flasher.start(flash_callback, args.period)
else else
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit(" \x95", "0" .. args.colors.blit_a, args.colors.blit_a .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. args.colors.blit_a, args.colors.blit_a .. e.fg_bg.blit_bkg)
end end
end end
@ -73,8 +73,8 @@ local function indicator_light(args)
flasher.stop(flash_callback) flasher.stop(flash_callback)
end end
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit(" \x95", "0" .. args.colors.blit_b, args.colors.blit_b .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. args.colors.blit_b, args.colors.blit_b .. e.fg_bg.blit_bkg)
end end
-- on state change -- on state change
@ -90,8 +90,8 @@ local function indicator_light(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(false) e.on_update(false)
e.window.setCursorPos(3, 1) e.w_set_cur(3, 1)
e.window.write(args.label) e.w_write(args.label)
return e.complete() return e.complete()
end end

View File

@ -34,12 +34,12 @@ local function power(args)
-- label color -- label color
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_a) e.w_set_fgd(args.lu_colors.color_a)
end end
-- write label -- write label
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.write(args.label) e.w_write(args.label)
local data_start = string.len(args.label) + 2 local data_start = string.len(args.label) + 2
if string.len(args.label) == 0 then data_start = 1 end if string.len(args.label) == 0 then data_start = 1 end
@ -52,13 +52,13 @@ local function power(args)
local data_str, unit = util.power_format(value, false, args.format) local data_str, unit = util.power_format(value, false, args.format)
-- write data -- write data
e.window.setCursorPos(data_start, 1) e.w_set_cur(data_start, 1)
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.write(util.comma_format(data_str)) e.w_write(util.comma_format(data_str))
-- write unit -- write unit
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_b) e.w_set_fgd(args.lu_colors.color_b)
end end
-- append per tick if rate is set -- append per tick if rate is set
@ -70,7 +70,7 @@ local function power(args)
if unit == "FE" then unit = "FE " end if unit == "FE" then unit = "FE " end
end end
e.window.write(" " .. unit) e.w_write(" " .. unit)
end end
-- set the value -- set the value

View File

@ -36,12 +36,12 @@ local function rad(args)
-- label color -- label color
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_a) e.w_set_fgd(args.lu_colors.color_a)
end end
-- write label -- write label
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.write(args.label) e.w_write(args.label)
local label_len = string.len(args.label) local label_len = string.len(args.label)
local data_start = 1 local data_start = 1
@ -58,24 +58,24 @@ local function rad(args)
e.value = value.radiation e.value = value.radiation
-- clear old data and label -- clear old data and label
e.window.setCursorPos(data_start, 1) e.w_set_cur(data_start, 1)
e.window.write(util.spaces(clear_width)) e.w_write(util.spaces(clear_width))
-- write data -- write data
local data_str = util.sprintf(args.format, e.value) local data_str = util.sprintf(args.format, e.value)
e.window.setCursorPos(data_start, 1) e.w_set_cur(data_start, 1)
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
if args.commas then if args.commas then
e.window.write(util.comma_format(data_str)) e.w_write(util.comma_format(data_str))
else else
e.window.write(data_str) e.w_write(data_str)
end end
-- write unit -- write unit
if args.lu_colors ~= nil then if args.lu_colors ~= nil then
e.window.setTextColor(args.lu_colors.color_b) e.w_set_fgd(args.lu_colors.color_b)
end end
e.window.write(" " .. value.unit) e.w_write(" " .. value.unit)
end end
-- set the value -- set the value

View File

@ -51,8 +51,8 @@ local function state_indicator(args)
table.insert(state_blit_cmds, { table.insert(state_blit_cmds, {
text = text, text = text,
fgd = util.strrep(state_def.color.blit_fgd, string.len(text)), fgd = string.rep(state_def.color.blit_fgd, string.len(text)),
bkg = util.strrep(state_def.color.blit_bkg, string.len(text)) bkg = string.rep(state_def.color.blit_bkg, string.len(text))
}) })
end end
@ -64,8 +64,8 @@ local function state_indicator(args)
function e.on_update(new_state) function e.on_update(new_state)
local blit_cmd = state_blit_cmds[new_state] local blit_cmd = state_blit_cmds[new_state]
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
e.window.blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg) e.w_blit(blit_cmd.text, blit_cmd.fgd, blit_cmd.bkg)
end end
-- set indicator state -- set indicator state

View File

@ -56,16 +56,16 @@ local function tristate_indicator_light(args)
-- called by flasher when enabled -- called by flasher when enabled
local function flash_callback() local function flash_callback()
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if flash_on then if flash_on then
if e.value == 2 then if e.value == 2 then
e.window.blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg)
elseif e.value == 3 then elseif e.value == 3 then
e.window.blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg)
end end
else else
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
flash_on = not flash_on flash_on = not flash_on
@ -77,7 +77,7 @@ local function tristate_indicator_light(args)
local was_off = e.value <= 1 local was_off = e.value <= 1
e.value = new_state e.value = new_state
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
if args.flash then if args.flash then
if was_off and (new_state > 1) then if was_off and (new_state > 1) then
@ -87,14 +87,14 @@ local function tristate_indicator_light(args)
flash_on = false flash_on = false
flasher.stop(flash_callback) flasher.stop(flash_callback)
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
elseif new_state == 2 then elseif new_state == 2 then
e.window.blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c2, c2 .. e.fg_bg.blit_bkg)
elseif new_state == 3 then elseif new_state == 3 then
e.window.blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c3, c3 .. e.fg_bg.blit_bkg)
else else
e.window.blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg) e.w_blit(" \x95", "0" .. c1, c1 .. e.fg_bg.blit_bkg)
end end
end end
@ -104,7 +104,7 @@ local function tristate_indicator_light(args)
-- write label and initial indicator light -- write label and initial indicator light
e.on_update(1) e.on_update(1)
e.window.write(args.label) e.w_write(args.label)
return e.complete() return e.complete()
end end

View File

@ -27,11 +27,11 @@ local function vbar(args)
local e = element.new(args) local e = element.new(args)
-- blit strings -- blit strings
local fgd = util.strrep(e.fg_bg.blit_fgd, e.frame.w) local fgd = string.rep(e.fg_bg.blit_fgd, e.frame.w)
local bkg = util.strrep(e.fg_bg.blit_bkg, e.frame.w) local bkg = string.rep(e.fg_bg.blit_bkg, e.frame.w)
local spaces = util.spaces(e.frame.w) local spaces = util.spaces(e.frame.w)
local one_third = util.strrep("\x8f", e.frame.w) local one_third = string.rep("\x8f", e.frame.w)
local two_thirds = util.strrep("\x83", e.frame.w) local two_thirds = string.rep("\x83", e.frame.w)
-- handle data changes -- handle data changes
---@param fraction number 0.0 to 1.0 ---@param fraction number 0.0 to 1.0
@ -56,28 +56,28 @@ local function vbar(args)
local y = e.frame.h local y = e.frame.h
-- start at base of vertical bar -- start at base of vertical bar
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
-- fill percentage -- fill percentage
for _ = 1, num_bars / 3 do for _ = 1, num_bars / 3 do
e.window.blit(spaces, bkg, fgd) e.w_blit(spaces, bkg, fgd)
y = y - 1 y = y - 1
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
end end
-- add fractional bar if needed -- add fractional bar if needed
if num_bars % 3 == 1 then if num_bars % 3 == 1 then
e.window.blit(one_third, bkg, fgd) e.w_blit(one_third, bkg, fgd)
y = y - 1 y = y - 1
elseif num_bars % 3 == 2 then elseif num_bars % 3 == 2 then
e.window.blit(two_thirds, bkg, fgd) e.w_blit(two_thirds, bkg, fgd)
y = y - 1 y = y - 1
end end
-- fill the rest blank -- fill the rest blank
while y > 0 do while y > 0 do
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
e.window.blit(spaces, fgd, bkg) e.w_blit(spaces, fgd, bkg)
y = y - 1 y = y - 1
end end
end end
@ -86,8 +86,8 @@ local function vbar(args)
-- change bar color -- change bar color
---@param fg_bg cpair new bar colors ---@param fg_bg cpair new bar colors
function e.recolor(fg_bg) function e.recolor(fg_bg)
fgd = util.strrep(fg_bg.blit_fgd, e.frame.w) fgd = string.rep(fg_bg.blit_fgd, e.frame.w)
bkg = util.strrep(fg_bg.blit_bkg, e.frame.w) bkg = string.rep(fg_bg.blit_bkg, e.frame.w)
-- re-draw -- re-draw
last_num_bars = 0 last_num_bars = 0

View File

@ -63,34 +63,34 @@ local function listbox(args)
-- draw up/down arrows -- draw up/down arrows
if pressed_arrow == 1 then if pressed_arrow == 1 then
e.window.setTextColor(active_fg_bg.fgd) e.w_set_fgd(active_fg_bg.fgd)
e.window.setBackgroundColor(active_fg_bg.bkg) e.w_set_bkg(active_fg_bg.bkg)
e.window.setCursorPos(e.frame.w, 1) e.w_set_cur(e.frame.w, 1)
e.window.write("\x1e") e.w_write("\x1e")
e.window.setTextColor(nav_fg_bg.fgd) e.w_set_fgd(nav_fg_bg.fgd)
e.window.setBackgroundColor(nav_fg_bg.bkg) e.w_set_bkg(nav_fg_bg.bkg)
e.window.setCursorPos(e.frame.w, e.frame.h) e.w_set_cur(e.frame.w, e.frame.h)
e.window.write("\x1f") e.w_write("\x1f")
elseif pressed_arrow == -1 then elseif pressed_arrow == -1 then
e.window.setTextColor(nav_fg_bg.fgd) e.w_set_fgd(nav_fg_bg.fgd)
e.window.setBackgroundColor(nav_fg_bg.bkg) e.w_set_bkg(nav_fg_bg.bkg)
e.window.setCursorPos(e.frame.w, 1) e.w_set_cur(e.frame.w, 1)
e.window.write("\x1e") e.w_write("\x1e")
e.window.setTextColor(active_fg_bg.fgd) e.w_set_fgd(active_fg_bg.fgd)
e.window.setBackgroundColor(active_fg_bg.bkg) e.w_set_bkg(active_fg_bg.bkg)
e.window.setCursorPos(e.frame.w, e.frame.h) e.w_set_cur(e.frame.w, e.frame.h)
e.window.write("\x1f") e.w_write("\x1f")
else else
e.window.setTextColor(nav_fg_bg.fgd) e.w_set_fgd(nav_fg_bg.fgd)
e.window.setBackgroundColor(nav_fg_bg.bkg) e.w_set_bkg(nav_fg_bg.bkg)
e.window.setCursorPos(e.frame.w, 1) e.w_set_cur(e.frame.w, 1)
e.window.write("\x1e") e.w_write("\x1e")
e.window.setCursorPos(e.frame.w, e.frame.h) e.w_set_cur(e.frame.w, e.frame.h)
e.window.write("\x1f") e.w_write("\x1f")
end end
e.window.setTextColor(e.fg_bg.fgd) e.w_set_fgd(e.fg_bg.fgd)
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
-- render the scroll bar and re-cacluate height & bounds -- render the scroll bar and re-cacluate height & bounds
@ -115,23 +115,23 @@ local function listbox(args)
for i = 2, e.frame.h - 1 do for i = 2, e.frame.h - 1 do
if (i >= offset and i < (bar_height + offset)) and (bar_height ~= max_bar_height) then if (i >= offset and i < (bar_height + offset)) and (bar_height ~= max_bar_height) then
if args.nav_fg_bg ~= nil then if args.nav_fg_bg ~= nil then
e.window.setBackgroundColor(args.nav_fg_bg.fgd) e.w_set_bkg(args.nav_fg_bg.fgd)
else else
e.window.setBackgroundColor(e.fg_bg.fgd) e.w_set_bkg(e.fg_bg.fgd)
end end
else else
if args.nav_fg_bg ~= nil then if args.nav_fg_bg ~= nil then
e.window.setBackgroundColor(args.nav_fg_bg.bkg) e.w_set_bkg(args.nav_fg_bg.bkg)
else else
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
end end
e.window.setCursorPos(e.frame.w, i) e.w_set_cur(e.frame.w, i)
e.window.write(" ") e.w_write(" ")
end end
e.window.setBackgroundColor(e.fg_bg.bkg) e.w_set_bkg(e.fg_bg.bkg)
end end
-- update item y positions and move elements -- update item y positions and move elements

View File

@ -73,7 +73,7 @@ local function pipenet(args)
y_step = util.trinary(pipe.y1 == pipe.y2, 0, y_step) y_step = util.trinary(pipe.y1 == pipe.y2, 0, y_step)
end end
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
local c = core.cpair(pipe.color, e.fg_bg.bkg) local c = core.cpair(pipe.color, e.fg_bg.bkg)
@ -84,24 +84,24 @@ local function pipenet(args)
if i == pipe.w then if i == pipe.w then
-- corner -- corner
if y_step > 0 then if y_step > 0 then
e.window.blit("\x93", c.blit_bkg, c.blit_fgd) e.w_blit("\x93", c.blit_bkg, c.blit_fgd)
else else
e.window.blit("\x8e", c.blit_fgd, c.blit_bkg) e.w_blit("\x8e", c.blit_fgd, c.blit_bkg)
end end
else else
e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) e.w_blit("\x8c", c.blit_fgd, c.blit_bkg)
end end
else else
if i == pipe.w and y_step > 0 then if i == pipe.w and y_step > 0 then
-- corner -- corner
e.window.blit(" ", c.blit_bkg, c.blit_fgd) e.w_blit(" ", c.blit_bkg, c.blit_fgd)
else else
e.window.blit("\x8f", c.blit_fgd, c.blit_bkg) e.w_blit("\x8f", c.blit_fgd, c.blit_bkg)
end end
end end
x = x + x_step x = x + x_step
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
end end
-- back up one -- back up one
@ -109,12 +109,12 @@ local function pipenet(args)
for _ = 1, pipe.h - 1 do for _ = 1, pipe.h - 1 do
y = y + y_step y = y + y_step
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
if pipe.thin then if pipe.thin then
e.window.blit("\x95", c.blit_bkg, c.blit_fgd) e.w_blit("\x95", c.blit_bkg, c.blit_fgd)
else else
e.window.blit(" ", c.blit_bkg, c.blit_fgd) e.w_blit(" ", c.blit_bkg, c.blit_fgd)
end end
end end
else else
@ -124,26 +124,26 @@ local function pipenet(args)
if i == pipe.h then if i == pipe.h then
-- corner -- corner
if y_step < 0 then if y_step < 0 then
e.window.blit("\x97", c.blit_bkg, c.blit_fgd) e.w_blit("\x97", c.blit_bkg, c.blit_fgd)
elseif y_step > 0 then elseif y_step > 0 then
e.window.blit("\x8d", c.blit_fgd, c.blit_bkg) e.w_blit("\x8d", c.blit_fgd, c.blit_bkg)
else else
e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) e.w_blit("\x8c", c.blit_fgd, c.blit_bkg)
end end
else else
e.window.blit("\x95", c.blit_fgd, c.blit_bkg) e.w_blit("\x95", c.blit_fgd, c.blit_bkg)
end end
else else
if i == pipe.h and y_step < 0 then if i == pipe.h and y_step < 0 then
-- corner -- corner
e.window.blit("\x83", c.blit_bkg, c.blit_fgd) e.w_blit("\x83", c.blit_bkg, c.blit_fgd)
else else
e.window.blit(" ", c.blit_bkg, c.blit_fgd) e.w_blit(" ", c.blit_bkg, c.blit_fgd)
end end
end end
y = y + y_step y = y + y_step
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
end end
-- back up one -- back up one
@ -151,12 +151,12 @@ local function pipenet(args)
for _ = 1, pipe.w - 1 do for _ = 1, pipe.w - 1 do
x = x + x_step x = x + x_step
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
if pipe.thin then if pipe.thin then
e.window.blit("\x8c", c.blit_fgd, c.blit_bkg) e.w_blit("\x8c", c.blit_fgd, c.blit_bkg)
else else
e.window.blit("\x83", c.blit_bkg, c.blit_fgd) e.w_blit("\x83", c.blit_bkg, c.blit_fgd)
end end
end end
end end
@ -298,12 +298,12 @@ local function pipenet(args)
end end
end end
e.window.setCursorPos(x, y) e.w_set_cur(x, y)
if invert then if invert then
e.window.blit(char, entry.bg, entry.fg) e.w_blit(char, entry.bg, entry.fg)
else else
e.window.blit(char, entry.fg, entry.bg) e.w_blit(char, entry.fg, entry.bg)
end end
end end
end end

View File

@ -56,7 +56,7 @@ local function rectangle(args)
-- draw bordered box if requested -- draw bordered box if requested
-- element constructor will have drawn basic colored rectangle regardless -- element constructor will have drawn basic colored rectangle regardless
if args.border ~= nil then if args.border ~= nil then
e.window.setCursorPos(1, 1) e.w_set_cur(1, 1)
local border_width = offset_x local border_width = offset_x
local border_height = offset_y local border_height = offset_y
@ -70,45 +70,45 @@ local function rectangle(args)
-- form the basic line strings and top/bottom blit strings -- form the basic line strings and top/bottom blit strings
local spaces = util.spaces(e.frame.w) local spaces = util.spaces(e.frame.w)
local blit_fg = util.strrep(e.fg_bg.blit_fgd, e.frame.w) local blit_fg = string.rep(e.fg_bg.blit_fgd, e.frame.w)
local blit_fg_sides = blit_fg local blit_fg_sides = blit_fg
local blit_bg_sides = "" local blit_bg_sides = ""
local blit_bg_top_bot = util.strrep(border_blit, e.frame.w) local blit_bg_top_bot = string.rep(border_blit, e.frame.w)
-- partial bars -- partial bars
local p_a, p_b, p_s local p_a, p_b, p_s
if args.thin == true then if args.thin == true then
if args.even_inner == true then if args.even_inner == true then
p_a = "\x9c" .. util.strrep("\x8c", inner_width) .. "\x93" p_a = "\x9c" .. string.rep("\x8c", inner_width) .. "\x93"
p_b = "\x8d" .. util.strrep("\x8c", inner_width) .. "\x8e" p_b = "\x8d" .. string.rep("\x8c", inner_width) .. "\x8e"
else else
p_a = "\x97" .. util.strrep("\x83", inner_width) .. "\x94" p_a = "\x97" .. string.rep("\x83", inner_width) .. "\x94"
p_b = "\x8a" .. util.strrep("\x8f", inner_width) .. "\x85" p_b = "\x8a" .. string.rep("\x8f", inner_width) .. "\x85"
end end
p_s = "\x95" .. util.spaces(inner_width) .. "\x95" p_s = "\x95" .. util.spaces(inner_width) .. "\x95"
else else
if args.even_inner == true then if args.even_inner == true then
p_a = util.strrep("\x83", inner_width + width_x2) p_a = string.rep("\x83", inner_width + width_x2)
p_b = util.strrep("\x8f", inner_width + width_x2) p_b = string.rep("\x8f", inner_width + width_x2)
else else
p_a = util.spaces(border_width) .. util.strrep("\x8f", inner_width) .. util.spaces(border_width) p_a = util.spaces(border_width) .. string.rep("\x8f", inner_width) .. util.spaces(border_width)
p_b = util.spaces(border_width) .. util.strrep("\x83", inner_width) .. util.spaces(border_width) p_b = util.spaces(border_width) .. string.rep("\x83", inner_width) .. util.spaces(border_width)
end end
p_s = spaces p_s = spaces
end end
local p_inv_fg = util.strrep(border_blit, border_width) .. util.strrep(e.fg_bg.blit_bkg, inner_width) .. local p_inv_fg = string.rep(border_blit, border_width) .. string.rep(e.fg_bg.blit_bkg, inner_width) ..
util.strrep(border_blit, border_width) string.rep(border_blit, border_width)
local p_inv_bg = util.strrep(e.fg_bg.blit_bkg, border_width) .. util.strrep(border_blit, inner_width) .. local p_inv_bg = string.rep(e.fg_bg.blit_bkg, border_width) .. string.rep(border_blit, inner_width) ..
util.strrep(e.fg_bg.blit_bkg, border_width) string.rep(e.fg_bg.blit_bkg, border_width)
if args.thin == true then if args.thin == true then
p_inv_fg = e.fg_bg.blit_bkg .. util.strrep(e.fg_bg.blit_bkg, inner_width) .. util.strrep(border_blit, border_width) p_inv_fg = e.fg_bg.blit_bkg .. string.rep(e.fg_bg.blit_bkg, inner_width) .. string.rep(border_blit, border_width)
p_inv_bg = border_blit .. util.strrep(border_blit, inner_width) .. util.strrep(e.fg_bg.blit_bkg, border_width) p_inv_bg = border_blit .. string.rep(border_blit, inner_width) .. string.rep(e.fg_bg.blit_bkg, border_width)
blit_fg_sides = border_blit .. util.strrep(e.fg_bg.blit_bkg, inner_width) .. e.fg_bg.blit_bkg blit_fg_sides = border_blit .. string.rep(e.fg_bg.blit_bkg, inner_width) .. e.fg_bg.blit_bkg
end end
-- form the body blit strings (sides are border, inside is normal) -- form the body blit strings (sides are border, inside is normal)
@ -127,28 +127,28 @@ local function rectangle(args)
-- draw rectangle with borders -- draw rectangle with borders
for y = 1, e.frame.h do for y = 1, e.frame.h do
e.window.setCursorPos(1, y) e.w_set_cur(1, y)
-- top border -- top border
if y <= border_height then if y <= border_height then
-- partial pixel fill -- partial pixel fill
if args.border.even and y == border_height then if args.border.even and y == border_height then
if args.thin == true then if args.thin == true then
e.window.blit(p_a, p_inv_bg, p_inv_fg) e.w_blit(p_a, p_inv_bg, p_inv_fg)
else else
local _fg = util.trinary(args.even_inner == true, util.strrep(e.fg_bg.blit_bkg, e.frame.w), p_inv_bg) local _fg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), p_inv_bg)
local _bg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) local _bg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg)
if width_x2 % 3 == 1 then if width_x2 % 3 == 1 then
e.window.blit(p_b, _fg, _bg) e.w_blit(p_b, _fg, _bg)
elseif width_x2 % 3 == 2 then elseif width_x2 % 3 == 2 then
e.window.blit(p_a, _fg, _bg) e.w_blit(p_a, _fg, _bg)
else else
-- skip line -- skip line
e.window.blit(spaces, blit_fg, blit_bg_sides) e.w_blit(spaces, blit_fg, blit_bg_sides)
end end
end end
else else
e.window.blit(spaces, blit_fg, blit_bg_top_bot) e.w_blit(spaces, blit_fg, blit_bg_top_bot)
end end
-- bottom border -- bottom border
elseif y > (e.frame.h - border_width) then elseif y > (e.frame.h - border_width) then
@ -156,31 +156,31 @@ local function rectangle(args)
if args.border.even and y == ((e.frame.h - border_width) + 1) then if args.border.even and y == ((e.frame.h - border_width) + 1) then
if args.thin == true then if args.thin == true then
if args.even_inner == true then if args.even_inner == true then
e.window.blit(p_b, blit_bg_top_bot, util.strrep(e.fg_bg.blit_bkg, e.frame.w)) e.w_blit(p_b, blit_bg_top_bot, string.rep(e.fg_bg.blit_bkg, e.frame.w))
else else
e.window.blit(p_b, util.strrep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot) e.w_blit(p_b, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot)
end end
else else
local _fg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg) local _fg = util.trinary(args.even_inner == true, blit_bg_top_bot, p_inv_fg)
local _bg = util.trinary(args.even_inner == true, util.strrep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot) local _bg = util.trinary(args.even_inner == true, string.rep(e.fg_bg.blit_bkg, e.frame.w), blit_bg_top_bot)
if width_x2 % 3 == 1 then if width_x2 % 3 == 1 then
e.window.blit(p_a, _fg, _bg) e.w_blit(p_a, _fg, _bg)
elseif width_x2 % 3 == 2 then elseif width_x2 % 3 == 2 then
e.window.blit(p_b, _fg, _bg) e.w_blit(p_b, _fg, _bg)
else else
-- skip line -- skip line
e.window.blit(spaces, blit_fg, blit_bg_sides) e.w_blit(spaces, blit_fg, blit_bg_sides)
end end
end end
else else
e.window.blit(spaces, blit_fg, blit_bg_top_bot) e.w_blit(spaces, blit_fg, blit_bg_top_bot)
end end
else else
if args.thin == true then if args.thin == true then
e.window.blit(p_s, blit_fg_sides, blit_bg_sides) e.w_blit(p_s, blit_fg_sides, blit_bg_sides)
else else
e.window.blit(p_s, blit_fg, blit_bg_sides) e.w_blit(p_s, blit_fg, blit_bg_sides)
end end
end end
end end

View File

@ -45,14 +45,14 @@ local function textbox(args)
-- use cursor position to align this line -- use cursor position to align this line
if alignment == TEXT_ALIGN.CENTER then if alignment == TEXT_ALIGN.CENTER then
e.window.setCursorPos(math.floor((e.frame.w - len) / 2) + 1, i) e.w_set_cur(math.floor((e.frame.w - len) / 2) + 1, i)
elseif alignment == TEXT_ALIGN.RIGHT then elseif alignment == TEXT_ALIGN.RIGHT then
e.window.setCursorPos((e.frame.w - len) + 1, i) e.w_set_cur((e.frame.w - len) + 1, i)
else else
e.window.setCursorPos(1, i) e.w_set_cur(1, i)
end end
e.window.write(lines[i]) e.w_write(lines[i])
end end
end end

View File

@ -42,7 +42,7 @@ local function tiling(args)
-- border -- border
if args.border_c ~= nil then if args.border_c ~= nil then
e.window.setBackgroundColor(args.border_c) e.w_set_bkg(args.border_c)
e.window.clear() e.window.clear()
start_x = 1 + util.trinary(even, 2, 1) start_x = 1 + util.trinary(even, 2, 1)
@ -60,19 +60,19 @@ local function tiling(args)
-- create pattern -- create pattern
for y = start_y, inner_height + (start_y - 1) do for y = start_y, inner_height + (start_y - 1) do
e.window.setCursorPos(start_x, y) e.w_set_cur(start_x, y)
for _ = 1, inner_width do for _ = 1, inner_width do
if alternator then if alternator then
if even then if even then
e.window.blit(" ", "00", fill_a .. fill_a) e.w_blit(" ", "00", fill_a .. fill_a)
else else
e.window.blit(" ", "0", fill_a) e.w_blit(" ", "0", fill_a)
end end
else else
if even then if even then
e.window.blit(" ", "00", fill_b .. fill_b) e.w_blit(" ", "00", fill_b .. fill_b)
else else
e.window.blit(" ", "0", fill_b) e.w_blit(" ", "0", fill_b)
end end
end end

View File

@ -1,5 +1,3 @@
require("lockbox").insecure();
local Bit = require("lockbox.util.bit"); local Bit = require("lockbox.util.bit");
local String = require("string"); local String = require("string");
local Math = require("math"); local Math = require("math");

View File

@ -1,5 +1,3 @@
require("lockbox").insecure();
local Bit = require("lockbox.util.bit"); local Bit = require("lockbox.util.bit");
local String = require("string"); local String = require("string");
local Math = require("math"); local Math = require("math");

View File

@ -1,25 +1,6 @@
local Lockbox = {}; local Lockbox = {}
-- cc-mek-scada lockbox version -- cc-mek-scada lockbox version
Lockbox.version = "1.0" Lockbox.version = "1.1"
--[[ return Lockbox
package.path = "./?.lua;"
.. "./cipher/?.lua;"
.. "./digest/?.lua;"
.. "./kdf/?.lua;"
.. "./mac/?.lua;"
.. "./padding/?.lua;"
.. "./test/?.lua;"
.. "./util/?.lua;"
.. package.path;
--]]
Lockbox.ALLOW_INSECURE = true;
Lockbox.insecure = function()
assert(Lockbox.ALLOW_INSECURE,
"This module is insecure! It should not be used in production." ..
"If you really want to use it, set Lockbox.ALLOW_INSECURE to true before importing it");
end
return Lockbox;

View File

@ -8,7 +8,7 @@ local HMAC = function()
local public = {}; local public = {};
local blockSize = 64; local blockSize = 64;
local Digest = nil; local Digest;
local outerPadding = {}; local outerPadding = {};
local innerPadding = {} local innerPadding = {}
local digest; local digest;

View File

@ -1,4 +1,3 @@
local String = require("string"); local String = require("string");
local Bit = require("lockbox.util.bit"); local Bit = require("lockbox.util.bit");
local Queue = require("lockbox.util.queue"); local Queue = require("lockbox.util.queue");

View File

@ -7,7 +7,7 @@ local iocontrol = require("pocket.iocontrol")
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local DEVICE_TYPE = comms.DEVICE_TYPE local DEVICE_TYPE = comms.DEVICE_TYPE
local ESTABLISH_ACK = comms.ESTABLISH_ACK local ESTABLISH_ACK = comms.ESTABLISH_ACK
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local LINK_STATE = iocontrol.LINK_STATE local LINK_STATE = iocontrol.LINK_STATE
@ -51,7 +51,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
nic.open(pkt_channel) nic.open(pkt_channel)
-- send a management packet to the supervisor -- send a management packet to the supervisor
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_sv(msg_type, msg) local function _send_sv(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -65,7 +65,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
end end
-- send a management packet to the coordinator -- send a management packet to the coordinator
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_crd(msg_type, msg) local function _send_crd(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -80,24 +80,24 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
-- attempt supervisor connection establishment -- attempt supervisor connection establishment
local function _send_sv_establish() local function _send_sv_establish()
_send_sv(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT }) _send_sv(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
end end
-- attempt coordinator API connection establishment -- attempt coordinator API connection establishment
local function _send_api_establish() local function _send_api_establish()
_send_crd(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT }) _send_crd(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
end end
-- keep alive ack to supervisor -- keep alive ack to supervisor
---@param srv_time integer ---@param srv_time integer
local function _send_sv_keep_alive_ack(srv_time) local function _send_sv_keep_alive_ack(srv_time)
_send_sv(SCADA_MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() }) _send_sv(MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() })
end end
-- keep alive ack to coordinator -- keep alive ack to coordinator
---@param srv_time integer ---@param srv_time integer
local function _send_api_keep_alive_ack(srv_time) local function _send_api_keep_alive_ack(srv_time)
_send_crd(SCADA_MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() }) _send_crd(MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() })
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -111,7 +111,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
self.sv.linked = false self.sv.linked = false
self.sv.r_seq_num = nil self.sv.r_seq_num = nil
self.sv.addr = comms.BROADCAST self.sv.addr = comms.BROADCAST
_send_sv(SCADA_MGMT_TYPE.CLOSE, {}) _send_sv(MGMT_TYPE.CLOSE, {})
end end
-- close connection to coordinator API server -- close connection to coordinator API server
@ -120,7 +120,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
self.api.linked = false self.api.linked = false
self.api.r_seq_num = nil self.api.r_seq_num = nil
self.api.addr = comms.BROADCAST self.api.addr = comms.BROADCAST
_send_crd(SCADA_MGMT_TYPE.CLOSE, {}) _send_crd(MGMT_TYPE.CLOSE, {})
end end
-- close the connections to the servers -- close the connections to the servers
@ -157,21 +157,21 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
-- supervisor get active alarm tones -- supervisor get active alarm tones
function public.diag__get_alarm_tones() function public.diag__get_alarm_tones()
if self.sv.linked then _send_sv(SCADA_MGMT_TYPE.DIAG_TONE_GET, {}) end if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_TONE_GET, {}) end
end end
-- supervisor test alarm tones by tone -- supervisor test alarm tones by tone
---@param id TONE|0 tone ID, or 0 to stop all ---@param id TONE|0 tone ID, or 0 to stop all
---@param state boolean tone state ---@param state boolean tone state
function public.diag__set_alarm_tone(id, state) function public.diag__set_alarm_tone(id, state)
if self.sv.linked then _send_sv(SCADA_MGMT_TYPE.DIAG_TONE_SET, { id, state }) end if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_TONE_SET, { id, state }) end
end end
-- supervisor test alarm tones by alarm -- supervisor test alarm tones by alarm
---@param id ALARM|0 alarm ID, 0 to stop all ---@param id ALARM|0 alarm ID, 0 to stop all
---@param state boolean alarm state ---@param state boolean alarm state
function public.diag__set_alarm(id, state) function public.diag__set_alarm(id, state)
if self.sv.linked then _send_sv(SCADA_MGMT_TYPE.DIAG_ALARM_SET, { id, state }) end if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_ALARM_SET, { id, state }) end
end end
-- parse a packet -- parse a packet
@ -180,7 +180,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
---@param reply_to integer ---@param reply_to integer
---@param message any ---@param message any
---@param distance integer ---@param distance integer
---@return mgmt_frame|capi_frame|nil packet ---@return mgmt_frame|crdn_frame|nil packet
function public.parse_packet(side, sender, reply_to, message, distance) function public.parse_packet(side, sender, reply_to, message, distance)
local s_pkt = nic.receive(side, sender, reply_to, message, distance) local s_pkt = nic.receive(side, sender, reply_to, message, distance)
local pkt = nil local pkt = nil
@ -192,11 +192,11 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
if mgmt_pkt.decode(s_pkt) then if mgmt_pkt.decode(s_pkt) then
pkt = mgmt_pkt.get() pkt = mgmt_pkt.get()
end end
-- get as coordinator API packet -- get as coordinator packet
elseif s_pkt.protocol() == PROTOCOL.COORD_API then elseif s_pkt.protocol() == PROTOCOL.SCADA_CRDN then
local capi_pkt = comms.capi_packet() local crdn_pkt = comms.crdn_packet()
if capi_pkt.decode(s_pkt) then if crdn_pkt.decode(s_pkt) then
pkt = capi_pkt.get() pkt = crdn_pkt.get()
end end
else else
log.debug("attempted parse of illegal packet type " .. s_pkt.protocol(), true) log.debug("attempted parse of illegal packet type " .. s_pkt.protocol(), true)
@ -207,7 +207,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
end end
-- handle a packet -- handle a packet
---@param packet mgmt_frame|capi_frame|nil ---@param packet mgmt_frame|crdn_frame|nil
function public.handle_packet(packet) function public.handle_packet(packet)
local diag = iocontrol.get_db().diag local diag = iocontrol.get_db().diag
@ -240,7 +240,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
if protocol == PROTOCOL.SCADA_MGMT then if protocol == PROTOCOL.SCADA_MGMT then
---@cast packet mgmt_frame ---@cast packet mgmt_frame
if self.api.linked then if self.api.linked then
if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if packet.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive request received, echo back -- keep alive request received, echo back
if packet.length == 1 then if packet.length == 1 then
local timestamp = packet.data[1] local timestamp = packet.data[1]
@ -256,7 +256,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("coordinator SCADA keep alive packet length mismatch") log.debug("coordinator SCADA keep alive packet length mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then elseif packet.type == MGMT_TYPE.CLOSE then
-- handle session close -- handle session close
api_watchdog.cancel() api_watchdog.cancel()
self.api.linked = false self.api.linked = false
@ -266,7 +266,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator") log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator")
end end
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- connection with coordinator established -- connection with coordinator established
if packet.length == 1 then if packet.length == 1 then
local est_ack = packet.data[1] local est_ack = packet.data[1]
@ -330,7 +330,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
if protocol == PROTOCOL.SCADA_MGMT then if protocol == PROTOCOL.SCADA_MGMT then
---@cast packet mgmt_frame ---@cast packet mgmt_frame
if self.sv.linked then if self.sv.linked then
if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if packet.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive request received, echo back -- keep alive request received, echo back
if packet.length == 1 then if packet.length == 1 then
local timestamp = packet.data[1] local timestamp = packet.data[1]
@ -346,14 +346,14 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("supervisor SCADA keep alive packet length mismatch") log.debug("supervisor SCADA keep alive packet length mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then elseif packet.type == MGMT_TYPE.CLOSE then
-- handle session close -- handle session close
sv_watchdog.cancel() sv_watchdog.cancel()
self.sv.linked = false self.sv.linked = false
self.sv.r_seq_num = nil self.sv.r_seq_num = nil
self.sv.addr = comms.BROADCAST self.sv.addr = comms.BROADCAST
log.info("supervisor server connection closed by remote host") log.info("supervisor server connection closed by remote host")
elseif packet.type == SCADA_MGMT_TYPE.DIAG_TONE_GET then elseif packet.type == MGMT_TYPE.DIAG_TONE_GET then
if packet.length == 8 then if packet.length == 8 then
for i = 1, #packet.data do for i = 1, #packet.data do
diag.tone_test.tone_indicators[i].update(packet.data[i] == true) diag.tone_test.tone_indicators[i].update(packet.data[i] == true)
@ -361,7 +361,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("supervisor SCADA diag alarm states packet length mismatch") log.debug("supervisor SCADA diag alarm states packet length mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.DIAG_TONE_SET then elseif packet.type == MGMT_TYPE.DIAG_TONE_SET then
if packet.length == 1 and packet.data[1] == false then if packet.length == 1 and packet.data[1] == false then
diag.tone_test.ready_warn.set_value("testing denied") diag.tone_test.ready_warn.set_value("testing denied")
log.debug("supervisor SCADA diag tone set failed") log.debug("supervisor SCADA diag tone set failed")
@ -380,7 +380,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("supervisor SCADA diag tone set packet length/type mismatch") log.debug("supervisor SCADA diag tone set packet length/type mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.DIAG_ALARM_SET then elseif packet.type == MGMT_TYPE.DIAG_ALARM_SET then
if packet.length == 1 and packet.data[1] == false then if packet.length == 1 and packet.data[1] == false then
diag.tone_test.ready_warn.set_value("testing denied") diag.tone_test.ready_warn.set_value("testing denied")
log.debug("supervisor SCADA diag alarm set failed") log.debug("supervisor SCADA diag alarm set failed")
@ -401,7 +401,7 @@ function pocket.comms(version, nic, pkt_channel, svr_channel, crd_channel, range
else else
log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor") log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor")
end end
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- connection with supervisor established -- connection with supervisor established
if packet.length == 1 then if packet.length == 1 then
local est_ack = packet.data[1] local est_ack = packet.data[1]

View File

@ -18,7 +18,7 @@ local iocontrol = require("pocket.iocontrol")
local pocket = require("pocket.pocket") local pocket = require("pocket.pocket")
local renderer = require("pocket.renderer") local renderer = require("pocket.renderer")
local POCKET_VERSION = "v0.6.0-alpha" local POCKET_VERSION = "v0.6.1-alpha"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts

View File

@ -28,6 +28,9 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local border = core.border local border = core.border
local ind_grn = style.ind_grn
local ind_red = style.ind_red
-- create new front panel view -- create new front panel view
---@param panel graphics_element main displaybox ---@param panel graphics_element main displaybox
local function init(panel) local function init(panel)
@ -41,14 +44,14 @@ local function init(panel)
local system = Div{parent=panel,width=14,height=18,x=2,y=3} local system = Div{parent=panel,width=14,height=18,x=2,y=3}
local init_ok = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)} local init_ok = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=cpair(colors.green,colors.green_off)} local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
system.line_break() system.line_break()
init_ok.register(databus.ps, "init_ok", init_ok.update) init_ok.register(databus.ps, "init_ok", init_ok.update)
heartbeat.register(databus.ps, "heartbeat", heartbeat.update) heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green} local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green}
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local modem = LED{parent=system,label="MODEM",colors=ind_grn}
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
network.update(types.PANEL_LINK_STATE.DISCONNECTED) network.update(types.PANEL_LINK_STATE.DISCONNECTED)
system.line_break() system.line_break()
@ -57,11 +60,11 @@ local function init(panel)
modem.register(databus.ps, "has_modem", modem.update) modem.register(databus.ps, "has_modem", modem.update)
network.register(databus.ps, "link_state", network.update) network.register(databus.ps, "link_state", network.update)
local rt_main = LED{parent=system,label="RT MAIN",colors=cpair(colors.green,colors.green_off)} local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
local rt_rps = LED{parent=system,label="RT RPS",colors=cpair(colors.green,colors.green_off)} local rt_rps = LED{parent=system,label="RT RPS",colors=ind_grn}
local rt_cmtx = LED{parent=system,label="RT COMMS TX",colors=cpair(colors.green,colors.green_off)} local rt_cmtx = LED{parent=system,label="RT COMMS TX",colors=ind_grn}
local rt_cmrx = LED{parent=system,label="RT COMMS RX",colors=cpair(colors.green,colors.green_off)} local rt_cmrx = LED{parent=system,label="RT COMMS RX",colors=ind_grn}
local rt_sctl = LED{parent=system,label="RT SPCTL",colors=cpair(colors.green,colors.green_off)} local rt_sctl = LED{parent=system,label="RT SPCTL",colors=ind_grn}
system.line_break() system.line_break()
rt_main.register(databus.ps, "routine__main", rt_main.update) rt_main.register(databus.ps, "routine__main", rt_main.update)
@ -80,7 +83,7 @@ local function init(panel)
local status = Div{parent=panel,width=19,height=18,x=17,y=3} local status = Div{parent=panel,width=19,height=18,x=17,y=3}
local active = LED{parent=status,x=2,width=12,label="RCT ACTIVE",colors=cpair(colors.green,colors.green_off)} local active = LED{parent=status,x=2,width=12,label="RCT ACTIVE",colors=ind_grn}
-- only show emergency coolant LED if emergency coolant is configured for this device -- only show emergency coolant LED if emergency coolant is configured for this device
if type(config.EMERGENCY_COOL) == "table" then if type(config.EMERGENCY_COOL) == "table" then
@ -90,7 +93,7 @@ local function init(panel)
local status_trip_rct = Rectangle{parent=status,width=20,height=3,x=1,border=border(1,colors.lightGray,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)} local status_trip_rct = Rectangle{parent=status,width=20,height=3,x=1,border=border(1,colors.lightGray,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)}
local status_trip = Div{parent=status_trip_rct,width=18,height=1,fg_bg=cpair(colors.black,colors.lightGray)} local status_trip = Div{parent=status_trip_rct,width=18,height=1,fg_bg=cpair(colors.black,colors.lightGray)}
local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=cpair(colors.red,colors.red_off),flash=true,period=flasher.PERIOD.BLINK_250_MS} local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=ind_red,flash=true,period=flasher.PERIOD.BLINK_250_MS}
local controls_rct = Rectangle{parent=status,width=17,height=3,x=1,border=border(1,colors.white,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)} local controls_rct = Rectangle{parent=status,width=17,height=3,x=1,border=border(1,colors.white,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)}
local controls = Div{parent=controls_rct,width=15,height=1,fg_bg=cpair(colors.black,colors.white)} local controls = Div{parent=controls_rct,width=15,height=1,fg_bg=cpair(colors.black,colors.white)}
@ -116,20 +119,20 @@ local function init(panel)
-- --
local rps = Rectangle{parent=panel,width=16,height=16,x=36,y=3,border=border(1,colors.lightGray),thin=true,fg_bg=cpair(colors.black,colors.lightGray)} local rps = Rectangle{parent=panel,width=16,height=16,x=36,y=3,border=border(1,colors.lightGray),thin=true,fg_bg=cpair(colors.black,colors.lightGray)}
local rps_man = LED{parent=rps,label="MANUAL",colors=cpair(colors.red,colors.red_off)} local rps_man = LED{parent=rps,label="MANUAL",colors=ind_red}
local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=cpair(colors.red,colors.red_off)} local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=ind_red}
local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=cpair(colors.red,colors.red_off)} local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=ind_red}
local rps_flt = LED{parent=rps,label="PLC FAULT",colors=cpair(colors.red,colors.red_off)} local rps_flt = LED{parent=rps,label="PLC FAULT",colors=ind_red}
local rps_fail = LED{parent=rps,label="RCT FAULT",colors=cpair(colors.red,colors.red_off)} local rps_fail = LED{parent=rps,label="RCT FAULT",colors=ind_red}
rps.line_break() rps.line_break()
local rps_dmg = LED{parent=rps,label="HI DAMAGE",colors=cpair(colors.red,colors.red_off)} local rps_dmg = LED{parent=rps,label="HI DAMAGE",colors=ind_red}
local rps_tmp = LED{parent=rps,label="HI TEMP",colors=cpair(colors.red,colors.red_off)} local rps_tmp = LED{parent=rps,label="HI TEMP",colors=ind_red}
rps.line_break() rps.line_break()
local rps_nof = LED{parent=rps,label="LO FUEL",colors=cpair(colors.red,colors.red_off)} local rps_nof = LED{parent=rps,label="LO FUEL",colors=ind_red}
local rps_wst = LED{parent=rps,label="HI WASTE",colors=cpair(colors.red,colors.red_off)} local rps_wst = LED{parent=rps,label="HI WASTE",colors=ind_red}
rps.line_break() rps.line_break()
local rps_ccl = LED{parent=rps,label="LO CCOOLANT",colors=cpair(colors.red,colors.red_off)} local rps_ccl = LED{parent=rps,label="LO CCOOLANT",colors=ind_red}
local rps_hcl = LED{parent=rps,label="HI HCOOLANT",colors=cpair(colors.red,colors.red_off)} local rps_hcl = LED{parent=rps,label="HI HCOOLANT",colors=ind_red}
rps_man.register(databus.ps, "rps_manual", rps_man.update) rps_man.register(databus.ps, "rps_manual", rps_man.update)
rps_auto.register(databus.ps, "rps_automatic", rps_auto.update) rps_auto.register(databus.ps, "rps_automatic", rps_auto.update)

View File

@ -39,4 +39,9 @@ style.colors = {
{ c = colors.brown, hex = 0x672223 } -- RED OFF { c = colors.brown, hex = 0x672223 } -- RED OFF
} }
-- COMMON COLOR PAIRS --
style.ind_grn = cpair(colors.green, colors.green_off)
style.ind_red = cpair(colors.red, colors.red_off)
return style return style

View File

@ -16,7 +16,7 @@ local PROTOCOL = comms.PROTOCOL
local DEVICE_TYPE = comms.DEVICE_TYPE local DEVICE_TYPE = comms.DEVICE_TYPE
local ESTABLISH_ACK = comms.ESTABLISH_ACK local ESTABLISH_ACK = comms.ESTABLISH_ACK
local RPLC_TYPE = comms.RPLC_TYPE local RPLC_TYPE = comms.RPLC_TYPE
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local AUTO_ACK = comms.PLC_AUTO_ACK local AUTO_ACK = comms.PLC_AUTO_ACK
local RPS_LIMITS = const.RPS_LIMITS local RPS_LIMITS = const.RPS_LIMITS
@ -489,7 +489,7 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -600,7 +600,7 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
-- keep alive ack -- keep alive ack
---@param srv_time integer ---@param srv_time integer
local function _send_keep_alive_ack(srv_time) local function _send_keep_alive_ack(srv_time)
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() })
end end
-- general ack -- general ack
@ -668,12 +668,12 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
function public.close() function public.close()
conn_watchdog.cancel() conn_watchdog.cancel()
public.unlink() public.unlink()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
end end
-- attempt to establish link with supervisor -- attempt to establish link with supervisor
function public.send_link_req() function public.send_link_req()
_send_mgmt(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PLC, id }) _send_mgmt(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PLC, id })
end end
-- send live status information -- send live status information
@ -929,7 +929,7 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
---@cast packet mgmt_frame ---@cast packet mgmt_frame
-- if linked, only accept packets from configured supervisor -- if linked, only accept packets from configured supervisor
if self.linked then if self.linked then
if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if packet.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive request received, echo back -- keep alive request received, echo back
if packet.length == 1 and type(packet.data[1]) == "number" then if packet.length == 1 and type(packet.data[1]) == "number" then
local timestamp = packet.data[1] local timestamp = packet.data[1]
@ -945,7 +945,7 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
else else
log.debug("SCADA_MGMT keep alive packet length/type mismatch") log.debug("SCADA_MGMT keep alive packet length/type mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then elseif packet.type == MGMT_TYPE.CLOSE then
-- handle session close -- handle session close
conn_watchdog.cancel() conn_watchdog.cancel()
public.unlink() public.unlink()
@ -954,7 +954,7 @@ function plc.comms(id, version, nic, plc_channel, svr_channel, range, reactor, r
else else
log.debug("received unsupported SCADA_MGMT packet type " .. packet.type) log.debug("received unsupported SCADA_MGMT packet type " .. packet.type)
end end
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- link request confirmation -- link request confirmation
if packet.length == 1 then if packet.length == 1 then
local est_ack = packet.data[1] local est_ack = packet.data[1]

View File

@ -19,7 +19,7 @@ local plc = require("reactor-plc.plc")
local renderer = require("reactor-plc.renderer") local renderer = require("reactor-plc.renderer")
local threads = require("reactor-plc.threads") local threads = require("reactor-plc.threads")
local R_PLC_VERSION = "v1.5.7" local R_PLC_VERSION = "v1.5.8"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts

View File

@ -215,7 +215,7 @@ function modbus.new(rtu_dev, use_parallel_read)
---@param value any ---@param value any
---@return boolean ok, MODBUS_EXCODE ---@return boolean ok, MODBUS_EXCODE
local function _5_write_single_coil(c_addr, value) local function _5_write_single_coil(c_addr, value)
local response = nil local response = MODBUS_EXCODE.OK
local _, coils, _, _ = rtu_dev.io_count() local _, coils, _, _ = rtu_dev.io_count()
local return_ok = c_addr <= coils local return_ok = c_addr <= coils
@ -239,7 +239,7 @@ function modbus.new(rtu_dev, use_parallel_read)
---@param value any ---@param value any
---@return boolean ok, MODBUS_EXCODE ---@return boolean ok, MODBUS_EXCODE
local function _6_write_single_holding_register(hr_addr, value) local function _6_write_single_holding_register(hr_addr, value)
local response = nil local response = MODBUS_EXCODE.OK
local _, _, _, hold_regs = rtu_dev.io_count() local _, _, _, hold_regs = rtu_dev.io_count()
local return_ok = hr_addr <= hold_regs local return_ok = hr_addr <= hold_regs
@ -263,7 +263,7 @@ function modbus.new(rtu_dev, use_parallel_read)
---@param values any ---@param values any
---@return boolean ok, MODBUS_EXCODE ---@return boolean ok, MODBUS_EXCODE
local function _15_write_multiple_coils(c_addr_start, values) local function _15_write_multiple_coils(c_addr_start, values)
local response = nil local response = MODBUS_EXCODE.OK
local _, coils, _, _ = rtu_dev.io_count() local _, coils, _, _ = rtu_dev.io_count()
local count = #values local count = #values
local return_ok = ((c_addr_start + count) <= (coils + 1)) and (count > 0) local return_ok = ((c_addr_start + count) <= (coils + 1)) and (count > 0)
@ -292,7 +292,7 @@ function modbus.new(rtu_dev, use_parallel_read)
---@param values any ---@param values any
---@return boolean ok, MODBUS_EXCODE ---@return boolean ok, MODBUS_EXCODE
local function _16_write_multiple_holding_registers(hr_addr_start, values) local function _16_write_multiple_holding_registers(hr_addr_start, values)
local response = nil local response = MODBUS_EXCODE.OK
local _, _, _, hold_regs = rtu_dev.io_count() local _, _, _, hold_regs = rtu_dev.io_count()
local count = #values local count = #values
local return_ok = ((hr_addr_start + count) <= (hold_regs + 1)) and (count > 0) local return_ok = ((hr_addr_start + count) <= (hold_regs + 1)) and (count > 0)
@ -403,7 +403,7 @@ function modbus.new(rtu_dev, use_parallel_read)
end end
if type(response) == "table" then if type(response) == "table" then
elseif type(response) == "nil" then elseif response == MODBUS_EXCODE.OK then
response = {} response = {}
else else
response = { response } response = { response }

View File

@ -22,8 +22,11 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC TANK", "IND MATRIX", "SPS", "SNA", "ENV DETECTOR" } local fp_label = style.fp_label
local ind_grn = style.ind_grn
local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC TANK", "IND MATRIX", "SPS", "SNA", "ENV DETECTOR" }
-- create new front panel view -- create new front panel view
---@param panel graphics_element main displaybox ---@param panel graphics_element main displaybox
@ -38,13 +41,13 @@ local function init(panel, units)
local system = Div{parent=panel,width=14,height=18,x=2,y=3} local system = Div{parent=panel,width=14,height=18,x=2,y=3}
local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)} local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=cpair(colors.green,colors.green_off)} local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
on.update(true) on.update(true)
system.line_break() system.line_break()
heartbeat.register(databus.ps, "heartbeat", heartbeat.update) heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local modem = LED{parent=system,label="MODEM",colors=ind_grn}
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}} local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
network.update(types.PANEL_LINK_STATE.DISCONNECTED) network.update(types.PANEL_LINK_STATE.DISCONNECTED)
system.line_break() system.line_break()
@ -52,8 +55,8 @@ local function init(panel, units)
modem.register(databus.ps, "has_modem", modem.update) modem.register(databus.ps, "has_modem", modem.update)
network.register(databus.ps, "link_state", network.update) network.register(databus.ps, "link_state", network.update)
local rt_main = LED{parent=system,label="RT MAIN",colors=cpair(colors.green,colors.green_off)} local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
local rt_comm = LED{parent=system,label="RT COMMS",colors=cpair(colors.green,colors.green_off)} local rt_comm = LED{parent=system,label="RT COMMS",colors=ind_grn}
system.line_break() system.line_break()
rt_main.register(databus.ps, "routine__main", rt_main.update) rt_main.register(databus.ps, "routine__main", rt_main.update)
@ -61,7 +64,7 @@ local function init(panel, units)
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
local comp_id = util.sprintf("(%d)", os.getComputerID()) local comp_id = util.sprintf("(%d)", os.getComputerID())
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=fp_label}
TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.label} TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.label}
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=cpair(colors.gray,colors.white)} local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=cpair(colors.gray,colors.white)}
@ -71,7 +74,7 @@ local function init(panel, units)
-- about label -- about label
-- --
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=cpair(colors.lightGray,colors.ivory)} local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=fp_label}
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
@ -90,7 +93,7 @@ local function init(panel, units)
-- show routine statuses -- show routine statuses
for i = 1, list_length do for i = 1, list_length do
TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1} TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1}
local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=cpair(colors.green,colors.green_off)} local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=ind_grn}
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update) rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
end end
@ -115,7 +118,7 @@ local function init(panel, units)
-- assignment (unit # or facility) -- assignment (unit # or facility)
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor) local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,height=1,fg_bg=cpair(colors.lightGray,colors.ivory)} TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,height=1,fg_bg=fp_label}
end end
end end

View File

@ -39,4 +39,10 @@ style.colors = {
{ c = colors.brown, hex = 0x672223 } -- RED OFF { c = colors.brown, hex = 0x672223 } -- RED OFF
} }
-- COMMON COLOR PAIRS --
style.fp_label = cpair(colors.lightGray, colors.ivory)
style.ind_grn = cpair(colors.green, colors.green_off)
return style return style

View File

@ -14,7 +14,7 @@ local rtu = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local DEVICE_TYPE = comms.DEVICE_TYPE local DEVICE_TYPE = comms.DEVICE_TYPE
local ESTABLISH_ACK = comms.ESTABLISH_ACK local ESTABLISH_ACK = comms.ESTABLISH_ACK
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
-- create a new RTU unit -- create a new RTU unit
@ -227,7 +227,7 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
nic.open(rtu_channel) nic.open(rtu_channel)
-- send a scada management packet -- send a scada management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send(msg_type, msg) local function _send(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -243,7 +243,7 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
-- keep alive ack -- keep alive ack
---@param srv_time integer ---@param srv_time integer
local function _send_keep_alive_ack(srv_time) local function _send_keep_alive_ack(srv_time)
_send(SCADA_MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() }) _send(MGMT_TYPE.KEEP_ALIVE, { srv_time, util.time() })
end end
-- generate device advertisement table -- generate device advertisement table
@ -298,25 +298,25 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
function public.close(rtu_state) function public.close(rtu_state)
conn_watchdog.cancel() conn_watchdog.cancel()
public.unlink(rtu_state) public.unlink(rtu_state)
_send(SCADA_MGMT_TYPE.CLOSE, {}) _send(MGMT_TYPE.CLOSE, {})
end end
-- send establish request (includes advertisement) -- send establish request (includes advertisement)
---@param units table ---@param units table
function public.send_establish(units) function public.send_establish(units)
_send(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.RTU, _generate_advertisement(units) }) _send(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.RTU, _generate_advertisement(units) })
end end
-- send capability advertisement -- send capability advertisement
---@param units table ---@param units table
function public.send_advertisement(units) function public.send_advertisement(units)
_send(SCADA_MGMT_TYPE.RTU_ADVERT, _generate_advertisement(units)) _send(MGMT_TYPE.RTU_ADVERT, _generate_advertisement(units))
end end
-- notify that a peripheral was remounted -- notify that a peripheral was remounted
---@param unit_index integer RTU unit ID ---@param unit_index integer RTU unit ID
function public.send_remounted(unit_index) function public.send_remounted(unit_index)
_send(SCADA_MGMT_TYPE.RTU_DEV_REMOUNT, { unit_index }) _send(MGMT_TYPE.RTU_DEV_REMOUNT, { unit_index })
end end
-- parse a MODBUS/SCADA packet -- parse a MODBUS/SCADA packet
@ -433,7 +433,7 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
---@cast packet mgmt_frame ---@cast packet mgmt_frame
-- SCADA management packet -- SCADA management packet
if rtu_state.linked then if rtu_state.linked then
if packet.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if packet.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive request received, echo back -- keep alive request received, echo back
if packet.length == 1 and type(packet.data[1]) == "number" then if packet.length == 1 and type(packet.data[1]) == "number" then
local timestamp = packet.data[1] local timestamp = packet.data[1]
@ -449,16 +449,16 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
else else
log.debug("SCADA_MGMT keep alive packet length/type mismatch") log.debug("SCADA_MGMT keep alive packet length/type mismatch")
end end
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then elseif packet.type == MGMT_TYPE.CLOSE then
-- close connection -- close connection
conn_watchdog.cancel() conn_watchdog.cancel()
public.unlink(rtu_state) public.unlink(rtu_state)
println_ts("server connection closed by remote host") println_ts("server connection closed by remote host")
log.warning("server connection closed by remote host") log.warning("server connection closed by remote host")
elseif packet.type == SCADA_MGMT_TYPE.RTU_ADVERT then elseif packet.type == MGMT_TYPE.RTU_ADVERT then
-- request for capabilities again -- request for capabilities again
public.send_advertisement(units) public.send_advertisement(units)
elseif packet.type == SCADA_MGMT_TYPE.RTU_TONE_ALARM then elseif packet.type == MGMT_TYPE.RTU_TONE_ALARM then
-- alarm tone update from supervisor -- alarm tone update from supervisor
if (packet.length == 1) and type(packet.data[1] == "table") and (#packet.data[1] == 8) then if (packet.length == 1) and type(packet.data[1] == "table") and (#packet.data[1] == 8) then
local states = packet.data[1] local states = packet.data[1]
@ -474,7 +474,7 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
-- not supported -- not supported
log.debug("received unsupported SCADA_MGMT message type " .. packet.type) log.debug("received unsupported SCADA_MGMT message type " .. packet.type)
end end
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
if packet.length == 1 then if packet.length == 1 then
local est_ack = packet.data[1] local est_ack = packet.data[1]

View File

@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu") local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local RTU_VERSION = "v1.6.2" local RTU_VERSION = "v1.6.4"
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE

View File

@ -6,23 +6,25 @@ local log = require("scada-common.log")
local insert = table.insert local insert = table.insert
---@type integer computer ID
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
local COMPUTER_ID = os.getComputerID() ---@type integer computer ID local COMPUTER_ID = os.getComputerID()
local max_distance = nil ---@type number|nil maximum acceptable transmission distance ---@type number|nil maximum acceptable transmission distance
local max_distance = nil
---@class comms ---@class comms
local comms = {} local comms = {}
comms.version = "2.2.1" -- protocol version (non-protocol changes tracked by util.lua version)
comms.version = "2.3.0"
---@enum PROTOCOL ---@enum PROTOCOL
local PROTOCOL = { local PROTOCOL = {
MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol MODBUS_TCP = 0, -- the "MODBUS TCP"-esque protocol
RPLC = 1, -- reactor PLC protocol RPLC = 1, -- reactor PLC protocol
SCADA_MGMT = 2, -- SCADA supervisor management, device advertisements, etc SCADA_MGMT = 2, -- SCADA supervisor management, device advertisements, etc
SCADA_CRDN = 3, -- data/control packets for coordinators to/from supervisory controllers SCADA_CRDN = 3 -- data/control packets for coordinators to/from supervisory controllers
COORD_API = 4 -- data/control packets for pocket computers to/from coordinators
} }
---@enum RPLC_TYPE ---@enum RPLC_TYPE
@ -40,8 +42,8 @@ local RPLC_TYPE = {
AUTO_BURN_RATE = 10 -- set an automatic burn rate, PLC will respond with status, enable toggle speed limited AUTO_BURN_RATE = 10 -- set an automatic burn rate, PLC will respond with status, enable toggle speed limited
} }
---@enum SCADA_MGMT_TYPE ---@enum MGMT_TYPE
local SCADA_MGMT_TYPE = { local MGMT_TYPE = {
ESTABLISH = 0, -- establish new connection ESTABLISH = 0, -- establish new connection
KEEP_ALIVE = 1, -- keep alive packet w/ RTT KEEP_ALIVE = 1, -- keep alive packet w/ RTT
CLOSE = 2, -- close a connection CLOSE = 2, -- close a connection
@ -53,8 +55,8 @@ local SCADA_MGMT_TYPE = {
DIAG_ALARM_SET = 8 -- diagnostic: set alarm to simulate audio for DIAG_ALARM_SET = 8 -- diagnostic: set alarm to simulate audio for
} }
---@enum SCADA_CRDN_TYPE ---@enum CRDN_TYPE
local SCADA_CRDN_TYPE = { local CRDN_TYPE = {
INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator
FAC_BUILDS = 1, -- facility RTU builds FAC_BUILDS = 1, -- facility RTU builds
FAC_STATUS = 2, -- state of facility and facility devices FAC_STATUS = 2, -- state of facility and facility devices
@ -64,10 +66,6 @@ local SCADA_CRDN_TYPE = {
UNIT_CMD = 6 -- command a reactor unit UNIT_CMD = 6 -- command a reactor unit
} }
---@enum CAPI_TYPE
local CAPI_TYPE = {
}
---@enum ESTABLISH_ACK ---@enum ESTABLISH_ACK
local ESTABLISH_ACK = { local ESTABLISH_ACK = {
ALLOW = 0, -- link approved ALLOW = 0, -- link approved
@ -76,14 +74,8 @@ local ESTABLISH_ACK = {
BAD_VERSION = 3 -- link denied due to comms version mismatch BAD_VERSION = 3 -- link denied due to comms version mismatch
} }
---@enum DEVICE_TYPE ---@enum DEVICE_TYPE device types for establish messages
local DEVICE_TYPE = { local DEVICE_TYPE = { PLC = 0, RTU = 1, SVR = 2, CRD = 3, PKT = 4 }
PLC = 0, -- PLC device type for establish
RTU = 1, -- RTU device type for establish
SV = 2, -- supervisor device type for establish
CRDN = 3, -- coordinator device type for establish
PKT = 4 -- pocket device type for establish
}
---@enum PLC_AUTO_ACK ---@enum PLC_AUTO_ACK
local PLC_AUTO_ACK = { local PLC_AUTO_ACK = {
@ -119,9 +111,8 @@ local UNIT_COMMAND = {
comms.PROTOCOL = PROTOCOL comms.PROTOCOL = PROTOCOL
comms.RPLC_TYPE = RPLC_TYPE comms.RPLC_TYPE = RPLC_TYPE
comms.SCADA_MGMT_TYPE = SCADA_MGMT_TYPE comms.MGMT_TYPE = MGMT_TYPE
comms.SCADA_CRDN_TYPE = SCADA_CRDN_TYPE comms.CRDN_TYPE = CRDN_TYPE
comms.CAPI_TYPE = CAPI_TYPE
comms.ESTABLISH_ACK = ESTABLISH_ACK comms.ESTABLISH_ACK = ESTABLISH_ACK
comms.DEVICE_TYPE = DEVICE_TYPE comms.DEVICE_TYPE = DEVICE_TYPE
@ -134,8 +125,8 @@ comms.FAC_COMMAND = FAC_COMMAND
-- destination broadcast address (to all devices) -- destination broadcast address (to all devices)
comms.BROADCAST = -1 comms.BROADCAST = -1
---@alias packet scada_packet|modbus_packet|rplc_packet|mgmt_packet|crdn_packet|capi_packet ---@alias packet scada_packet|modbus_packet|rplc_packet|mgmt_packet|crdn_packet
---@alias frame modbus_frame|rplc_frame|mgmt_frame|crdn_frame|capi_frame ---@alias frame modbus_frame|rplc_frame|mgmt_frame|crdn_frame
-- configure the maximum allowable message receive distance<br> -- configure the maximum allowable message receive distance<br>
-- packets received with distances greater than this will be silently discarded -- packets received with distances greater than this will be silently discarded
@ -144,7 +135,7 @@ function comms.set_trusted_range(distance)
if distance == 0 then max_distance = nil else max_distance = distance end if distance == 0 then max_distance = nil else max_distance = distance end
end end
-- generic SCADA packet object -- generic SCADA packet
---@nodiscard ---@nodiscard
function comms.scada_packet() function comms.scada_packet()
local self = { local self = {
@ -199,9 +190,9 @@ function comms.scada_packet()
self.valid = false self.valid = false
self.raw = self.modem_msg_in.msg self.raw = self.modem_msg_in.msg
if (type(max_distance) == "number") and (distance > max_distance) then if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then
-- outside of maximum allowable transmission distance -- outside of maximum allowable transmission distance
-- log.debug("comms.scada_packet.receive(): discarding packet with distance " .. distance .. " outside of trusted range") -- log.debug("comms.scada_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
else else
if type(self.raw) == "table" then if type(self.raw) == "table" then
if #self.raw == 5 then if #self.raw == 5 then
@ -227,12 +218,8 @@ function comms.scada_packet()
-- check if this packet is destined for this device -- check if this packet is destined for this device
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID) local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
self.valid = is_destination and self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
type(self.src_addr) == "number" and type(self.seq_num) == "number" and type(self.protocol) == "number" and type(self.payload) == "table"
type(self.dest_addr) == "number" and
type(self.seq_num) == "number" and
type(self.protocol) == "number" and
type(self.payload) == "table"
end end
end end
@ -275,7 +262,7 @@ function comms.scada_packet()
return public return public
end end
-- authenticated SCADA packet object -- authenticated SCADA packet
---@nodiscard ---@nodiscard
function comms.authd_packet() function comms.authd_packet()
local self = { local self = {
@ -325,7 +312,7 @@ function comms.authd_packet()
if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then
-- outside of maximum allowable transmission distance -- outside of maximum allowable transmission distance
-- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " outside of trusted range") -- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
else else
if type(self.raw) == "table" then if type(self.raw) == "table" then
if #self.raw == 4 then if #self.raw == 4 then
@ -343,11 +330,8 @@ function comms.authd_packet()
-- check if this packet is destined for this device -- check if this packet is destined for this device
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID) local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
self.valid = is_destination and self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
type(self.src_addr) == "number" and type(self.mac) == "string" and type(self.payload) == "string"
type(self.dest_addr) == "number" and
type(self.mac) == "string" and
type(self.payload) == "string"
end end
end end
@ -381,8 +365,7 @@ function comms.authd_packet()
return public return public
end end
-- MODBUS packet<br> -- MODBUS packet, modeled after MODBUS TCP
-- modeled after MODBUS TCP packet
---@nodiscard ---@nodiscard
function comms.modbus_packet() function comms.modbus_packet()
local self = { local self = {
@ -436,9 +419,7 @@ function comms.modbus_packet()
public.make(data[1], data[2], data[3], { table.unpack(data, 4, #data) }) public.make(data[1], data[2], data[3], { table.unpack(data, 4, #data) })
end end
local valid = type(self.txn_id) == "number" and local valid = type(self.txn_id) == "number" and type(self.unit_id) == "number" and type(self.func_code) == "number"
type(self.unit_id) == "number" and
type(self.func_code) == "number"
return size_ok and valid return size_ok and valid
else else
@ -489,21 +470,6 @@ function comms.rplc_packet()
---@class rplc_packet ---@class rplc_packet
local public = {} local public = {}
-- check that type is known
local function _rplc_type_valid()
return self.type == RPLC_TYPE.STATUS or
self.type == RPLC_TYPE.MEK_STRUCT or
self.type == RPLC_TYPE.MEK_BURN_RATE or
self.type == RPLC_TYPE.RPS_ENABLE or
self.type == RPLC_TYPE.RPS_SCRAM or
self.type == RPLC_TYPE.RPS_ASCRAM or
self.type == RPLC_TYPE.RPS_STATUS or
self.type == RPLC_TYPE.RPS_ALARM or
self.type == RPLC_TYPE.RPS_RESET or
self.type == RPLC_TYPE.RPS_AUTO_RESET or
self.type == RPLC_TYPE.AUTO_BURN_RATE
end
-- make an RPLC packet -- make an RPLC packet
---@param id integer ---@param id integer
---@param packet_type RPLC_TYPE ---@param packet_type RPLC_TYPE
@ -539,7 +505,6 @@ function comms.rplc_packet()
if ok then if ok then
local data = frame.data() local data = frame.data()
public.make(data[1], data[2], { table.unpack(data, 3, #data) }) public.make(data[1], data[2], { table.unpack(data, 3, #data) })
ok = _rplc_type_valid()
end end
ok = ok and type(self.id) == "number" ok = ok and type(self.id) == "number"
@ -583,7 +548,7 @@ function comms.mgmt_packet()
local self = { local self = {
frame = nil, frame = nil,
raw = {}, raw = {},
type = 0, ---@type SCADA_MGMT_TYPE type = 0, ---@type MGMT_TYPE
length = 0, length = 0,
data = {} data = {}
} }
@ -591,22 +556,8 @@ function comms.mgmt_packet()
---@class mgmt_packet ---@class mgmt_packet
local public = {} local public = {}
-- check that type is known
local function _scada_type_valid()
return self.type == SCADA_MGMT_TYPE.ESTABLISH or
self.type == SCADA_MGMT_TYPE.KEEP_ALIVE or
self.type == SCADA_MGMT_TYPE.CLOSE or
self.type == SCADA_MGMT_TYPE.REMOTE_LINKED or
self.type == SCADA_MGMT_TYPE.RTU_ADVERT or
self.type == SCADA_MGMT_TYPE.RTU_DEV_REMOUNT or
self.type == SCADA_MGMT_TYPE.RTU_TONE_ALARM or
self.type == SCADA_MGMT_TYPE.DIAG_TONE_GET or
self.type == SCADA_MGMT_TYPE.DIAG_TONE_SET or
self.type == SCADA_MGMT_TYPE.DIAG_ALARM_SET
end
-- make a SCADA management packet -- make a SCADA management packet
---@param packet_type SCADA_MGMT_TYPE ---@param packet_type MGMT_TYPE
---@param data table ---@param data table
function public.make(packet_type, data) function public.make(packet_type, data)
if type(data) == "table" then if type(data) == "table" then
@ -638,7 +589,6 @@ function comms.mgmt_packet()
if ok then if ok then
local data = frame.data() local data = frame.data()
public.make(data[1], { table.unpack(data, 2, #data) }) public.make(data[1], { table.unpack(data, 2, #data) })
ok = _scada_type_valid()
end end
return ok return ok
@ -679,7 +629,7 @@ function comms.crdn_packet()
local self = { local self = {
frame = nil, frame = nil,
raw = {}, raw = {},
type = 0, ---@type SCADA_CRDN_TYPE type = 0, ---@type CRDN_TYPE
length = 0, length = 0,
data = {} data = {}
} }
@ -687,20 +637,8 @@ function comms.crdn_packet()
---@class crdn_packet ---@class crdn_packet
local public = {} local public = {}
-- check that type is known
---@nodiscard
local function _crdn_type_valid()
return self.type == SCADA_CRDN_TYPE.INITIAL_BUILDS or
self.type == SCADA_CRDN_TYPE.FAC_BUILDS or
self.type == SCADA_CRDN_TYPE.FAC_STATUS or
self.type == SCADA_CRDN_TYPE.FAC_CMD or
self.type == SCADA_CRDN_TYPE.UNIT_BUILDS or
self.type == SCADA_CRDN_TYPE.UNIT_STATUSES or
self.type == SCADA_CRDN_TYPE.UNIT_CMD
end
-- make a coordinator packet -- make a coordinator packet
---@param packet_type SCADA_CRDN_TYPE ---@param packet_type CRDN_TYPE
---@param data table ---@param data table
function public.make(packet_type, data) function public.make(packet_type, data)
if type(data) == "table" then if type(data) == "table" then
@ -732,7 +670,6 @@ function comms.crdn_packet()
if ok then if ok then
local data = frame.data() local data = frame.data()
public.make(data[1], { table.unpack(data, 2, #data) }) public.make(data[1], { table.unpack(data, 2, #data) })
ok = _crdn_type_valid()
end end
return ok return ok
@ -767,92 +704,4 @@ function comms.crdn_packet()
return public return public
end end
-- coordinator API (CAPI) packet
---@todo implement for pocket access, set enum type for self.type
---@nodiscard
function comms.capi_packet()
local self = {
frame = nil,
raw = {},
type = 0,
length = 0,
data = {}
}
---@class capi_packet
local public = {}
local function _capi_type_valid()
---@todo
return false
end
-- make a coordinator API packet
---@param packet_type CAPI_TYPE
---@param data table
function public.make(packet_type, data)
if type(data) == "table" then
-- packet accessor properties
self.type = packet_type
self.length = #data
self.data = data
-- populate raw array
self.raw = { self.type }
for i = 1, #data do
insert(self.raw, data[i])
end
else
log.error("comms.capi_packet.make(): data not table")
end
end
-- decode a coordinator API packet from a SCADA frame
---@param frame scada_packet
---@return boolean success
function public.decode(frame)
if frame then
self.frame = frame
if frame.protocol() == PROTOCOL.COORD_API then
local ok = frame.length() >= 1
if ok then
local data = frame.data()
public.make(data[1], { table.unpack(data, 2, #data) })
ok = _capi_type_valid()
end
return ok
else
log.debug("attempted COORD_API parse of incorrect protocol " .. frame.protocol(), true)
return false
end
else
log.debug("nil frame encountered", true)
return false
end
end
-- get raw to send
---@nodiscard
function public.raw_sendable() return self.raw end
-- get this packet as a frame with an immutable relation to this object
---@nodiscard
function public.get()
---@class capi_frame
local frame = {
scada_frame = self.frame,
type = self.type,
length = self.length,
data = self.data
}
return frame
end
return public
end
return comms return comms

View File

@ -16,7 +16,7 @@ local logger = {
path = "/log.txt", path = "/log.txt",
mode = MODE.APPEND, mode = MODE.APPEND,
debug = false, debug = false,
file = nil, file = nil, ---@type table|nil
dmesg_out = nil, dmesg_out = nil,
dmesg_restore_coord = { 1, 1 }, dmesg_restore_coord = { 1, 1 },
dmesg_scroll_count = 0 dmesg_scroll_count = 0

View File

@ -375,6 +375,7 @@ types.MODBUS_FCODE = {
-- MODBUS exception codes -- MODBUS exception codes
---@enum MODBUS_EXCODE ---@enum MODBUS_EXCODE
types.MODBUS_EXCODE = { types.MODBUS_EXCODE = {
OK = 0x00,
ILLEGAL_FUNCTION = 0x01, ILLEGAL_FUNCTION = 0x01,
ILLEGAL_DATA_ADDR = 0x02, ILLEGAL_DATA_ADDR = 0x02,
ILLEGAL_DATA_VALUE = 0x03, ILLEGAL_DATA_VALUE = 0x03,

View File

@ -8,7 +8,7 @@ local cc_strings = require("cc.strings")
local util = {} local util = {}
-- scada-common version -- scada-common version
util.version = "1.0.2" util.version = "1.1.1"
-- ENVIRONMENT CONSTANTS -- -- ENVIRONMENT CONSTANTS --
@ -76,25 +76,12 @@ function util.strval(val)
end end
end end
-- repeat a string n times
---@nodiscard
---@param str string
---@param n integer
---@return string
function util.strrep(str, n)
local repeated = ""
for _ = 1, n do repeated = repeated .. str end
return repeated
end
-- repeat a space n times -- repeat a space n times
---@nodiscard ---@nodiscard
---@param n integer ---@param n integer
---@return string ---@return string
function util.spaces(n) function util.spaces(n)
return util.strrep(" ", n) return string.rep(" ", n)
end end
-- pad text to a minimum width -- pad text to a minimum width

View File

@ -4,6 +4,8 @@
local databus = require("supervisor.databus") local databus = require("supervisor.databus")
local style = require("supervisor.panel.style")
local core = require("graphics.core") local core = require("graphics.core")
local Div = require("graphics.elements.div") local Div = require("graphics.elements.div")
@ -15,28 +17,31 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local black_lg = style.black_lg
local lg_white = style.lg_white
-- create a pocket diagnostics list entry -- create a pocket diagnostics list entry
---@param parent graphics_element parent ---@param parent graphics_element parent
---@param id integer PDG session ID ---@param id integer PDG session ID
local function init(parent, id) local function init(parent, id)
-- root div -- root div
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true} local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=cpair(colors.black,colors.white)} local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
local ps_prefix = "pdg_" .. id .. "_" local ps_prefix = "pdg_" .. id .. "_"
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=black_lg,nav_active=cpair(colors.gray,colors.black)}
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value) pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value)
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1} TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=cpair(colors.lightGray,colors.white)} local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=lg_white}
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value) pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1} TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=cpair(colors.lightGray,colors.white)} local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=cpair(colors.lightGray,colors.white)} TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update) pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor) pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)

View File

@ -4,6 +4,8 @@
local databus = require("supervisor.databus") local databus = require("supervisor.databus")
local style = require("supervisor.panel.style")
local core = require("graphics.core") local core = require("graphics.core")
local Div = require("graphics.elements.div") local Div = require("graphics.elements.div")
@ -15,32 +17,35 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local black_lg = style.black_lg
local lg_white = style.lg_white
-- create an RTU list entry -- create an RTU list entry
---@param parent graphics_element parent ---@param parent graphics_element parent
---@param id integer RTU session ID ---@param id integer RTU session ID
local function init(parent, id) local function init(parent, id)
-- root div -- root div
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true} local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=cpair(colors.black,colors.white)} local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
local ps_prefix = "rtu_" .. id .. "_" local ps_prefix = "rtu_" .. id .. "_"
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray),nav_active=cpair(colors.gray,colors.black)} local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=black_lg,nav_active=cpair(colors.gray,colors.black)}
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value) rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value)
TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1} TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1}
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=cpair(colors.gray,colors.white)} local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.gray_white}
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value) unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
TextBox{parent=entry,x=21,y=2,text="FW:",width=3,height=1} TextBox{parent=entry,x=21,y=2,text="FW:",width=3,height=1}
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=cpair(colors.lightGray,colors.white)} local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value) rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4,height=1} TextBox{parent=entry,x=36,y=2,text="RTT:",width=4,height=1}
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=cpair(colors.lightGray,colors.white)} local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=cpair(colors.lightGray,colors.white)} TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update) rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor) rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)

View File

@ -29,6 +29,14 @@ local TEXT_ALIGN = core.TEXT_ALIGN
local cpair = core.cpair local cpair = core.cpair
local bw_fg_bg = style.bw_fg_bg
local black_lg = style.black_lg
local lg_white = style.lg_white
local gry_wht = style.gray_white
local ind_grn = style.ind_grn
-- create new front panel view -- create new front panel view
---@param panel graphics_element main displaybox ---@param panel graphics_element main displaybox
local function init(panel) local function init(panel)
@ -45,26 +53,26 @@ local function init(panel)
local system = Div{parent=main_page,width=14,height=17,x=2,y=2} local system = Div{parent=main_page,width=14,height=17,x=2,y=2}
local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)} local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=cpair(colors.green,colors.green_off)} local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
on.update(true) on.update(true)
system.line_break() system.line_break()
heartbeat.register(databus.ps, "heartbeat", heartbeat.update) heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)} local modem = LED{parent=system,label="MODEM",colors=ind_grn}
system.line_break() system.line_break()
modem.register(databus.ps, "has_modem", modem.update) modem.register(databus.ps, "has_modem", modem.update)
---@diagnostic disable-next-line: undefined-field ---@diagnostic disable-next-line: undefined-field
local comp_id = util.sprintf("(%d)", os.getComputerID()) local comp_id = util.sprintf("(%d)", os.getComputerID())
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)} TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp_label}
-- --
-- about footer -- about footer
-- --
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=cpair(colors.lightGray,colors.ivory)} local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp_label}
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1} local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
@ -82,25 +90,25 @@ local function init(panel)
for i = 1, config.NUM_REACTORS do for i = 1, config.NUM_REACTORS do
local ps_prefix = "plc_" .. i .. "_" local ps_prefix = "plc_" .. i .. "_"
local plc_entry = Div{parent=plc_list,height=3,fg_bg=cpair(colors.black,colors.white)} local plc_entry = Div{parent=plc_list,height=3,fg_bg=bw_fg_bg}
TextBox{parent=plc_entry,x=1,y=1,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=plc_entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=TEXT_ALIGN.CENTER,width=8,height=1,fg_bg=black_lg}
TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=cpair(colors.black,colors.lightGray)} TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=cpair(colors.green,colors.green_off)} local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=ind_grn}
conn.register(databus.ps, ps_prefix .. "conn", conn.update) conn.register(databus.ps, ps_prefix .. "conn", conn.update)
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=gry_wht}
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value) plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1} TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1}
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=cpair(colors.lightGray,colors.white)} local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value) plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4,height=1} TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4,height=1}
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=cpair(colors.lightGray,colors.white)} local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=lg_white}
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,height=1,fg_bg=cpair(colors.lightGray,colors.white)} TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update) plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor) plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
@ -116,22 +124,22 @@ local function init(panel)
-- coordinator page -- coordinator page
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true} local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=cpair(colors.black,colors.white)} local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=bw_fg_bg}
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green,colors.green_off)} local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=ind_grn}
crd_conn.register(databus.ps, "crd_conn", crd_conn.update) crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=cpair(colors.gray,colors.white)} TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=gry_wht}
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=cpair(colors.gray,colors.white)} local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=gry_wht}
crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value) crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value)
TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1} TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1}
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=cpair(colors.lightGray,colors.white)} local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value) crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4,height=1} TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4,height=1}
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=cpair(colors.lightGray,colors.white)} local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,height=1,fg_bg=cpair(colors.lightGray,colors.white)} TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update) crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor) crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
@ -155,7 +163,7 @@ local function init(panel)
{ name = "PKT", color = cpair(colors.black, colors.ivory) }, { name = "PKT", color = cpair(colors.black, colors.ivory) },
} }
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=cpair(colors.black,colors.white)} TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=bw_fg_bg}
-- link RTU/PDG list management to PGI -- link RTU/PDG list management to PGI
pgi.link_elements(rtu_list, rtu_entry, pdg_list, pdg_entry) pgi.link_elements(rtu_list, rtu_entry, pdg_list, pdg_entry)

View File

@ -39,4 +39,16 @@ style.colors = {
{ c = colors.brown, hex = 0x672223 } -- RED OFF { c = colors.brown, hex = 0x672223 } -- RED OFF
} }
-- COMMON COLOR PAIRS --
style.text_fg_bg = cpair(colors.black, colors.ivory)
style.bw_fg_bg = cpair(colors.black, colors.white)
style.fp_label = cpair(colors.lightGray, colors.ivory)
style.black_lg = cpair(colors.black, colors.lightGray)
style.lg_white = cpair(colors.lightGray, colors.white)
style.gray_white = cpair(colors.gray, colors.white)
style.ind_grn = cpair(colors.green, colors.green_off)
return style return style

View File

@ -10,8 +10,8 @@ local svqtypes = require("supervisor.session.svqtypes")
local coordinator = {} local coordinator = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local SCADA_CRDN_TYPE = comms.SCADA_CRDN_TYPE local CRDN_TYPE = comms.CRDN_TYPE
local UNIT_COMMAND = comms.UNIT_COMMAND local UNIT_COMMAND = comms.UNIT_COMMAND
local FAC_COMMAND = comms.FAC_COMMAND local FAC_COMMAND = comms.FAC_COMMAND
@ -94,7 +94,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
end end
-- send a CRDN packet -- send a CRDN packet
---@param msg_type SCADA_CRDN_TYPE ---@param msg_type CRDN_TYPE
---@param msg table ---@param msg table
local function _send(msg_type, msg) local function _send(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -108,7 +108,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -130,12 +130,12 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
unit_builds[unit.get_id()] = unit.get_build() unit_builds[unit.get_id()] = unit.get_build()
end end
_send(SCADA_CRDN_TYPE.INITIAL_BUILDS, { facility.get_build(), unit_builds }) _send(CRDN_TYPE.INITIAL_BUILDS, { facility.get_build(), unit_builds })
end end
-- send facility builds -- send facility builds
local function _send_fac_builds() local function _send_fac_builds()
_send(SCADA_CRDN_TYPE.FAC_BUILDS, { facility.get_build() }) _send(CRDN_TYPE.FAC_BUILDS, { facility.get_build() })
end end
-- send unit builds -- send unit builds
@ -147,7 +147,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
builds[unit.get_id()] = unit.get_build() builds[unit.get_id()] = unit.get_build()
end end
_send(SCADA_CRDN_TYPE.UNIT_BUILDS, { builds }) _send(CRDN_TYPE.UNIT_BUILDS, { builds })
end end
-- send facility status -- send facility status
@ -158,7 +158,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
facility.get_alarm_tones() facility.get_alarm_tones()
} }
_send(SCADA_CRDN_TYPE.FAC_STATUS, status) _send(CRDN_TYPE.FAC_STATUS, status)
end end
-- send unit statuses -- send unit statuses
@ -178,7 +178,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
} }
end end
_send(SCADA_CRDN_TYPE.UNIT_STATUSES, status) _send(CRDN_TYPE.UNIT_STATUSES, status)
end end
-- handle a packet -- handle a packet
@ -200,7 +200,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
-- process packet -- process packet
if pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then if pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
if pkt.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if pkt.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive reply -- keep alive reply
if pkt.length == 2 then if pkt.length == 2 then
local srv_start = pkt.data[1] local srv_start = pkt.data[1]
@ -219,7 +219,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_header .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == SCADA_MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
_close() _close()
else else
@ -227,22 +227,22 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
end end
elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_CRDN then elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_CRDN then
---@cast pkt crdn_frame ---@cast pkt crdn_frame
if pkt.type == SCADA_CRDN_TYPE.INITIAL_BUILDS then if pkt.type == CRDN_TYPE.INITIAL_BUILDS then
-- acknowledgement to coordinator receiving builds -- acknowledgement to coordinator receiving builds
self.acks.builds = true self.acks.builds = true
elseif pkt.type == SCADA_CRDN_TYPE.FAC_BUILDS then elseif pkt.type == CRDN_TYPE.FAC_BUILDS then
-- acknowledgement to coordinator receiving builds -- acknowledgement to coordinator receiving builds
self.acks.fac_builds = true self.acks.fac_builds = true
elseif pkt.type == SCADA_CRDN_TYPE.FAC_CMD then elseif pkt.type == CRDN_TYPE.FAC_CMD then
if pkt.length >= 1 then if pkt.length >= 1 then
local cmd = pkt.data[1] local cmd = pkt.data[1]
if cmd == FAC_COMMAND.SCRAM_ALL then if cmd == FAC_COMMAND.SCRAM_ALL then
facility.scram_all() facility.scram_all()
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, true }) _send(CRDN_TYPE.FAC_CMD, { cmd, true })
elseif cmd == FAC_COMMAND.STOP then elseif cmd == FAC_COMMAND.STOP then
facility.auto_stop() facility.auto_stop()
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, true }) _send(CRDN_TYPE.FAC_CMD, { cmd, true })
elseif cmd == FAC_COMMAND.START then elseif cmd == FAC_COMMAND.START then
if pkt.length == 6 then if pkt.length == 6 then
---@type coord_auto_config ---@type coord_auto_config
@ -254,22 +254,22 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
limits = pkt.data[6] limits = pkt.data[6]
} }
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, table.unpack(facility.auto_start(config)) }) _send(CRDN_TYPE.FAC_CMD, { cmd, table.unpack(facility.auto_start(config)) })
else else
log.debug(log_header .. "CRDN auto start (with configuration) packet length mismatch") log.debug(log_header .. "CRDN auto start (with configuration) packet length mismatch")
end end
elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then
facility.ack_all() facility.ack_all()
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, true }) _send(CRDN_TYPE.FAC_CMD, { cmd, true })
elseif cmd == FAC_COMMAND.SET_WASTE_MODE then elseif cmd == FAC_COMMAND.SET_WASTE_MODE then
if pkt.length == 2 then if pkt.length == 2 then
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, facility.set_waste_product(pkt.data[2]) }) _send(CRDN_TYPE.FAC_CMD, { cmd, facility.set_waste_product(pkt.data[2]) })
else else
log.debug(log_header .. "CRDN set waste mode packet length mismatch") log.debug(log_header .. "CRDN set waste mode packet length mismatch")
end end
elseif cmd == FAC_COMMAND.SET_PU_FB then elseif cmd == FAC_COMMAND.SET_PU_FB then
if pkt.length == 2 then if pkt.length == 2 then
_send(SCADA_CRDN_TYPE.FAC_CMD, { cmd, facility.set_pu_fallback(pkt.data[2]) }) _send(CRDN_TYPE.FAC_CMD, { cmd, facility.set_pu_fallback(pkt.data[2]) })
else else
log.debug(log_header .. "CRDN set pu fallback packet length mismatch") log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
end end
@ -279,10 +279,10 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
else else
log.debug(log_header .. "CRDN facility command packet length mismatch") log.debug(log_header .. "CRDN facility command packet length mismatch")
end end
elseif pkt.type == SCADA_CRDN_TYPE.UNIT_BUILDS then elseif pkt.type == CRDN_TYPE.UNIT_BUILDS then
-- acknowledgement to coordinator receiving builds -- acknowledgement to coordinator receiving builds
self.acks.unit_builds = true self.acks.unit_builds = true
elseif pkt.type == SCADA_CRDN_TYPE.UNIT_CMD then elseif pkt.type == CRDN_TYPE.UNIT_CMD then
if pkt.length >= 2 then if pkt.length >= 2 then
-- get command and unit id -- get command and unit id
local cmd = pkt.data[1] local cmd = pkt.data[1]
@ -315,7 +315,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
end end
elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then
unit.ack_all() unit.ack_all()
_send(SCADA_CRDN_TYPE.UNIT_CMD, { cmd, uid, true }) _send(CRDN_TYPE.UNIT_CMD, { cmd, uid, true })
elseif cmd == UNIT_COMMAND.ACK_ALARM then elseif cmd == UNIT_COMMAND.ACK_ALARM then
if pkt.length == 3 then if pkt.length == 3 then
unit.ack_alarm(pkt.data[3]) unit.ack_alarm(pkt.data[3])
@ -331,7 +331,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
elseif cmd == UNIT_COMMAND.SET_GROUP then elseif cmd == UNIT_COMMAND.SET_GROUP then
if (pkt.length == 3) and (type(pkt.data[3]) == "number") and (pkt.data[3] >= 0) and (pkt.data[3] <= 4) then if (pkt.length == 3) and (type(pkt.data[3]) == "number") and (pkt.data[3] >= 0) and (pkt.data[3] <= 4) then
facility.set_group(unit.get_id(), pkt.data[3]) facility.set_group(unit.get_id(), pkt.data[3])
_send(SCADA_CRDN_TYPE.UNIT_CMD, { cmd, uid, pkt.data[3] }) _send(CRDN_TYPE.UNIT_CMD, { cmd, uid, pkt.data[3] })
else else
log.debug(log_header .. "CRDN unit command set group missing group id") log.debug(log_header .. "CRDN unit command set group missing group id")
end end
@ -374,7 +374,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
-- close the connection -- close the connection
function public.close() function public.close()
_close() _close()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
println("connection to coordinator " .. id .. " closed by server") println("connection to coordinator " .. id .. " closed by server")
log.info(log_header .. "session closed by server") log.info(log_header .. "session closed by server")
end end
@ -406,7 +406,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
if cmd.key == CRD_S_DATA.CMD_ACK then if cmd.key == CRD_S_DATA.CMD_ACK then
local ack = cmd.val ---@type coord_ack local ack = cmd.val ---@type coord_ack
_send(SCADA_CRDN_TYPE.UNIT_CMD, { ack.cmd, ack.unit, ack.ack }) _send(CRDN_TYPE.UNIT_CMD, { ack.cmd, ack.unit, ack.ack })
elseif cmd.key == CRD_S_DATA.RESEND_PLC_BUILD then elseif cmd.key == CRD_S_DATA.RESEND_PLC_BUILD then
-- re-send PLC build -- re-send PLC build
-- retry logic will be kept as-is, so as long as no retry is needed, this will be a small update -- retry logic will be kept as-is, so as long as no retry is needed, this will be a small update
@ -419,7 +419,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
local unit = self.units[unit_id] ---@type reactor_unit local unit = self.units[unit_id] ---@type reactor_unit
builds[unit_id] = unit.get_build(-1) builds[unit_id] = unit.get_build(-1)
_send(SCADA_CRDN_TYPE.UNIT_BUILDS, { builds }) _send(CRDN_TYPE.UNIT_BUILDS, { builds })
elseif cmd.key == CRD_S_DATA.RESEND_RTU_BUILD then elseif cmd.key == CRD_S_DATA.RESEND_RTU_BUILD then
local unit_id = cmd.val.unit local unit_id = cmd.val.unit
if unit_id > 0 then if unit_id > 0 then
@ -433,14 +433,14 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
local unit = self.units[unit_id] ---@type reactor_unit local unit = self.units[unit_id] ---@type reactor_unit
builds[unit_id] = unit.get_build(cmd.val.type) builds[unit_id] = unit.get_build(cmd.val.type)
_send(SCADA_CRDN_TYPE.UNIT_BUILDS, { builds }) _send(CRDN_TYPE.UNIT_BUILDS, { builds })
else else
-- re-send facility RTU builds -- re-send facility RTU builds
-- retry logic will be kept as-is, so as long as no retry is needed, this will be a small update -- retry logic will be kept as-is, so as long as no retry is needed, this will be a small update
self.retry_times.f_builds_packet = util.time() + PARTIAL_RETRY_PERIOD self.retry_times.f_builds_packet = util.time() + PARTIAL_RETRY_PERIOD
self.acks.fac_builds = false self.acks.fac_builds = false
_send(SCADA_CRDN_TYPE.FAC_BUILDS, { facility.get_build(cmd.val.type) }) _send(CRDN_TYPE.FAC_BUILDS, { facility.get_build(cmd.val.type) })
end end
else else
log.error(log_header .. "unsupported data command received in in_queue (this is a bug)", true) log.error(log_header .. "unsupported data command received in in_queue (this is a bug)", true)
@ -474,7 +474,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
periodics.keep_alive = periodics.keep_alive + elapsed periodics.keep_alive = periodics.keep_alive + elapsed
if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { util.time() })
periodics.keep_alive = 0 periodics.keep_alive = 0
end end

View File

@ -12,7 +12,7 @@ local plc = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local RPLC_TYPE = comms.RPLC_TYPE local RPLC_TYPE = comms.RPLC_TYPE
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local PLC_AUTO_ACK = comms.PLC_AUTO_ACK local PLC_AUTO_ACK = comms.PLC_AUTO_ACK
local UNIT_COMMAND = comms.UNIT_COMMAND local UNIT_COMMAND = comms.UNIT_COMMAND
@ -258,7 +258,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -482,7 +482,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
end end
elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
if pkt.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if pkt.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive reply -- keep alive reply
if pkt.length == 2 then if pkt.length == 2 then
local srv_start = pkt.data[1] local srv_start = pkt.data[1]
@ -501,7 +501,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_header .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == SCADA_MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
_close() _close()
else else
@ -595,7 +595,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
-- close the connection -- close the connection
function public.close() function public.close()
_close() _close()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
println("connection to reactor " .. reactor_id .. " PLC closed by server") println("connection to reactor " .. reactor_id .. " PLC closed by server")
log.info(log_header .. "session closed by server") log.info(log_header .. "session closed by server")
end end
@ -726,7 +726,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
periodics.keep_alive = periodics.keep_alive + elapsed periodics.keep_alive = periodics.keep_alive + elapsed
if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { util.time() })
periodics.keep_alive = 0 periodics.keep_alive = 0
end end

View File

@ -7,7 +7,7 @@ local databus = require("supervisor.databus")
local pocket = {} local pocket = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
-- retry time constants in ms -- retry time constants in ms
-- local INITIAL_WAIT = 1500 -- local INITIAL_WAIT = 1500
@ -76,7 +76,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -108,7 +108,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
-- process packet -- process packet
if pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then if pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
if pkt.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if pkt.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive reply -- keep alive reply
if pkt.length == 2 then if pkt.length == 2 then
local srv_start = pkt.data[1] local srv_start = pkt.data[1]
@ -127,13 +127,13 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_header .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == SCADA_MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
_close() _close()
elseif pkt.type == SCADA_MGMT_TYPE.DIAG_TONE_GET then elseif pkt.type == MGMT_TYPE.DIAG_TONE_GET then
-- get the state of alarm tones -- get the state of alarm tones
_send_mgmt(SCADA_MGMT_TYPE.DIAG_TONE_GET, facility.get_alarm_tones()) _send_mgmt(MGMT_TYPE.DIAG_TONE_GET, facility.get_alarm_tones())
elseif pkt.type == SCADA_MGMT_TYPE.DIAG_TONE_SET then elseif pkt.type == MGMT_TYPE.DIAG_TONE_SET then
local valid = false local valid = false
-- attempt to set a tone state -- attempt to set a tone state
@ -144,7 +144,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
-- try to set tone states, then send back if testing is allowed -- try to set tone states, then send back if testing is allowed
local allow_testing, test_tone_states = facility.diag_set_test_tone(pkt.data[1], pkt.data[2]) local allow_testing, test_tone_states = facility.diag_set_test_tone(pkt.data[1], pkt.data[2])
_send_mgmt(SCADA_MGMT_TYPE.DIAG_TONE_SET, { allow_testing, test_tone_states }) _send_mgmt(MGMT_TYPE.DIAG_TONE_SET, { allow_testing, test_tone_states })
else else
log.debug(log_header .. "SCADA diag tone set packet data type mismatch") log.debug(log_header .. "SCADA diag tone set packet data type mismatch")
end end
@ -155,8 +155,8 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
log.debug(log_header .. "DIAG_TONE_SET is blocked without HMAC for security") log.debug(log_header .. "DIAG_TONE_SET is blocked without HMAC for security")
end end
if not valid then _send_mgmt(SCADA_MGMT_TYPE.DIAG_TONE_SET, { false }) end if not valid then _send_mgmt(MGMT_TYPE.DIAG_TONE_SET, { false }) end
elseif pkt.type == SCADA_MGMT_TYPE.DIAG_ALARM_SET then elseif pkt.type == MGMT_TYPE.DIAG_ALARM_SET then
local valid = false local valid = false
-- attempt to set an alarm state -- attempt to set an alarm state
@ -167,7 +167,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
-- try to set alarm states, then send back if testing is allowed -- try to set alarm states, then send back if testing is allowed
local allow_testing, test_alarm_states = facility.diag_set_test_alarm(pkt.data[1], pkt.data[2]) local allow_testing, test_alarm_states = facility.diag_set_test_alarm(pkt.data[1], pkt.data[2])
_send_mgmt(SCADA_MGMT_TYPE.DIAG_ALARM_SET, { allow_testing, test_alarm_states }) _send_mgmt(MGMT_TYPE.DIAG_ALARM_SET, { allow_testing, test_alarm_states })
else else
log.debug(log_header .. "SCADA diag alarm set packet data type mismatch") log.debug(log_header .. "SCADA diag alarm set packet data type mismatch")
end end
@ -178,7 +178,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
log.debug(log_header .. "DIAG_ALARM_SET is blocked without HMAC for security") log.debug(log_header .. "DIAG_ALARM_SET is blocked without HMAC for security")
end end
if not valid then _send_mgmt(SCADA_MGMT_TYPE.DIAG_ALARM_SET, { false }) end if not valid then _send_mgmt(MGMT_TYPE.DIAG_ALARM_SET, { false }) end
else else
log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type) log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type)
end end
@ -204,7 +204,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
-- close the connection -- close the connection
function public.close() function public.close()
_close() _close()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
println("connection to pocket diag session " .. id .. " closed by server") println("connection to pocket diag session " .. id .. " closed by server")
log.info(log_header .. "session closed by server") log.info(log_header .. "session closed by server")
end end
@ -261,7 +261,7 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
periodics.keep_alive = periodics.keep_alive + elapsed periodics.keep_alive = periodics.keep_alive + elapsed
if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { util.time() })
periodics.keep_alive = 0 periodics.keep_alive = 0
end end

View File

@ -22,7 +22,7 @@ local svrs_turbinev = require("supervisor.session.rtu.turbinev")
local rtu = {} local rtu = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
local PERIODICS = { local PERIODICS = {
@ -223,7 +223,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
end end
-- send a SCADA management packet -- send a SCADA management packet
---@param msg_type SCADA_MGMT_TYPE ---@param msg_type MGMT_TYPE
---@param msg table ---@param msg table
local function _send_mgmt(msg_type, msg) local function _send_mgmt(msg_type, msg)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
@ -262,7 +262,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
-- handle management packet -- handle management packet
if pkt.type == SCADA_MGMT_TYPE.KEEP_ALIVE then if pkt.type == MGMT_TYPE.KEEP_ALIVE then
-- keep alive reply -- keep alive reply
if pkt.length == 2 then if pkt.length == 2 then
local srv_start = pkt.data[1] local srv_start = pkt.data[1]
@ -281,17 +281,17 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_header .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == SCADA_MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
_close() _close()
elseif pkt.type == SCADA_MGMT_TYPE.RTU_ADVERT then elseif pkt.type == MGMT_TYPE.RTU_ADVERT then
-- RTU unit advertisement -- RTU unit advertisement
log.debug(log_header .. "received updated advertisement") log.debug(log_header .. "received updated advertisement")
self.advert = pkt.data self.advert = pkt.data
-- handle advertisement; this will re-create all unit sub-sessions -- handle advertisement; this will re-create all unit sub-sessions
_handle_advertisement() _handle_advertisement()
elseif pkt.type == SCADA_MGMT_TYPE.RTU_DEV_REMOUNT then elseif pkt.type == MGMT_TYPE.RTU_DEV_REMOUNT then
if pkt.length == 1 then if pkt.length == 1 then
local unit_id = pkt.data[1] local unit_id = pkt.data[1]
if self.units[unit_id] ~= nil then if self.units[unit_id] ~= nil then
@ -322,7 +322,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
-- close the connection -- close the connection
function public.close() function public.close()
_close() _close()
_send_mgmt(SCADA_MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
println(log_header .. "connection to RTU closed by server") println(log_header .. "connection to RTU closed by server")
log.info(log_header .. "session closed by server") log.info(log_header .. "session closed by server")
end end
@ -387,7 +387,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
periodics.keep_alive = periodics.keep_alive + elapsed periodics.keep_alive = periodics.keep_alive + elapsed
if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then if periodics.keep_alive >= PERIODICS.KEEP_ALIVE then
_send_mgmt(SCADA_MGMT_TYPE.KEEP_ALIVE, { util.time() }) _send_mgmt(MGMT_TYPE.KEEP_ALIVE, { util.time() })
periodics.keep_alive = 0 periodics.keep_alive = 0
end end
@ -395,7 +395,7 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
periodics.alarm_tones = periodics.alarm_tones + elapsed periodics.alarm_tones = periodics.alarm_tones + elapsed
if periodics.alarm_tones >= PERIODICS.ALARM_TONES then if periodics.alarm_tones >= PERIODICS.ALARM_TONES then
_send_mgmt(SCADA_MGMT_TYPE.RTU_TONE_ALARM, { facility.get_alarm_tones() }) _send_mgmt(MGMT_TYPE.RTU_TONE_ALARM, { facility.get_alarm_tones() })
periodics.alarm_tones = 0 periodics.alarm_tones = 0
end end

View File

@ -55,7 +55,7 @@ function txnctrl.new()
-- mark a transaction as resolved to get its transaction type -- mark a transaction as resolved to get its transaction type
---@nodiscard ---@nodiscard
---@param txn_id integer ---@param txn_id integer
---@return integer txn_type ---@return integer|nil txn_type
function public.resolve(txn_id) function public.resolve(txn_id)
local txn_type = nil local txn_type = nil

View File

@ -73,7 +73,13 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
if m_pkt.scada_frame.protocol() == PROTOCOL.MODBUS_TCP then if m_pkt.scada_frame.protocol() == PROTOCOL.MODBUS_TCP then
if m_pkt.unit_id == unit_id then if m_pkt.unit_id == unit_id then
local txn_type = self.transaction_controller.resolve(m_pkt.txn_id) local txn_type = self.transaction_controller.resolve(m_pkt.txn_id)
local txn_tag = " (" .. util.strval(txn_tags[txn_type]) .. ")" local txn_tag = util.c(" (", txn_tags[txn_type], ")")
if txn_type == nil then
-- couldn't find this transaction
log.debug(log_tag .. "MODBUS: expired or spurious transaction reply (txn_id " .. m_pkt.txn_id .. ")")
return false, m_pkt.txn_id
end
if bit.band(m_pkt.func_code, MODBUS_FCODE.ERROR_FLAG) ~= 0 then if bit.band(m_pkt.func_code, MODBUS_FCODE.ERROR_FLAG) ~= 0 then
-- transaction incomplete or failed -- transaction incomplete or failed

View File

@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.0.2" local SUPERVISOR_VERSION = "v1.0.4"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts

View File

@ -11,7 +11,7 @@ local supervisor = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local DEVICE_TYPE = comms.DEVICE_TYPE local DEVICE_TYPE = comms.DEVICE_TYPE
local ESTABLISH_ACK = comms.ESTABLISH_ACK local ESTABLISH_ACK = comms.ESTABLISH_ACK
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
-- supervisory controller communications -- supervisory controller communications
---@nodiscard ---@nodiscard
@ -58,7 +58,7 @@ function supervisor.comms(_version, nic, fp_ok)
local s_pkt = comms.scada_packet() local s_pkt = comms.scada_packet()
local m_pkt = comms.mgmt_packet() local m_pkt = comms.mgmt_packet()
m_pkt.make(SCADA_MGMT_TYPE.ESTABLISH, { ack, data }) m_pkt.make(MGMT_TYPE.ESTABLISH, { ack, data })
s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable()) s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
nic.transmit(packet.remote_channel(), svr_channel, s_pkt) nic.transmit(packet.remote_channel(), svr_channel, s_pkt)
@ -147,7 +147,7 @@ function supervisor.comms(_version, nic, fp_ok)
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- establish a new session -- establish a new session
local last_ack = self.last_est_acks[src_addr] local last_ack = self.last_est_acks[src_addr]
@ -221,7 +221,7 @@ function supervisor.comms(_version, nic, fp_ok)
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- establish a new session -- establish a new session
local last_ack = self.last_est_acks[src_addr] local last_ack = self.last_est_acks[src_addr]
@ -275,7 +275,7 @@ function supervisor.comms(_version, nic, fp_ok)
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- establish a new session -- establish a new session
local last_ack = self.last_est_acks[src_addr] local last_ack = self.last_est_acks[src_addr]
@ -291,7 +291,7 @@ function supervisor.comms(_version, nic, fp_ok)
end end
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION) _send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
elseif dev_type == DEVICE_TYPE.CRDN then elseif dev_type == DEVICE_TYPE.CRD then
-- this is an attempt to establish a new coordinator session -- this is an attempt to establish a new coordinator session
local s_id = svsessions.establish_crd_session(src_addr, firmware_v) local s_id = svsessions.establish_crd_session(src_addr, firmware_v)
@ -342,7 +342,7 @@ function supervisor.comms(_version, nic, fp_ok)
if session ~= nil then if session ~= nil then
-- pass the packet onto the session handler -- pass the packet onto the session handler
session.in_queue.push_packet(packet) session.in_queue.push_packet(packet)
elseif packet.type == SCADA_MGMT_TYPE.ESTABLISH then elseif packet.type == MGMT_TYPE.ESTABLISH then
-- establish a new session -- establish a new session
local last_ack = self.last_est_acks[src_addr] local last_ack = self.last_est_acks[src_addr]

View File

@ -358,6 +358,7 @@ end
---@param self _unit_self unit instance ---@param self _unit_self unit instance
---@param tripped boolean if the alarm condition is still active ---@param tripped boolean if the alarm condition is still active
---@param alarm alarm_def alarm table ---@param alarm alarm_def alarm table
---@return boolean new_trip if the alarm just changed to being tripped
local function _update_alarm_state(self, tripped, alarm) local function _update_alarm_state(self, tripped, alarm)
local AISTATE = self.types.AISTATE local AISTATE = self.types.AISTATE
local int_state = alarm.state local int_state = alarm.state
@ -439,7 +440,8 @@ local function _update_alarm_state(self, tripped, alarm)
if alarm.state ~= int_state then if alarm.state ~= int_state then
local change_str = util.c(AISTATE_NAMES[int_state], " -> ", AISTATE_NAMES[alarm.state]) local change_str = util.c(AISTATE_NAMES[int_state], " -> ", AISTATE_NAMES[alarm.state])
log.debug(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): ", change_str)) log.debug(util.c("UNIT ", self.r_id, " ALARM ", alarm.id, " (", types.ALARM_NAMES[alarm.id], "): ", change_str))
end return alarm.state == AISTATE.TRIPPED
else return false end
end end
-- evaluate alarm conditions -- evaluate alarm conditions
@ -469,11 +471,17 @@ function logic.update_alarms(self)
-- Reactor Damage -- Reactor Damage
local rps_dmg_90 = plc_cache.rps_status.high_dmg and not self.last_rps_trips.high_dmg local rps_dmg_90 = plc_cache.rps_status.high_dmg and not self.last_rps_trips.high_dmg
_update_alarm_state(self, (plc_cache.damage > 0) or rps_dmg_90, self.alarms.ReactorDamage) if _update_alarm_state(self, (plc_cache.damage > 0) or rps_dmg_90, self.alarms.ReactorDamage) then
log.debug(util.c(">> Trip Detail Report for ", types.ALARM_NAMES[self.alarms.ReactorDamage.id]," <<"))
log.debug(util.c("| plc_cache.damage[", plc_cache.damage, "] rps_dmg_90[", rps_dmg_90, "]"))
end
-- Over-Temperature -- Over-Temperature
local rps_high_temp = plc_cache.rps_status.high_temp and not self.last_rps_trips.high_temp local rps_high_temp = plc_cache.rps_status.high_temp and not self.last_rps_trips.high_temp
_update_alarm_state(self, (plc_cache.temp >= 1200) or rps_high_temp, self.alarms.ReactorOverTemp) if _update_alarm_state(self, (plc_cache.temp >= 1200) or rps_high_temp, self.alarms.ReactorOverTemp) then
log.debug(util.c(">> Trip Detail Report for ", types.ALARM_NAMES[self.alarms.ReactorOverTemp.id]," <<"))
log.debug(util.c("| plc_cache.temp[", plc_cache.temp, "] rps_high_temp[", rps_high_temp, "]"))
end
-- High Temperature -- High Temperature
_update_alarm_state(self, plc_cache.temp >= ALARM_LIMS.HIGH_TEMP, self.alarms.ReactorHighTemp) _update_alarm_state(self, plc_cache.temp >= ALARM_LIMS.HIGH_TEMP, self.alarms.ReactorHighTemp)
@ -483,7 +491,10 @@ function logic.update_alarms(self)
-- High Waste -- High Waste
local rps_high_waste = plc_cache.rps_status.ex_waste and not self.last_rps_trips.ex_waste local rps_high_waste = plc_cache.rps_status.ex_waste and not self.last_rps_trips.ex_waste
_update_alarm_state(self, (plc_cache.waste > ALARM_LIMS.HIGH_WASTE) or rps_high_waste, self.alarms.ReactorHighWaste) if _update_alarm_state(self, (plc_cache.waste > ALARM_LIMS.HIGH_WASTE) or rps_high_waste, self.alarms.ReactorHighWaste) then
log.debug(util.c(">> Trip Detail Report for ", types.ALARM_NAMES[self.alarms.ReactorHighWaste.id]," <<"))
log.debug(util.c("| plc_cache.waste[", plc_cache.waste, "] rps_high_waste[", rps_high_waste, "]"))
end
-- RPS Transient (excludes timeouts and manual trips) -- RPS Transient (excludes timeouts and manual trips)
local rps_alarm = false local rps_alarm = false
@ -514,7 +525,13 @@ function logic.update_alarms(self)
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
end end
_update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) if _update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) then
log.debug(util.c(">> Trip Detail Report for ", types.ALARM_NAMES[self.alarms.RCSTransient.id]," <<"))
log.debug(util.c("| any_low[", any_low, "] any_over[", any_over, "] gen_trip[", gen_trip, "]"))
log.debug(util.c("| RCPTrip[", annunc.RCPTrip, "] MaxWaterReturnFeed[", annunc.MaxWaterReturnFeed, "]"))
log.debug(util.c("| RCSFlowLow[", annunc.RCSFlowLow, "] BoilRateMismatch[", annunc.BoilRateMismatch,
"] CoolantFeedMismatch[", annunc.CoolantFeedMismatch, "] SteamFeedMismatch[", annunc.SteamFeedMismatch, "]"))
end
-- Turbine Trip -- Turbine Trip
local any_trip = false local any_trip = false
@ -522,9 +539,7 @@ function logic.update_alarms(self)
_update_alarm_state(self, any_trip, self.alarms.TurbineTrip) _update_alarm_state(self, any_trip, self.alarms.TurbineTrip)
-- update last trips table -- update last trips table
for key, val in pairs(plc_cache.rps_status) do for key, val in pairs(plc_cache.rps_status) do self.last_rps_trips[key] = val end
self.last_rps_trips[key] = val
end
end end
-- update the internal automatic safety control performed while in auto control mode -- update the internal automatic safety control performed while in auto control mode