mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
Merge branch 'devel' into 506-single-file-off-line-installer
This commit is contained in:
commit
d424cf74d3
@ -138,6 +138,7 @@ local function gen_tree(manifest)
|
|||||||
|
|
||||||
for i = 1, #list do
|
for i = 1, #list do
|
||||||
local split = {}
|
local split = {}
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
string.gsub(list[i], "([^/]+)", function(c) split[#split + 1] = c end)
|
string.gsub(list[i], "([^/]+)", function(c) split[#split + 1] = c end)
|
||||||
if #split == 1 then table.insert(tree, list[i])
|
if #split == 1 then table.insert(tree, list[i])
|
||||||
else table.insert(tree, _tree_add(tree, split)) end
|
else table.insert(tree, _tree_add(tree, split)) end
|
||||||
|
@ -232,8 +232,8 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
local self = {
|
local self = {
|
||||||
sv_linked = false,
|
sv_linked = false,
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
sv_seq_num = 0,
|
sv_seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
sv_r_seq_num = nil,
|
sv_r_seq_num = nil, ---@type nil|integer
|
||||||
sv_config_err = false,
|
sv_config_err = false,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||||
last_api_est_acks = {},
|
last_api_est_acks = {},
|
||||||
@ -492,7 +492,7 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
||||||
elseif dev_type == DEVICE_TYPE.PKT then
|
elseif dev_type == DEVICE_TYPE.PKT then
|
||||||
-- pocket linking request
|
-- pocket linking request
|
||||||
local id = apisessions.establish_session(src_addr, firmware_v)
|
local id = apisessions.establish_session(src_addr, packet.scada_frame.seq_num(), firmware_v)
|
||||||
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
||||||
|
|
||||||
local conf = iocontrol.get_db().facility.conf
|
local conf = iocontrol.get_db().facility.conf
|
||||||
@ -515,15 +515,15 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
elseif r_chan == config.SVR_Channel then
|
elseif r_chan == config.SVR_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.sv_r_seq_num == nil then
|
if self.sv_r_seq_num == nil then
|
||||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.sv_linked and ((self.sv_r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.sv_r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return false
|
return false
|
||||||
elseif self.sv_linked and src_addr ~= self.sv_addr then
|
elseif self.sv_linked and src_addr ~= self.sv_addr then
|
||||||
log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?")
|
log.debug("received packet from unknown computer " .. src_addr .. " while linked; channel in use by another system?")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
self.sv_r_seq_num = packet.scada_frame.seq_num()
|
self.sv_r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@ -706,7 +706,6 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
self.sv_addr = src_addr
|
self.sv_addr = src_addr
|
||||||
self.sv_linked = true
|
self.sv_linked = true
|
||||||
self.sv_r_seq_num = nil
|
|
||||||
self.sv_config_err = false
|
self.sv_config_err = false
|
||||||
|
|
||||||
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
|
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
|
||||||
|
@ -89,10 +89,11 @@ end
|
|||||||
|
|
||||||
-- establish a new API session
|
-- establish a new API session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer pocket computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string pocket version
|
||||||
---@return integer session_id
|
---@return integer session_id
|
||||||
function apisessions.establish_session(source_addr, version)
|
function apisessions.establish_session(source_addr, i_seq_num, version)
|
||||||
---@class pkt_session_struct
|
---@class pkt_session_struct
|
||||||
local pkt_s = {
|
local pkt_s = {
|
||||||
open = true,
|
open = true,
|
||||||
@ -105,7 +106,7 @@ function apisessions.establish_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_id
|
local id = self.next_id
|
||||||
|
|
||||||
pkt_s.instance = pocket.new_session(id, source_addr, pkt_s.in_queue, pkt_s.out_queue, self.config.API_Timeout)
|
pkt_s.instance = pocket.new_session(id, source_addr, i_seq_num, pkt_s.in_queue, pkt_s.out_queue, self.config.API_Timeout)
|
||||||
table.insert(self.sessions, pkt_s)
|
table.insert(self.sessions, pkt_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
|
@ -32,16 +32,17 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
|
||||||
local log_header = "pkt_session(" .. id .. "): "
|
local log_header = "pkt_session(" .. id .. "): "
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@ -104,13 +105,11 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
|||||||
---@param pkt mgmt_frame|crdn_frame
|
---@param pkt mgmt_frame|crdn_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
|
|||||||
local sounder = require("coordinator.sounder")
|
local sounder = require("coordinator.sounder")
|
||||||
local threads = require("coordinator.threads")
|
local threads = require("coordinator.threads")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v1.4.7"
|
local COORDINATOR_VERSION = "v1.5.1"
|
||||||
|
|
||||||
local CHUNK_LOAD_DELAY_S = 30.0
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
@ -151,8 +151,8 @@ local function main()
|
|||||||
|
|
||||||
-- core coordinator devices
|
-- core coordinator devices
|
||||||
crd_dev = {
|
crd_dev = {
|
||||||
speaker = ppm.get_device("speaker"),
|
modem = ppm.get_wireless_modem(),
|
||||||
modem = ppm.get_wireless_modem()
|
speaker = ppm.get_device("speaker")
|
||||||
},
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
|
@ -342,10 +342,8 @@ function iocontrol.report_link_state(state, sv_addr, api_addr)
|
|||||||
io.ps.publish("crd_conn_quality", 0)
|
io.ps.publish("crd_conn_quality", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
if state == LINK_STATE.LINKED then
|
if sv_addr then io.ps.publish("sv_addr", sv_addr) end
|
||||||
io.ps.publish("sv_addr", sv_addr)
|
if api_addr then io.ps.publish("api_addr", api_addr) end
|
||||||
io.ps.publish("api_addr", api_addr)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- determine supervisor connection quality (trip time)
|
-- determine supervisor connection quality (trip time)
|
||||||
@ -525,7 +523,10 @@ function iocontrol.record_unit_data(data)
|
|||||||
reactor_state = 6 -- SCRAM
|
reactor_state = 6 -- SCRAM
|
||||||
rps_status = 2
|
rps_status = 2
|
||||||
end
|
end
|
||||||
else rps_status = 4 end
|
else
|
||||||
|
rps_status = 4
|
||||||
|
reactor_state = 4
|
||||||
|
end
|
||||||
|
|
||||||
-- update reactor/control status
|
-- update reactor/control status
|
||||||
if unit.reactor_data.mek_status.status then
|
if unit.reactor_data.mek_status.status then
|
||||||
|
@ -78,15 +78,16 @@ end
|
|||||||
---@enum POCKET_APP_ID
|
---@enum POCKET_APP_ID
|
||||||
local APP_ID = {
|
local APP_ID = {
|
||||||
ROOT = 1,
|
ROOT = 1,
|
||||||
|
LOADER = 2,
|
||||||
-- main app pages
|
-- main app pages
|
||||||
UNITS = 2,
|
UNITS = 3,
|
||||||
GUIDE = 3,
|
GUIDE = 4,
|
||||||
ABOUT = 4,
|
ABOUT = 5,
|
||||||
-- diag app page
|
-- diag app page
|
||||||
ALARMS = 5,
|
ALARMS = 6,
|
||||||
-- other
|
-- other
|
||||||
DUMMY = 6,
|
DUMMY = 7,
|
||||||
NUM_APPS = 6
|
NUM_APPS = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
pocket.APP_ID = APP_ID
|
pocket.APP_ID = APP_ID
|
||||||
@ -98,9 +99,9 @@ pocket.APP_ID = APP_ID
|
|||||||
---@field switcher function|nil function to switch between children
|
---@field switcher function|nil function to switch between children
|
||||||
---@field tasks table tasks to run while viewing this page
|
---@field tasks table tasks to run while viewing this page
|
||||||
|
|
||||||
-- allocate the page navigation system
|
-- initialize the page navigation system
|
||||||
---@param render_queue mqueue
|
---@param smem pkt_shared_memory
|
||||||
function pocket.init_nav(render_queue)
|
function pocket.init_nav(smem)
|
||||||
local self = {
|
local self = {
|
||||||
pane = nil, ---@type graphics_element
|
pane = nil, ---@type graphics_element
|
||||||
sidebar = nil, ---@type graphics_element
|
sidebar = nil, ---@type graphics_element
|
||||||
@ -108,6 +109,7 @@ function pocket.init_nav(render_queue)
|
|||||||
containers = {},
|
containers = {},
|
||||||
help_map = {},
|
help_map = {},
|
||||||
help_return = nil,
|
help_return = nil,
|
||||||
|
loader_return = nil,
|
||||||
cur_app = APP_ID.ROOT
|
cur_app = APP_ID.ROOT
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,10 +145,13 @@ function pocket.init_nav(render_queue)
|
|||||||
app.load = function () app.loaded = true end
|
app.load = function () app.loaded = true end
|
||||||
app.unload = function () app.loaded = false end
|
app.unload = function () app.loaded = false end
|
||||||
|
|
||||||
-- check which connections this requires
|
-- check which connections this requires (for unload)
|
||||||
---@return boolean requires_sv, boolean requires_api
|
---@return boolean requires_sv, boolean requires_api
|
||||||
function app.check_requires() return require_sv or false, require_api or false end
|
function app.check_requires() return require_sv or false, require_api or false end
|
||||||
|
|
||||||
|
-- check if any connection is required (for load)
|
||||||
|
function app.requires_conn() return require_sv or require_api or false end
|
||||||
|
|
||||||
-- delayed set of the pane if it wasn't ready at the start
|
-- delayed set of the pane if it wasn't ready at the start
|
||||||
---@param root_pane graphics_element multipane
|
---@param root_pane graphics_element multipane
|
||||||
function app.set_root_pane(root_pane)
|
function app.set_root_pane(root_pane)
|
||||||
@ -254,7 +259,14 @@ function pocket.init_nav(render_queue)
|
|||||||
|
|
||||||
local app = self.apps[app_id] ---@type pocket_app
|
local app = self.apps[app_id] ---@type pocket_app
|
||||||
if app then
|
if app then
|
||||||
if not app.loaded then render_queue.push_data(MQ__RENDER_DATA.LOAD_APP, app_id) end
|
if app.requires_conn() and not smem.pkt_sys.pocket_comms.is_linked() then
|
||||||
|
-- bring up the app loader
|
||||||
|
self.loader_return = app_id
|
||||||
|
app_id = APP_ID.LOADER
|
||||||
|
app = self.apps[app_id]
|
||||||
|
else self.loader_return = nil end
|
||||||
|
|
||||||
|
if not app.loaded then smem.q.mq_render.push_data(MQ__RENDER_DATA.LOAD_APP, app_id) end
|
||||||
|
|
||||||
self.cur_app = app_id
|
self.cur_app = app_id
|
||||||
self.pane.set_value(app_id)
|
self.pane.set_value(app_id)
|
||||||
@ -267,6 +279,13 @@ function pocket.init_nav(render_queue)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- open the app that was blocked on connecting
|
||||||
|
function nav.on_loader_connected()
|
||||||
|
if self.loader_return then
|
||||||
|
nav.open_app(self.loader_return)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- load a given app
|
-- load a given app
|
||||||
---@param app_id POCKET_APP_ID
|
---@param app_id POCKET_APP_ID
|
||||||
function nav.load_app(app_id)
|
function nav.load_app(app_id)
|
||||||
@ -351,14 +370,14 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
sv = {
|
sv = {
|
||||||
linked = false,
|
linked = false,
|
||||||
addr = comms.BROADCAST,
|
addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil, ---@type nil|integer
|
r_seq_num = nil, ---@type nil|integer
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
},
|
},
|
||||||
api = {
|
api = {
|
||||||
linked = false,
|
linked = false,
|
||||||
addr = comms.BROADCAST,
|
addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil, ---@type nil|integer
|
r_seq_num = nil, ---@type nil|integer
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
},
|
},
|
||||||
@ -585,8 +604,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
elseif r_chan == config.CRD_Channel then
|
elseif r_chan == config.CRD_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.api.r_seq_num == nil then
|
if self.api.r_seq_num == nil then
|
||||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.connected and ((self.api.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.api.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order (API): last = " .. self.api.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
||||||
@ -594,7 +613,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
self.api.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@ -705,8 +724,8 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
elseif r_chan == config.SVR_Channel then
|
elseif r_chan == config.SVR_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.sv.r_seq_num == nil then
|
if self.sv.r_seq_num == nil then
|
||||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.connected and ((self.sv.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.sv.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order (SVR): last = " .. self.sv.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.sv.linked and (src_addr ~= self.sv.addr) then
|
elseif self.sv.linked and (src_addr ~= self.sv.addr) then
|
||||||
@ -714,7 +733,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.sv.r_seq_num = packet.scada_frame.seq_num()
|
self.sv.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@ -844,6 +863,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_api_linked() return self.api.linked end
|
function public.is_api_linked() return self.api.linked end
|
||||||
|
|
||||||
|
-- check if we are still linked with the supervisor and coordinator
|
||||||
|
---@nodiscard
|
||||||
|
function public.is_linked() return self.sv.linked and self.api.linked end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ local pocket = require("pocket.pocket")
|
|||||||
local renderer = require("pocket.renderer")
|
local renderer = require("pocket.renderer")
|
||||||
local threads = require("pocket.threads")
|
local threads = require("pocket.threads")
|
||||||
|
|
||||||
local POCKET_VERSION = "v0.10.0-alpha"
|
local POCKET_VERSION = "v0.11.0-alpha"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -123,7 +123,7 @@ local function main()
|
|||||||
-- setup system
|
-- setup system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
smem_sys.nav = pocket.init_nav(__shared_memory.q.mq_render)
|
smem_sys.nav = pocket.init_nav(__shared_memory)
|
||||||
|
|
||||||
-- message authentication init
|
-- message authentication init
|
||||||
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
||||||
|
@ -32,7 +32,7 @@ local function create_pages(root)
|
|||||||
|
|
||||||
local alarm_test = Div{parent=root,x=1,y=1}
|
local alarm_test = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
local alarm_app = db.nav.register_app(APP_ID.ALARMS, alarm_test)
|
local alarm_app = db.nav.register_app(APP_ID.ALARMS, alarm_test, nil, true)
|
||||||
|
|
||||||
local page = alarm_app.new_page(nil, function () end)
|
local page = alarm_app.new_page(nil, function () end)
|
||||||
page.tasks = { db.diag.tone_test.get_tone_states }
|
page.tasks = { db.diag.tone_test.get_tone_states }
|
||||||
|
49
pocket/ui/apps/loader.lua
Normal file
49
pocket/ui/apps/loader.lua
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
--
|
||||||
|
-- Loading Screen App
|
||||||
|
--
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
|
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
|
local LINK_STATE = iocontrol.LINK_STATE
|
||||||
|
|
||||||
|
-- create the connecting to SV & API page
|
||||||
|
---@param root graphics_element parent
|
||||||
|
local function create_pages(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local main = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
db.nav.register_app(APP_ID.LOADER, main).new_page(nil, function () end)
|
||||||
|
|
||||||
|
local conn_sv_wait = conn_waiting(main, 6, false)
|
||||||
|
local conn_api_wait = conn_waiting(main, 6, true)
|
||||||
|
local main_pane = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
|
local root_pane = MultiPane{parent=main,x=1,y=1,panes={conn_sv_wait,conn_api_wait,main_pane}}
|
||||||
|
|
||||||
|
root_pane.register(db.ps, "link_state", function (state)
|
||||||
|
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
||||||
|
root_pane.set_value(1)
|
||||||
|
elseif state == LINK_STATE.SV_LINK_ONLY then
|
||||||
|
root_pane.set_value(2)
|
||||||
|
else
|
||||||
|
root_pane.set_value(3)
|
||||||
|
db.nav.on_loader_connected()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
TextBox{parent=main_pane,text="Connected!",x=1,y=6,alignment=core.ALIGN.CENTER}
|
||||||
|
end
|
||||||
|
|
||||||
|
return create_pages
|
@ -23,16 +23,16 @@ local function init(parent, y, is_api)
|
|||||||
local root = Div{parent=parent,x=1,y=1}
|
local root = Div{parent=parent,x=1,y=1}
|
||||||
|
|
||||||
-- bounding box div
|
-- bounding box div
|
||||||
local box = Div{parent=root,x=1,y=y,height=5}
|
local box = Div{parent=root,x=1,y=y,height=6}
|
||||||
|
|
||||||
local waiting_x = math.floor(parent.get_width() / 2) - 1
|
local waiting_x = math.floor(parent.get_width() / 2) - 1
|
||||||
|
|
||||||
if is_api then
|
if is_api then
|
||||||
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.blue,style.root.bkg)}
|
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.blue,style.root.bkg)}
|
||||||
TextBox{parent=box,text="Connecting to API",alignment=ALIGN.CENTER,y=5,height=1,fg_bg=cpair(colors.white,style.root.bkg)}
|
TextBox{parent=box,text="Connecting to API",alignment=ALIGN.CENTER,y=5,fg_bg=cpair(colors.white,style.root.bkg)}
|
||||||
else
|
else
|
||||||
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.green,style.root.bkg)}
|
WaitingAnim{parent=box,x=waiting_x,y=1,fg_bg=cpair(colors.green,style.root.bkg)}
|
||||||
TextBox{parent=box,text="Connecting to Supervisor",alignment=ALIGN.CENTER,y=5,height=1,fg_bg=cpair(colors.white,style.root.bkg)}
|
TextBox{parent=box,text="Connecting to Supervisor",alignment=ALIGN.CENTER,y=5,fg_bg=cpair(colors.white,style.root.bkg)}
|
||||||
end
|
end
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
@ -10,11 +10,10 @@ local pocket = require("pocket.pocket")
|
|||||||
local diag_apps = require("pocket.ui.apps.diag_apps")
|
local diag_apps = require("pocket.ui.apps.diag_apps")
|
||||||
local dummy_app = require("pocket.ui.apps.dummy_app")
|
local dummy_app = require("pocket.ui.apps.dummy_app")
|
||||||
local guide_app = require("pocket.ui.apps.guide")
|
local guide_app = require("pocket.ui.apps.guide")
|
||||||
|
local loader_app = require("pocket.ui.apps.loader")
|
||||||
local sys_apps = require("pocket.ui.apps.sys_apps")
|
local sys_apps = require("pocket.ui.apps.sys_apps")
|
||||||
local unit_app = require("pocket.ui.apps.unit")
|
local unit_app = require("pocket.ui.apps.unit")
|
||||||
|
|
||||||
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
|
||||||
|
|
||||||
local home_page = require("pocket.ui.pages.home_page")
|
local home_page = require("pocket.ui.pages.home_page")
|
||||||
|
|
||||||
local style = require("pocket.ui.style")
|
local style = require("pocket.ui.style")
|
||||||
@ -33,8 +32,6 @@ local SignalBar = require("graphics.elements.indicators.signal")
|
|||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local LINK_STATE = iocontrol.LINK_STATE
|
|
||||||
|
|
||||||
local APP_ID = pocket.APP_ID
|
local APP_ID = pocket.APP_ID
|
||||||
|
|
||||||
-- create new main view
|
-- create new main view
|
||||||
@ -42,7 +39,9 @@ local APP_ID = pocket.APP_ID
|
|||||||
local function init(main)
|
local function init(main)
|
||||||
local db = iocontrol.get_db()
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
-- window header message
|
local main_pane = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
|
-- window header message and connection status
|
||||||
TextBox{parent=main,y=1,text="EARLY ACCESS ALPHA S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
TextBox{parent=main,y=1,text="EARLY ACCESS ALPHA S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
||||||
local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
@ -50,40 +49,18 @@ local function init(main)
|
|||||||
db.ps.subscribe("svr_conn_quality", svr_conn.set_value)
|
db.ps.subscribe("svr_conn_quality", svr_conn.set_value)
|
||||||
db.ps.subscribe("crd_conn_quality", crd_conn.set_value)
|
db.ps.subscribe("crd_conn_quality", crd_conn.set_value)
|
||||||
|
|
||||||
--#region root panel panes (connection screens + main screen)
|
|
||||||
|
|
||||||
local root_pane_div = Div{parent=main,x=1,y=2}
|
|
||||||
|
|
||||||
local conn_sv_wait = conn_waiting(root_pane_div, 6, false)
|
|
||||||
local conn_api_wait = conn_waiting(root_pane_div, 6, true)
|
|
||||||
local main_pane = Div{parent=main,x=1,y=2}
|
|
||||||
|
|
||||||
local root_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={conn_sv_wait,conn_api_wait,main_pane}}
|
|
||||||
|
|
||||||
root_pane.register(db.ps, "link_state", function (state)
|
|
||||||
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
|
||||||
root_pane.set_value(1)
|
|
||||||
elseif state == LINK_STATE.SV_LINK_ONLY then
|
|
||||||
root_pane.set_value(2)
|
|
||||||
else
|
|
||||||
root_pane.set_value(3)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
--#endregion
|
|
||||||
|
|
||||||
--#region main page panel panes & sidebar
|
|
||||||
|
|
||||||
local page_div = Div{parent=main_pane,x=4,y=1}
|
local page_div = Div{parent=main_pane,x=4,y=1}
|
||||||
|
|
||||||
|
-- create all the apps & pages
|
||||||
home_page(page_div)
|
home_page(page_div)
|
||||||
|
|
||||||
unit_app(page_div)
|
unit_app(page_div)
|
||||||
guide_app(page_div)
|
guide_app(page_div)
|
||||||
|
loader_app(page_div)
|
||||||
sys_apps(page_div)
|
sys_apps(page_div)
|
||||||
diag_apps(page_div)
|
diag_apps(page_div)
|
||||||
dummy_app(page_div)
|
dummy_app(page_div)
|
||||||
|
|
||||||
|
-- verify all apps were created
|
||||||
assert(util.table_len(db.nav.get_containers()) == APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered")
|
assert(util.table_len(db.nav.get_containers()) == APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered")
|
||||||
|
|
||||||
db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()})
|
db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()})
|
||||||
@ -92,8 +69,6 @@ local function init(main)
|
|||||||
PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up}
|
PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up}
|
||||||
|
|
||||||
db.nav.open_app(APP_ID.ROOT)
|
db.nav.open_app(APP_ID.ROOT)
|
||||||
|
|
||||||
--#endregion
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return init
|
return init
|
||||||
|
@ -524,8 +524,8 @@ end
|
|||||||
function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
||||||
local self = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil,
|
r_seq_num = nil, ---@type nil|integer
|
||||||
scrammed = false,
|
scrammed = false,
|
||||||
linked = false,
|
linked = false,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||||
@ -571,33 +571,17 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
self.seq_num = self.seq_num + 1
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- variable reactor status information, excluding heating rate
|
-- dynamic reactor status information, excluding heating rate
|
||||||
---@return table data_table, boolean faulted
|
---@return table data_table, boolean faulted
|
||||||
local function _reactor_status()
|
local function _get_reactor_status()
|
||||||
local fuel = nil
|
local fuel = nil
|
||||||
local waste = nil
|
local waste = nil
|
||||||
local coolant = nil
|
local coolant = nil
|
||||||
local hcoolant = nil
|
local hcoolant = nil
|
||||||
|
|
||||||
local data_table = {
|
local data_table = {}
|
||||||
false, -- getStatus
|
|
||||||
0, -- getBurnRate
|
reactor.__p_disable_afc()
|
||||||
0, -- getActualBurnRate
|
|
||||||
0, -- getTemperature
|
|
||||||
0, -- getDamagePercent
|
|
||||||
0, -- getBoilEfficiency
|
|
||||||
0, -- getEnvironmentalLoss
|
|
||||||
0, -- fuel_amnt
|
|
||||||
0, -- getFuelFilledPercentage
|
|
||||||
0, -- waste_amnt
|
|
||||||
0, -- getWasteFilledPercentage
|
|
||||||
"", -- coolant_name
|
|
||||||
0, -- coolant_amnt
|
|
||||||
0, -- getCoolantFilledPercentage
|
|
||||||
"", -- hcoolant_name
|
|
||||||
0, -- hcoolant_amnt
|
|
||||||
0 -- getHeatedCoolantFilledPercentage
|
|
||||||
}
|
|
||||||
|
|
||||||
local tasks = {
|
local tasks = {
|
||||||
function () data_table[1] = reactor.getStatus() end,
|
function () data_table[1] = reactor.getStatus() end,
|
||||||
@ -637,31 +621,33 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
data_table[16] = hcoolant.amount
|
data_table[16] = hcoolant.amount
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reactor.__p_enable_afc()
|
||||||
|
|
||||||
return data_table, reactor.__p_is_faulted()
|
return data_table, reactor.__p_is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update the status cache if changed
|
-- update the status cache if changed
|
||||||
---@return boolean changed
|
---@return boolean changed
|
||||||
local function _update_status_cache()
|
local function _update_status_cache()
|
||||||
local status, faulted = _reactor_status()
|
local status, faulted = _get_reactor_status()
|
||||||
local changed = false
|
local changed = false
|
||||||
|
|
||||||
if self.status_cache ~= nil then
|
|
||||||
if not faulted then
|
if not faulted then
|
||||||
|
if self.status_cache ~= nil then
|
||||||
for i = 1, #status do
|
for i = 1, #status do
|
||||||
if status[i] ~= self.status_cache[i] then
|
if status[i] ~= self.status_cache[i] then
|
||||||
changed = true
|
changed = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
else
|
else
|
||||||
changed = true
|
changed = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if changed and not faulted then
|
if changed then
|
||||||
self.status_cache = status
|
self.status_cache = status
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return changed
|
return changed
|
||||||
end
|
end
|
||||||
@ -679,9 +665,11 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
_send(msg_type, { status })
|
_send(msg_type, { status })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- send structure properties (these should not change, server will cache these)
|
-- send static structure properties, cached by server
|
||||||
local function _send_struct()
|
local function _send_struct()
|
||||||
local mek_data = { false, 0, 0, 0, types.new_zero_coordinate(), types.new_zero_coordinate(), 0, 0, 0, 0, 0, 0, 0, 0 }
|
local mek_data = {}
|
||||||
|
|
||||||
|
reactor.__p_disable_afc()
|
||||||
|
|
||||||
local tasks = {
|
local tasks = {
|
||||||
function () mek_data[1] = reactor.getLength() end,
|
function () mek_data[1] = reactor.getLength() end,
|
||||||
@ -705,6 +693,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
||||||
self.resend_build = false
|
self.resend_build = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reactor.__p_enable_afc()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
@ -835,8 +825,8 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
if l_chan == config.PLC_Channel then
|
if l_chan == config.PLC_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif self.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif self.linked and (src_addr ~= self.sv_addr) then
|
elseif self.linked and (src_addr ~= self.sv_addr) then
|
||||||
@ -844,7 +834,7 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed the watchdog first so it doesn't uhh...eat our packets :)
|
-- feed the watchdog first so it doesn't uhh...eat our packets :)
|
||||||
@ -1030,10 +1020,9 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
|
|||||||
println_ts("linked!")
|
println_ts("linked!")
|
||||||
log.info("supervisor establish request approved, linked to SV (CID#" .. src_addr .. ")")
|
log.info("supervisor establish request approved, linked to SV (CID#" .. src_addr .. ")")
|
||||||
|
|
||||||
-- link + reset remote sequence number and cache
|
-- link + reset cache
|
||||||
self.sv_addr = src_addr
|
self.sv_addr = src_addr
|
||||||
self.linked = true
|
self.linked = true
|
||||||
self.r_seq_num = nil
|
|
||||||
self.status_cache = nil
|
self.status_cache = nil
|
||||||
|
|
||||||
if plc_state.reactor_formed then _send_struct() end
|
if plc_state.reactor_formed then _send_struct() end
|
||||||
|
@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
|
|||||||
local renderer = require("reactor-plc.renderer")
|
local renderer = require("reactor-plc.renderer")
|
||||||
local threads = require("reactor-plc.threads")
|
local threads = require("reactor-plc.threads")
|
||||||
|
|
||||||
local R_PLC_VERSION = "v1.7.11"
|
local R_PLC_VERSION = "v1.8.0"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
11
rtu/rtu.lua
11
rtu/rtu.lua
@ -284,8 +284,8 @@ end
|
|||||||
function rtu.comms(version, nic, conn_watchdog)
|
function rtu.comms(version, nic, conn_watchdog)
|
||||||
local self = {
|
local self = {
|
||||||
sv_addr = comms.BROADCAST,
|
sv_addr = comms.BROADCAST,
|
||||||
seq_num = 0,
|
seq_num = util.time_ms() * 10, -- unique per peer, restarting will not re-use seq nums due to message rate
|
||||||
r_seq_num = nil,
|
r_seq_num = nil, ---@type nil|integer
|
||||||
txn_id = 0,
|
txn_id = 0,
|
||||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||||
}
|
}
|
||||||
@ -442,8 +442,8 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
if l_chan == config.RTU_Channel then
|
if l_chan == config.RTU_Channel then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
elseif rtu_state.linked and ((self.r_seq_num + 1) ~= packet.scada_frame.seq_num()) then
|
elseif self.r_seq_num ~= packet.scada_frame.seq_num() then
|
||||||
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
elseif rtu_state.linked and (src_addr ~= self.sv_addr) then
|
elseif rtu_state.linked and (src_addr ~= self.sv_addr) then
|
||||||
@ -451,7 +451,7 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
@ -556,7 +556,6 @@ function rtu.comms(version, nic, conn_watchdog)
|
|||||||
-- establish allowed
|
-- establish allowed
|
||||||
rtu_state.linked = true
|
rtu_state.linked = true
|
||||||
self.sv_addr = packet.scada_frame.src_addr()
|
self.sv_addr = packet.scada_frame.src_addr()
|
||||||
self.r_seq_num = nil
|
|
||||||
println_ts("supervisor connection established")
|
println_ts("supervisor connection established")
|
||||||
log.info("supervisor connection established")
|
log.info("supervisor connection established")
|
||||||
else
|
else
|
||||||
|
@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
|||||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||||
|
|
||||||
local RTU_VERSION = "v1.9.6"
|
local RTU_VERSION = "v1.10.1"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
@ -93,14 +93,6 @@ local function main()
|
|||||||
network.init_mac(config.AuthKey)
|
network.init_mac(config.AuthKey)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get modem
|
|
||||||
local modem = ppm.get_wireless_modem()
|
|
||||||
if modem == nil then
|
|
||||||
println("boot> wireless modem not found")
|
|
||||||
log.fatal("no wireless modem on startup")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- generate alarm tones
|
-- generate alarm tones
|
||||||
audio.generate_tones()
|
audio.generate_tones()
|
||||||
|
|
||||||
@ -116,12 +108,13 @@ local function main()
|
|||||||
|
|
||||||
-- RTU gateway devices (not RTU units)
|
-- RTU gateway devices (not RTU units)
|
||||||
rtu_dev = {
|
rtu_dev = {
|
||||||
|
modem = ppm.get_wireless_modem(),
|
||||||
sounders = {}
|
sounders = {}
|
||||||
},
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
rtu_sys = {
|
rtu_sys = {
|
||||||
nic = network.nic(modem),
|
nic = nil, ---@type nic
|
||||||
rtu_comms = nil, ---@type rtu_comms
|
rtu_comms = nil, ---@type rtu_comms
|
||||||
conn_watchdog = nil, ---@type watchdog
|
conn_watchdog = nil, ---@type watchdog
|
||||||
units = {}
|
units = {}
|
||||||
@ -134,8 +127,9 @@ local function main()
|
|||||||
}
|
}
|
||||||
|
|
||||||
local smem_sys = __shared_memory.rtu_sys
|
local smem_sys = __shared_memory.rtu_sys
|
||||||
|
local smem_dev = __shared_memory.rtu_dev
|
||||||
|
|
||||||
databus.tx_hw_modem(true)
|
local rtu_state = __shared_memory.rtu_state
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- interpret config and init units
|
-- interpret config and init units
|
||||||
@ -501,8 +495,6 @@ local function main()
|
|||||||
-- start system
|
-- start system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local rtu_state = __shared_memory.rtu_state
|
|
||||||
|
|
||||||
log.debug("boot> running sys_config()")
|
log.debug("boot> running sys_config()")
|
||||||
|
|
||||||
if sys_config() then
|
if sys_config() then
|
||||||
@ -517,23 +509,33 @@ local function main()
|
|||||||
log.info("startup> running in headless mode without front panel")
|
log.info("startup> running in headless mode without front panel")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check modem
|
||||||
|
if smem_dev.modem == nil then
|
||||||
|
println("startup> wireless modem not found")
|
||||||
|
log.fatal("no wireless modem on startup")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
databus.tx_hw_modem(true)
|
||||||
|
|
||||||
-- find and setup all speakers
|
-- find and setup all speakers
|
||||||
local speakers = ppm.get_all_devices("speaker")
|
local speakers = ppm.get_all_devices("speaker")
|
||||||
for _, s in pairs(speakers) do
|
for _, s in pairs(speakers) do
|
||||||
local sounder = rtu.init_sounder(s)
|
local sounder = rtu.init_sounder(s)
|
||||||
|
|
||||||
table.insert(__shared_memory.rtu_dev.sounders, sounder)
|
table.insert(smem_dev.sounders, sounder)
|
||||||
|
|
||||||
log.debug(util.c("startup> added speaker, attached as ", sounder.name))
|
log.debug(util.c("startup> added speaker, attached as ", sounder.name))
|
||||||
end
|
end
|
||||||
|
|
||||||
databus.tx_hw_spkr_count(#__shared_memory.rtu_dev.sounders)
|
databus.tx_hw_spkr_count(#smem_dev.sounders)
|
||||||
|
|
||||||
-- start connection watchdog
|
-- start connection watchdog
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
||||||
log.debug("startup> conn watchdog started")
|
log.debug("startup> conn watchdog started")
|
||||||
|
|
||||||
-- setup comms
|
-- setup comms
|
||||||
|
smem_sys.nic = network.nic(smem_dev.modem)
|
||||||
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_sys.nic, smem_sys.conn_watchdog)
|
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_sys.nic, smem_sys.conn_watchdog)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ local max_distance = nil
|
|||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
||||||
comms.version = "2.5.2"
|
comms.version = "3.0.0"
|
||||||
comms.api_version = "0.0.3"
|
comms.api_version = "0.0.3"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
@ -240,6 +240,8 @@ function comms.scada_packet()
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.modem_event() return self.modem_msg_in end
|
function public.modem_event() return self.modem_msg_in end
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
function public.raw_header() return { self.src_addr, self.dest_addr, self.seq_num, self.protocol } end
|
||||||
|
---@nodiscard
|
||||||
function public.raw_sendable() return self.raw end
|
function public.raw_sendable() return self.raw end
|
||||||
|
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@ -278,7 +280,7 @@ function comms.authd_packet()
|
|||||||
src_addr = comms.BROADCAST,
|
src_addr = comms.BROADCAST,
|
||||||
dest_addr = comms.BROADCAST,
|
dest_addr = comms.BROADCAST,
|
||||||
mac = "",
|
mac = "",
|
||||||
payload = ""
|
payload = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class authd_packet
|
---@class authd_packet
|
||||||
@ -286,14 +288,13 @@ function comms.authd_packet()
|
|||||||
|
|
||||||
-- make an authenticated SCADA packet
|
-- make an authenticated SCADA packet
|
||||||
---@param s_packet scada_packet scada packet to authenticate
|
---@param s_packet scada_packet scada packet to authenticate
|
||||||
---@param mac function message authentication function
|
---@param mac function message authentication hash function
|
||||||
function public.make(s_packet, mac)
|
function public.make(s_packet, mac)
|
||||||
self.valid = true
|
self.valid = true
|
||||||
self.src_addr = s_packet.src_addr()
|
self.src_addr = s_packet.src_addr()
|
||||||
self.dest_addr = s_packet.dest_addr()
|
self.dest_addr = s_packet.dest_addr()
|
||||||
self.payload = textutils.serialize(s_packet.raw_sendable(), { allow_repetitions = true, compact = true })
|
self.mac = mac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||||
self.mac = mac(self.payload)
|
self.raw = { self.src_addr, self.dest_addr, self.mac, s_packet.raw_sendable() }
|
||||||
self.raw = { self.src_addr, self.dest_addr, self.mac, self.payload }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- parse in a modem message as an authenticated SCADA packet
|
-- parse in a modem message as an authenticated SCADA packet
|
||||||
@ -330,14 +331,14 @@ function comms.authd_packet()
|
|||||||
self.src_addr = nil
|
self.src_addr = nil
|
||||||
self.dest_addr = nil
|
self.dest_addr = nil
|
||||||
self.mac = ""
|
self.mac = ""
|
||||||
self.payload = ""
|
self.payload = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if this packet is destined for this device
|
-- check if this packet is destined for this device
|
||||||
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
|
local is_destination = (self.dest_addr == comms.BROADCAST) or (self.dest_addr == COMPUTER_ID)
|
||||||
|
|
||||||
self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
|
self.valid = is_destination and type(self.src_addr) == "number" and type(self.dest_addr) == "number" and
|
||||||
type(self.mac) == "string" and type(self.payload) == "string"
|
type(self.mac) == "string" and type(self.payload) == "table"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,7 +52,6 @@ local alarms = {}
|
|||||||
|
|
||||||
-- unit alarms
|
-- unit alarms
|
||||||
|
|
||||||
alarms.HIGH_TEMP = 1150 -- temp >= 1150K
|
|
||||||
alarms.HIGH_WASTE = 0.85 -- fill > 85%
|
alarms.HIGH_WASTE = 0.85 -- fill > 85%
|
||||||
alarms.HIGH_RADIATION = 0.00005 -- 50 uSv/h, not yet damaging but this isn't good
|
alarms.HIGH_RADIATION = 0.00005 -- 50 uSv/h, not yet damaging but this isn't good
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ function network.nic(modem)
|
|||||||
modem.open(channel)
|
modem.open(channel)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- link all public functions except for transmit
|
-- link all public functions except for transmit, open, and close
|
||||||
for key, func in pairs(modem) do
|
for key, func in pairs(modem) do
|
||||||
if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end
|
if key ~= "transmit" and key ~= "open" and key ~= "close" and key ~= "closeAll" then public[key] = func end
|
||||||
end
|
end
|
||||||
@ -184,7 +184,7 @@ function network.nic(modem)
|
|||||||
---@cast tx_packet authd_packet
|
---@cast tx_packet authd_packet
|
||||||
tx_packet.make(packet, compute_hmac)
|
tx_packet.make(packet, compute_hmac)
|
||||||
|
|
||||||
-- log.debug("crypto.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("network.modem.transmit: data processing took " .. (util.time_ms() - start) .. "ms")
|
||||||
end
|
end
|
||||||
|
|
||||||
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
modem.transmit(dest_channel, local_channel, tx_packet.raw_sendable())
|
||||||
@ -211,17 +211,18 @@ function network.nic(modem)
|
|||||||
a_packet.receive(side, sender, reply_to, message, distance)
|
a_packet.receive(side, sender, reply_to, message, distance)
|
||||||
|
|
||||||
if a_packet.is_valid() then
|
if a_packet.is_valid() then
|
||||||
-- local start = util.time_ms()
|
s_packet.receive(side, sender, reply_to, a_packet.data(), distance)
|
||||||
local packet_hmac = a_packet.mac()
|
|
||||||
local msg = a_packet.data()
|
|
||||||
local computed_hmac = compute_hmac(msg)
|
|
||||||
|
|
||||||
if packet_hmac == computed_hmac then
|
if s_packet.is_valid() then
|
||||||
-- log.debug("crypto.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
-- local start = util.time_ms()
|
||||||
s_packet.receive(side, sender, reply_to, textutils.unserialize(msg), distance)
|
local computed_hmac = compute_hmac(textutils.serialize(s_packet.raw_header(), { allow_repetitions = true, compact = true }))
|
||||||
|
|
||||||
|
if a_packet.mac() == computed_hmac then
|
||||||
|
-- log.debug("network.modem.receive: HMAC verified in " .. (util.time_ms() - start) .. "ms")
|
||||||
s_packet.stamp_authenticated()
|
s_packet.stamp_authenticated()
|
||||||
else
|
else
|
||||||
-- log.debug("crypto.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
-- log.debug("network.modem.receive: HMAC failed verification in " .. (util.time_ms() - start) .. "ms")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
26
startup.lua
26
startup.lua
@ -1,35 +1,31 @@
|
|||||||
local util = require("scada-common.util")
|
local BOOTLOADER_VERSION = "1.1"
|
||||||
|
|
||||||
local println = util.println
|
print("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
||||||
|
print("BOOT> SCANNING FOR APPLICATIONS...")
|
||||||
local BOOTLOADER_VERSION = "1.0"
|
|
||||||
|
|
||||||
println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
|
||||||
println("BOOT> SCANNING FOR APPLICATIONS...")
|
|
||||||
|
|
||||||
local exit_code
|
local exit_code
|
||||||
|
|
||||||
if fs.exists("reactor-plc/startup.lua") then
|
if fs.exists("reactor-plc/startup.lua") then
|
||||||
println("BOOT> EXEC REACTOR PLC STARTUP")
|
print("BOOT> EXEC REACTOR PLC STARTUP")
|
||||||
exit_code = shell.execute("reactor-plc/startup")
|
exit_code = shell.execute("reactor-plc/startup")
|
||||||
elseif fs.exists("rtu/startup.lua") then
|
elseif fs.exists("rtu/startup.lua") then
|
||||||
println("BOOT> EXEC RTU STARTUP")
|
print("BOOT> EXEC RTU STARTUP")
|
||||||
exit_code = shell.execute("rtu/startup")
|
exit_code = shell.execute("rtu/startup")
|
||||||
elseif fs.exists("supervisor/startup.lua") then
|
elseif fs.exists("supervisor/startup.lua") then
|
||||||
println("BOOT> EXEC SUPERVISOR STARTUP")
|
print("BOOT> EXEC SUPERVISOR STARTUP")
|
||||||
exit_code = shell.execute("supervisor/startup")
|
exit_code = shell.execute("supervisor/startup")
|
||||||
elseif fs.exists("coordinator/startup.lua") then
|
elseif fs.exists("coordinator/startup.lua") then
|
||||||
println("BOOT> EXEC COORDINATOR STARTUP")
|
print("BOOT> EXEC COORDINATOR STARTUP")
|
||||||
exit_code = shell.execute("coordinator/startup")
|
exit_code = shell.execute("coordinator/startup")
|
||||||
elseif fs.exists("pocket/startup.lua") then
|
elseif fs.exists("pocket/startup.lua") then
|
||||||
println("BOOT> EXEC POCKET STARTUP")
|
print("BOOT> EXEC POCKET STARTUP")
|
||||||
exit_code = shell.execute("pocket/startup")
|
exit_code = shell.execute("pocket/startup")
|
||||||
else
|
else
|
||||||
println("BOOT> NO SCADA STARTUP FOUND")
|
print("BOOT> NO SCADA STARTUP FOUND")
|
||||||
println("BOOT> EXIT")
|
print("BOOT> EXIT")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not exit_code then println("BOOT> APPLICATION CRASHED") end
|
if not exit_code then print("BOOT> APPLICATION CRASHED") end
|
||||||
|
|
||||||
return exit_code
|
return exit_code
|
||||||
|
@ -43,12 +43,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facility, fp_ok)
|
function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -57,8 +58,8 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
local self = {
|
local self = {
|
||||||
units = facility.get_units(),
|
units = facility.get_units(),
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
establish_time = util.time_s(),
|
establish_time = util.time_s(),
|
||||||
@ -182,13 +183,11 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
---@param pkt mgmt_frame|crdn_frame
|
---@param pkt mgmt_frame|crdn_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
@ -48,12 +48,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param reactor_id integer reactor ID
|
---@param reactor_id integer reactor ID
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, fp_ok)
|
function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue, timeout, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -66,8 +67,8 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
ramping_rate = false,
|
ramping_rate = false,
|
||||||
auto_lock = false,
|
auto_lock = false,
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
received_struct = false,
|
received_struct = false,
|
||||||
received_status_cache = false,
|
received_status_cache = false,
|
||||||
@ -190,71 +191,111 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
-- copy in the RPS status
|
-- copy in the RPS status
|
||||||
---@param rps_status table
|
---@param rps_status table
|
||||||
local function _copy_rps_status(rps_status)
|
local function _copy_rps_status(rps_status)
|
||||||
|
local rps = self.sDB.rps_status
|
||||||
|
|
||||||
self.sDB.rps_tripped = rps_status[1]
|
self.sDB.rps_tripped = rps_status[1]
|
||||||
self.sDB.rps_trip_cause = rps_status[2]
|
self.sDB.rps_trip_cause = rps_status[2]
|
||||||
self.sDB.rps_status.high_dmg = rps_status[3]
|
|
||||||
self.sDB.rps_status.high_temp = rps_status[4]
|
rps.high_dmg = rps_status[3]
|
||||||
self.sDB.rps_status.low_cool = rps_status[5]
|
rps.high_temp = rps_status[4]
|
||||||
self.sDB.rps_status.ex_waste = rps_status[6]
|
rps.low_cool = rps_status[5]
|
||||||
self.sDB.rps_status.ex_hcool = rps_status[7]
|
rps.ex_waste = rps_status[6]
|
||||||
self.sDB.rps_status.no_fuel = rps_status[8]
|
rps.ex_hcool = rps_status[7]
|
||||||
self.sDB.rps_status.fault = rps_status[9]
|
rps.no_fuel = rps_status[8]
|
||||||
self.sDB.rps_status.timeout = rps_status[10]
|
rps.fault = rps_status[9]
|
||||||
self.sDB.rps_status.manual = rps_status[11]
|
rps.timeout = rps_status[10]
|
||||||
self.sDB.rps_status.automatic = rps_status[12]
|
rps.manual = rps_status[11]
|
||||||
self.sDB.rps_status.sys_fail = rps_status[13]
|
rps.automatic = rps_status[12]
|
||||||
self.sDB.rps_status.force_dis = rps_status[14]
|
rps.sys_fail = rps_status[13]
|
||||||
|
rps.force_dis = rps_status[14]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy in the reactor status
|
-- copy in the reactor status
|
||||||
---@param mek_data table
|
---@param mek_data table
|
||||||
local function _copy_status(mek_data)
|
local function _copy_status(mek_data)
|
||||||
|
local stat = self.sDB.mek_status
|
||||||
|
local struct = self.sDB.mek_struct
|
||||||
|
|
||||||
-- copy status information
|
-- copy status information
|
||||||
self.sDB.mek_status.status = mek_data[1]
|
stat.status = mek_data[1]
|
||||||
self.sDB.mek_status.burn_rate = mek_data[2]
|
stat.burn_rate = mek_data[2]
|
||||||
self.sDB.mek_status.act_burn_rate = mek_data[3]
|
stat.act_burn_rate = mek_data[3]
|
||||||
self.sDB.mek_status.temp = mek_data[4]
|
stat.temp = mek_data[4]
|
||||||
self.sDB.mek_status.damage = mek_data[5]
|
stat.damage = mek_data[5]
|
||||||
self.sDB.mek_status.boil_eff = mek_data[6]
|
stat.boil_eff = mek_data[6]
|
||||||
self.sDB.mek_status.env_loss = mek_data[7]
|
stat.env_loss = mek_data[7]
|
||||||
|
|
||||||
-- copy container information
|
-- copy container information
|
||||||
self.sDB.mek_status.fuel = mek_data[8]
|
stat.fuel = mek_data[8]
|
||||||
self.sDB.mek_status.fuel_fill = mek_data[9]
|
stat.fuel_fill = mek_data[9]
|
||||||
self.sDB.mek_status.waste = mek_data[10]
|
stat.waste = mek_data[10]
|
||||||
self.sDB.mek_status.waste_fill = mek_data[11]
|
stat.waste_fill = mek_data[11]
|
||||||
self.sDB.mek_status.ccool_type = mek_data[12]
|
stat.ccool_type = mek_data[12]
|
||||||
self.sDB.mek_status.ccool_amnt = mek_data[13]
|
stat.ccool_amnt = mek_data[13]
|
||||||
self.sDB.mek_status.ccool_fill = mek_data[14]
|
stat.ccool_fill = mek_data[14]
|
||||||
self.sDB.mek_status.hcool_type = mek_data[15]
|
stat.hcool_type = mek_data[15]
|
||||||
self.sDB.mek_status.hcool_amnt = mek_data[16]
|
stat.hcool_amnt = mek_data[16]
|
||||||
self.sDB.mek_status.hcool_fill = mek_data[17]
|
stat.hcool_fill = mek_data[17]
|
||||||
|
|
||||||
-- update computable fields if we have our structure
|
-- update computable fields if we have our structure
|
||||||
if self.received_struct then
|
if self.received_struct then
|
||||||
self.sDB.mek_status.fuel_need = self.sDB.mek_struct.fuel_cap - self.sDB.mek_status.fuel_fill
|
stat.fuel_need = struct.fuel_cap - stat.fuel_fill
|
||||||
self.sDB.mek_status.waste_need = self.sDB.mek_struct.waste_cap - self.sDB.mek_status.waste_fill
|
stat.waste_need = struct.waste_cap - stat.waste_fill
|
||||||
self.sDB.mek_status.cool_need = self.sDB.mek_struct.ccool_cap - self.sDB.mek_status.ccool_fill
|
stat.cool_need = struct.ccool_cap - stat.ccool_fill
|
||||||
self.sDB.mek_status.hcool_need = self.sDB.mek_struct.hcool_cap - self.sDB.mek_status.hcool_fill
|
stat.hcool_need = struct.hcool_cap - stat.hcool_fill
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- copy in the reactor structure
|
-- copy in the reactor structure
|
||||||
---@param mek_data table
|
---@param mek_data table
|
||||||
local function _copy_struct(mek_data)
|
local function _copy_struct(mek_data)
|
||||||
self.sDB.mek_struct.length = mek_data[1]
|
local struct = self.sDB.mek_struct
|
||||||
self.sDB.mek_struct.width = mek_data[2]
|
|
||||||
self.sDB.mek_struct.height = mek_data[3]
|
struct.length = mek_data[1]
|
||||||
self.sDB.mek_struct.min_pos = mek_data[4]
|
struct.width = mek_data[2]
|
||||||
self.sDB.mek_struct.max_pos = mek_data[5]
|
struct.height = mek_data[3]
|
||||||
self.sDB.mek_struct.heat_cap = mek_data[6]
|
struct.min_pos = mek_data[4]
|
||||||
self.sDB.mek_struct.fuel_asm = mek_data[7]
|
struct.max_pos = mek_data[5]
|
||||||
self.sDB.mek_struct.fuel_sa = mek_data[8]
|
struct.heat_cap = mek_data[6]
|
||||||
self.sDB.mek_struct.fuel_cap = mek_data[9]
|
struct.fuel_asm = mek_data[7]
|
||||||
self.sDB.mek_struct.waste_cap = mek_data[10]
|
struct.fuel_sa = mek_data[8]
|
||||||
self.sDB.mek_struct.ccool_cap = mek_data[11]
|
struct.fuel_cap = mek_data[9]
|
||||||
self.sDB.mek_struct.hcool_cap = mek_data[12]
|
struct.waste_cap = mek_data[10]
|
||||||
self.sDB.mek_struct.max_burn = mek_data[13]
|
struct.ccool_cap = mek_data[11]
|
||||||
|
struct.hcool_cap = mek_data[12]
|
||||||
|
struct.max_burn = mek_data[13]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle a reactor status packet
|
||||||
|
---@param pkt rplc_frame
|
||||||
|
local function _handle_status(pkt)
|
||||||
|
local valid = (type(pkt.data[1]) == "number") and (type(pkt.data[2]) == "boolean") and
|
||||||
|
(type(pkt.data[3]) == "boolean") and (type(pkt.data[4]) == "boolean") and
|
||||||
|
(type(pkt.data[5]) == "number")
|
||||||
|
|
||||||
|
if valid then
|
||||||
|
self.sDB.last_status_update = pkt.data[1]
|
||||||
|
self.sDB.control_state = pkt.data[2]
|
||||||
|
self.sDB.no_reactor = pkt.data[3]
|
||||||
|
self.sDB.formed = pkt.data[4]
|
||||||
|
self.sDB.auto_ack_token = pkt.data[5]
|
||||||
|
|
||||||
|
if (not self.sDB.no_reactor) and self.sDB.formed and (type(pkt.data[6]) == "number") then
|
||||||
|
self.sDB.mek_status.heating_rate = pkt.data[6] or 0.0
|
||||||
|
|
||||||
|
-- attempt to read mek_data table
|
||||||
|
if type(pkt.data[7]) == "table" then
|
||||||
|
if #pkt.data[7] == 17 then
|
||||||
|
_copy_status(pkt.data[7])
|
||||||
|
self.received_status_cache = true
|
||||||
|
else
|
||||||
|
log.error(log_header .. "RPLC status packet reactor data length mismatch")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(log_header .. "RPLC status packet invalid")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mark this PLC session as closed, stop watchdog
|
-- mark this PLC session as closed, stop watchdog
|
||||||
@ -309,13 +350,11 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
---@param pkt mgmt_frame|rplc_frame
|
---@param pkt mgmt_frame|rplc_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- process packet
|
-- process packet
|
||||||
@ -334,48 +373,17 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
if pkt.type == RPLC_TYPE.STATUS then
|
if pkt.type == RPLC_TYPE.STATUS then
|
||||||
-- status packet received, update data
|
-- status packet received, update data
|
||||||
if pkt.length >= 5 then
|
if pkt.length >= 5 then
|
||||||
if (type(pkt.data[1]) == "number") and (type(pkt.data[2]) == "boolean") and (type(pkt.data[3]) == "boolean") and
|
_handle_status(pkt)
|
||||||
(type(pkt.data[4]) == "boolean") and (type(pkt.data[5]) == "number") then
|
|
||||||
self.sDB.last_status_update = pkt.data[1]
|
|
||||||
self.sDB.control_state = pkt.data[2]
|
|
||||||
self.sDB.no_reactor = pkt.data[3]
|
|
||||||
self.sDB.formed = pkt.data[4]
|
|
||||||
self.sDB.auto_ack_token = pkt.data[5]
|
|
||||||
|
|
||||||
if (not self.sDB.no_reactor) and self.sDB.formed and (type(pkt.data[6]) == "number") then
|
|
||||||
self.sDB.mek_status.heating_rate = pkt.data[6] or 0.0
|
|
||||||
|
|
||||||
-- attempt to read mek_data table
|
|
||||||
if type(pkt.data[7]) == "table" then
|
|
||||||
local status = pcall(_copy_status, pkt.data[7])
|
|
||||||
if status then
|
|
||||||
-- copied in status data OK
|
|
||||||
self.received_status_cache = true
|
|
||||||
else
|
|
||||||
-- error copying status data
|
|
||||||
log.error(log_header .. "failed to parse status packet data")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.debug(log_header .. "RPLC status packet invalid")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "RPLC status packet length mismatch")
|
log.debug(log_header .. "RPLC status packet length mismatch")
|
||||||
end
|
end
|
||||||
elseif pkt.type == RPLC_TYPE.MEK_STRUCT then
|
elseif pkt.type == RPLC_TYPE.MEK_STRUCT then
|
||||||
-- received reactor structure, record it
|
-- received reactor structure, record it
|
||||||
if pkt.length == 14 then
|
if pkt.length == 13 then
|
||||||
local status = pcall(_copy_struct, pkt.data)
|
_copy_struct(pkt.data)
|
||||||
if status then
|
|
||||||
-- copied in structure data OK
|
|
||||||
_compute_op_temps()
|
_compute_op_temps()
|
||||||
self.received_struct = true
|
self.received_struct = true
|
||||||
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
||||||
else
|
|
||||||
-- error copying structure data
|
|
||||||
log.error(log_header .. "failed to parse struct packet data")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "RPLC struct packet length mismatch")
|
log.debug(log_header .. "RPLC struct packet length mismatch")
|
||||||
end
|
end
|
||||||
|
@ -30,12 +30,13 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility, fp_ok)
|
function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -43,8 +44,8 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
|
|||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@ -93,13 +94,11 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout, facility,
|
|||||||
---@param pkt mgmt_frame
|
---@param pkt mgmt_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
@ -34,13 +34,14 @@ local PERIODICS = {
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param id integer session ID
|
---@param id integer session ID
|
||||||
---@param s_addr integer device source address
|
---@param s_addr integer device source address
|
||||||
|
---@param i_seq_num integer initial sequence number
|
||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
---@param advertisement table RTU device advertisement
|
---@param advertisement table RTU device advertisement
|
||||||
---@param facility facility facility data table
|
---@param facility facility facility data table
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement, facility, fp_ok)
|
function rtu.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout, advertisement, facility, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -51,8 +52,8 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
advert = advertisement,
|
advert = advertisement,
|
||||||
fac_units = facility.get_units(),
|
fac_units = facility.get_units(),
|
||||||
-- connection properties
|
-- connection properties
|
||||||
seq_num = 0,
|
seq_num = i_seq_num + 2, -- next after the establish approval was sent
|
||||||
r_seq_num = nil,
|
r_seq_num = i_seq_num + 1,
|
||||||
connected = true,
|
connected = true,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
@ -240,13 +241,11 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
|||||||
---@param pkt modbus_frame|mgmt_frame
|
---@param pkt modbus_frame|mgmt_frame
|
||||||
local function _handle_packet(pkt)
|
local function _handle_packet(pkt)
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num ~= pkt.scada_frame.seq_num() then
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
|
||||||
elseif (self.r_seq_num + 1) ~= pkt.scada_frame.seq_num() then
|
|
||||||
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
log.warning(log_header .. "sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.r_seq_num = pkt.scada_frame.seq_num()
|
self.r_seq_num = pkt.scada_frame.seq_num() + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- feed watchdog
|
-- feed watchdog
|
||||||
|
@ -273,11 +273,12 @@ end
|
|||||||
|
|
||||||
-- establish a new PLC session
|
-- establish a new PLC session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer PLC computer ID
|
||||||
---@param for_reactor integer
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
---@param version string
|
---@param for_reactor integer unit ID
|
||||||
|
---@param version string PLC version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
function svsessions.establish_plc_session(source_addr, i_seq_num, for_reactor, version)
|
||||||
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.config.UnitCount then
|
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.config.UnitCount then
|
||||||
---@class plc_session_struct
|
---@class plc_session_struct
|
||||||
local plc_s = {
|
local plc_s = {
|
||||||
@ -294,7 +295,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
|||||||
|
|
||||||
local id = self.next_ids.plc
|
local id = self.next_ids.plc
|
||||||
|
|
||||||
plc_s.instance = plc.new_session(id, source_addr, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
plc_s.instance = plc.new_session(id, source_addr, i_seq_num, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
||||||
table.insert(self.sessions.plc, plc_s)
|
table.insert(self.sessions.plc, plc_s)
|
||||||
|
|
||||||
local units = self.facility.get_units()
|
local units = self.facility.get_units()
|
||||||
@ -320,13 +321,14 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- establish a new RTU session
|
-- establish a new RTU gateway session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer RTU gateway computer ID
|
||||||
---@param advertisement table
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
---@param version string
|
---@param advertisement table RTU capability advertisement
|
||||||
|
---@param version string RTU gateway version
|
||||||
---@return integer session_id
|
---@return integer session_id
|
||||||
function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
function svsessions.establish_rtu_session(source_addr, i_seq_num, advertisement, version)
|
||||||
---@class rtu_session_struct
|
---@class rtu_session_struct
|
||||||
local rtu_s = {
|
local rtu_s = {
|
||||||
s_type = "rtu",
|
s_type = "rtu",
|
||||||
@ -341,7 +343,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
|||||||
|
|
||||||
local id = self.next_ids.rtu
|
local id = self.next_ids.rtu
|
||||||
|
|
||||||
rtu_s.instance = rtu.new_session(id, source_addr, rtu_s.in_queue, rtu_s.out_queue, self.config.RTU_Timeout, advertisement, self.facility, self.fp_ok)
|
rtu_s.instance = rtu.new_session(id, source_addr, i_seq_num, rtu_s.in_queue, rtu_s.out_queue, self.config.RTU_Timeout, advertisement, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.rtu, rtu_s)
|
table.insert(self.sessions.rtu, rtu_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
@ -362,10 +364,11 @@ end
|
|||||||
|
|
||||||
-- establish a new coordinator session
|
-- establish a new coordinator session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer coordinator computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string coordinator version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_crd_session(source_addr, version)
|
function svsessions.establish_crd_session(source_addr, i_seq_num, version)
|
||||||
if svsessions.get_crd_session() == nil then
|
if svsessions.get_crd_session() == nil then
|
||||||
---@class crd_session_struct
|
---@class crd_session_struct
|
||||||
local crd_s = {
|
local crd_s = {
|
||||||
@ -381,7 +384,7 @@ function svsessions.establish_crd_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_ids.crd
|
local id = self.next_ids.crd
|
||||||
|
|
||||||
crd_s.instance = coordinator.new_session(id, source_addr, crd_s.in_queue, crd_s.out_queue, self.config.CRD_Timeout, self.facility, self.fp_ok)
|
crd_s.instance = coordinator.new_session(id, source_addr, i_seq_num, crd_s.in_queue, crd_s.out_queue, self.config.CRD_Timeout, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.crd, crd_s)
|
table.insert(self.sessions.crd, crd_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
@ -406,10 +409,11 @@ end
|
|||||||
|
|
||||||
-- establish a new pocket diagnostics session
|
-- establish a new pocket diagnostics session
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param source_addr integer
|
---@param source_addr integer pocket computer ID
|
||||||
---@param version string
|
---@param i_seq_num integer initial (most recent) sequence number
|
||||||
|
---@param version string pocket version
|
||||||
---@return integer|false session_id
|
---@return integer|false session_id
|
||||||
function svsessions.establish_pdg_session(source_addr, version)
|
function svsessions.establish_pdg_session(source_addr, i_seq_num, version)
|
||||||
---@class pdg_session_struct
|
---@class pdg_session_struct
|
||||||
local pdg_s = {
|
local pdg_s = {
|
||||||
s_type = "pkt",
|
s_type = "pkt",
|
||||||
@ -424,7 +428,7 @@ function svsessions.establish_pdg_session(source_addr, version)
|
|||||||
|
|
||||||
local id = self.next_ids.pdg
|
local id = self.next_ids.pdg
|
||||||
|
|
||||||
pdg_s.instance = pocket.new_session(id, source_addr, pdg_s.in_queue, pdg_s.out_queue, self.config.PKT_Timeout, self.facility, self.fp_ok)
|
pdg_s.instance = pocket.new_session(id, source_addr, i_seq_num, pdg_s.in_queue, pdg_s.out_queue, self.config.PKT_Timeout, self.facility, self.fp_ok)
|
||||||
table.insert(self.sessions.pdg, pdg_s)
|
table.insert(self.sessions.pdg, pdg_s)
|
||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
|
@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v1.3.12"
|
local SUPERVISOR_VERSION = "v1.4.0"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -214,7 +214,7 @@ local function main()
|
|||||||
elseif event == "modem_message" then
|
elseif event == "modem_message" then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
superv_comms.handle_packet(packet)
|
if packet then superv_comms.handle_packet(packet) end
|
||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" or
|
||||||
event == "double_click" then
|
event == "double_click" then
|
||||||
-- handle a mouse event
|
-- handle a mouse event
|
||||||
|
@ -191,13 +191,13 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- handle a packet
|
-- handle a packet
|
||||||
---@param packet modbus_frame|rplc_frame|mgmt_frame|crdn_frame|nil
|
---@param packet modbus_frame|rplc_frame|mgmt_frame|crdn_frame
|
||||||
function public.handle_packet(packet)
|
function public.handle_packet(packet)
|
||||||
if packet ~= nil then
|
|
||||||
local l_chan = packet.scada_frame.local_channel()
|
local l_chan = packet.scada_frame.local_channel()
|
||||||
local r_chan = packet.scada_frame.remote_channel()
|
local r_chan = packet.scada_frame.remote_channel()
|
||||||
local src_addr = packet.scada_frame.src_addr()
|
local src_addr = packet.scada_frame.src_addr()
|
||||||
local protocol = packet.scada_frame.protocol()
|
local protocol = packet.scada_frame.protocol()
|
||||||
|
local i_seq_num = packet.scada_frame.seq_num()
|
||||||
|
|
||||||
if l_chan ~= config.SVR_Channel then
|
if l_chan ~= config.SVR_Channel then
|
||||||
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
||||||
@ -241,7 +241,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
-- PLC linking request
|
-- PLC linking request
|
||||||
if packet.length == 4 and type(packet.data[4]) == "number" then
|
if packet.length == 4 and type(packet.data[4]) == "number" then
|
||||||
local reactor_id = packet.data[4]
|
local reactor_id = packet.data[4]
|
||||||
local plc_id = svsessions.establish_plc_session(src_addr, reactor_id, firmware_v)
|
local plc_id = svsessions.establish_plc_session(src_addr, i_seq_num, reactor_id, firmware_v)
|
||||||
|
|
||||||
if plc_id == false then
|
if plc_id == false then
|
||||||
-- reactor already has a PLC assigned
|
-- reactor already has a PLC assigned
|
||||||
@ -315,7 +315,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
if packet.length == 4 then
|
if packet.length == 4 then
|
||||||
-- this is an RTU advertisement for a new session
|
-- this is an RTU advertisement for a new session
|
||||||
local rtu_advert = packet.data[4]
|
local rtu_advert = packet.data[4]
|
||||||
local s_id = svsessions.establish_rtu_session(src_addr, rtu_advert, firmware_v)
|
local s_id = svsessions.establish_rtu_session(src_addr, i_seq_num, rtu_advert, firmware_v)
|
||||||
|
|
||||||
println(util.c("RTU (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
println(util.c("RTU (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
log.info(util.c("RTU_ESTABLISH: RTU (",firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||||
@ -367,7 +367,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
elseif dev_type == DEVICE_TYPE.CRD 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, i_seq_num, firmware_v)
|
||||||
|
|
||||||
if s_id ~= false then
|
if s_id ~= false then
|
||||||
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
@ -434,7 +434,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
elseif dev_type == DEVICE_TYPE.PKT then
|
elseif dev_type == DEVICE_TYPE.PKT then
|
||||||
-- this is an attempt to establish a new pocket diagnostic session
|
-- this is an attempt to establish a new pocket diagnostic session
|
||||||
local s_id = svsessions.establish_pdg_session(src_addr, firmware_v)
|
local s_id = svsessions.establish_pdg_session(src_addr, i_seq_num, firmware_v)
|
||||||
|
|
||||||
println(util.c("PKT (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
println(util.c("PKT (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||||
log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
log.info(util.c("PDG_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||||
@ -469,7 +469,6 @@ function supervisor.comms(_version, nic, fp_ok)
|
|||||||
log.debug("received packet for unknown channel " .. r_chan, true)
|
log.debug("received packet for unknown channel " .. r_chan, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user