diff --git a/install_manifest.json b/install_manifest.json new file mode 100644 index 0000000..819e6ee --- /dev/null +++ b/install_manifest.json @@ -0,0 +1 @@ +{"versions": {"installer": "v1.2", "bootloader": "0.2", "comms": "1.4.1", "reactor-plc": "v1.3.6", "rtu": "v1.2.6", "supervisor": "v0.16.6", "coordinator": "v0.15.6", "pocket": "alpha-v0.3.6"}, "files": {"system": ["initenv.lua", "startup.lua"], "common": ["scada-common/crypto.lua", "scada-common/ppm.lua", "scada-common/comms.lua", "scada-common/psil.lua", "scada-common/tcallbackdsp.lua", "scada-common/rsio.lua", "scada-common/constants.lua", "scada-common/mqueue.lua", "scada-common/crash.lua", "scada-common/log.lua", "scada-common/types.lua", "scada-common/util.lua"], "graphics": ["graphics/element.lua", "graphics/events.lua", "graphics/flasher.lua", "graphics/core.lua", "graphics/elements/listbox.lua", "graphics/elements/textbox.lua", "graphics/elements/displaybox.lua", "graphics/elements/pipenet.lua", "graphics/elements/rectangle.lua", "graphics/elements/div.lua", "graphics/elements/multipane.lua", "graphics/elements/tiling.lua", "graphics/elements/colormap.lua", "graphics/elements/indicators/alight.lua", "graphics/elements/indicators/icon.lua", "graphics/elements/indicators/power.lua", "graphics/elements/indicators/rad.lua", "graphics/elements/indicators/state.lua", "graphics/elements/indicators/light.lua", "graphics/elements/indicators/vbar.lua", "graphics/elements/indicators/led.lua", "graphics/elements/indicators/coremap.lua", "graphics/elements/indicators/data.lua", "graphics/elements/indicators/ledpair.lua", "graphics/elements/indicators/hbar.lua", "graphics/elements/indicators/trilight.lua", "graphics/elements/indicators/ledrgb.lua", "graphics/elements/controls/switch_button.lua", "graphics/elements/controls/spinbox_numeric.lua", "graphics/elements/controls/hazard_button.lua", "graphics/elements/controls/push_button.lua", "graphics/elements/controls/radio_button.lua", "graphics/elements/controls/multi_button.lua", "graphics/elements/controls/tabbar.lua", "graphics/elements/controls/sidebar.lua", "graphics/elements/animations/waiting.lua"], "lockbox": ["lockbox/init.lua", "lockbox/LICENSE", "lockbox/kdf/pbkdf2.lua", "lockbox/util/bit.lua", "lockbox/util/array.lua", "lockbox/util/stream.lua", "lockbox/util/queue.lua", "lockbox/digest/sha2_224.lua", "lockbox/digest/sha1.lua", "lockbox/digest/sha2_256.lua", "lockbox/cipher/aes128.lua", "lockbox/cipher/aes256.lua", "lockbox/cipher/aes192.lua", "lockbox/cipher/mode/ofb.lua", "lockbox/cipher/mode/cbc.lua", "lockbox/cipher/mode/ctr.lua", "lockbox/cipher/mode/cfb.lua", "lockbox/mac/hmac.lua", "lockbox/padding/ansix923.lua", "lockbox/padding/pkcs7.lua", "lockbox/padding/zero.lua", "lockbox/padding/isoiec7816.lua"], "reactor-plc": ["reactor-plc/renderer.lua", "reactor-plc/threads.lua", "reactor-plc/databus.lua", "reactor-plc/plc.lua", "reactor-plc/config.lua", "reactor-plc/startup.lua", "reactor-plc/panel/front_panel.lua", "reactor-plc/panel/style.lua"], "rtu": ["rtu/renderer.lua", "rtu/threads.lua", "rtu/rtu.lua", "rtu/databus.lua", "rtu/modbus.lua", "rtu/config.lua", "rtu/startup.lua", "rtu/panel/front_panel.lua", "rtu/panel/style.lua", "rtu/dev/sps_rtu.lua", "rtu/dev/envd_rtu.lua", "rtu/dev/boilerv_rtu.lua", "rtu/dev/redstone_rtu.lua", "rtu/dev/sna_rtu.lua", "rtu/dev/imatrix_rtu.lua", "rtu/dev/turbinev_rtu.lua"], "supervisor": ["supervisor/renderer.lua", "supervisor/databus.lua", "supervisor/supervisor.lua", "supervisor/unit.lua", "supervisor/config.lua", "supervisor/startup.lua", "supervisor/unitlogic.lua", "supervisor/facility.lua", "supervisor/panel/pgi.lua", "supervisor/panel/front_panel.lua", "supervisor/panel/style.lua", "supervisor/panel/components/rtu_entry.lua", "supervisor/panel/components/pdg_entry.lua", "supervisor/session/coordinator.lua", "supervisor/session/svqtypes.lua", "supervisor/session/pocket.lua", "supervisor/session/svsessions.lua", "supervisor/session/rtu.lua", "supervisor/session/plc.lua", "supervisor/session/rsctl.lua", "supervisor/session/rtu/boilerv.lua", "supervisor/session/rtu/txnctrl.lua", "supervisor/session/rtu/unit_session.lua", "supervisor/session/rtu/turbinev.lua", "supervisor/session/rtu/envd.lua", "supervisor/session/rtu/imatrix.lua", "supervisor/session/rtu/sps.lua", "supervisor/session/rtu/qtypes.lua", "supervisor/session/rtu/sna.lua", "supervisor/session/rtu/redstone.lua"], "coordinator": ["coordinator/coordinator.lua", "coordinator/renderer.lua", "coordinator/iocontrol.lua", "coordinator/sounder.lua", "coordinator/config.lua", "coordinator/startup.lua", "coordinator/process.lua", "coordinator/ui/dialog.lua", "coordinator/ui/style.lua", "coordinator/ui/layout/main_view.lua", "coordinator/ui/layout/unit_view.lua", "coordinator/ui/components/reactor.lua", "coordinator/ui/components/processctl.lua", "coordinator/ui/components/unit_overview.lua", "coordinator/ui/components/boiler.lua", "coordinator/ui/components/unit_detail.lua", "coordinator/ui/components/imatrix.lua", "coordinator/ui/components/turbine.lua", "coordinator/session/api.lua", "coordinator/session/apisessions.lua"], "pocket": ["pocket/pocket.lua", "pocket/renderer.lua", "pocket/config.lua", "pocket/coreio.lua", "pocket/startup.lua", "pocket/ui/main.lua", "pocket/ui/style.lua", "pocket/ui/components/conn_waiting.lua", "pocket/ui/pages/turbine_page.lua", "pocket/ui/pages/reactor_page.lua", "pocket/ui/pages/home_page.lua", "pocket/ui/pages/unit_page.lua", "pocket/ui/pages/boiler_page.lua"]}, "depends": {"reactor-plc": ["system", "common", "graphics"], "rtu": ["system", "common", "graphics"], "supervisor": ["system", "common"], "coordinator": ["system", "common", "graphics"], "pocket": ["system", "common", "graphics"]}, "sizes": {"manifest": 5775, "system": 1991, "common": 91102, "graphics": 144089, "lockbox": 100797, "reactor-plc": 95896, "rtu": 100988, "supervisor": 310870, "coordinator": 197544, "pocket": 36338}} \ No newline at end of file diff --git a/supervisor/panel/front_panel.lua b/supervisor/panel/front_panel.lua index 86d9839..8ebc448 100644 --- a/supervisor/panel/front_panel.lua +++ b/supervisor/panel/front_panel.lua @@ -18,21 +18,16 @@ local core = require("graphics.core") local Div = require("graphics.elements.div") local ListBox = require("graphics.elements.listbox") local MultiPane = require("graphics.elements.multipane") -local Rectangle = require("graphics.elements.rectangle") local TextBox = require("graphics.elements.textbox") -local PushButton = require("graphics.elements.controls.push_button") local TabBar = require("graphics.elements.controls.tabbar") local LED = require("graphics.elements.indicators.led") -local LEDPair = require("graphics.elements.indicators.ledpair") -local RGBLED = require("graphics.elements.indicators.ledrgb") local DataIndicator = require("graphics.elements.indicators.data") local TEXT_ALIGN = core.TEXT_ALIGN local cpair = core.cpair -local border = core.border -- create new main view ---@param panel graphics_element main displaybox diff --git a/supervisor/panel/pgi.lua b/supervisor/panel/pgi.lua index 4126dee..9065f72 100644 --- a/supervisor/panel/pgi.lua +++ b/supervisor/panel/pgi.lua @@ -28,6 +28,14 @@ function pgi.link_elements(rtu_list, rtu_entry, pdg_list, pdg_entry) data.pdg_entry = pdg_entry end +-- unlink all fields, disabling the PGI +function pgi.unlink() + data.rtu_list = nil + data.pdg_list = nil + data.rtu_entry = nil + data.pdg_entry = nil +end + -- add an RTU entry to the RTU list ---@param session_id integer RTU session function pgi.create_rtu_entry(session_id) diff --git a/supervisor/renderer.lua b/supervisor/renderer.lua index 5dfb7d1..1bc70a4 100644 --- a/supervisor/renderer.lua +++ b/supervisor/renderer.lua @@ -3,6 +3,7 @@ -- local panel_view = require("supervisor.panel.front_panel") +local pgi = require("supervisor.panel.pgi") local style = require("supervisor.panel.style") local flasher = require("graphics.flasher") @@ -44,6 +45,9 @@ function renderer.close_ui() -- stop blinking indicators flasher.clear() + -- disable PGI + pgi.unlink() + -- hide to stop animation callbacks ui.display.hide() diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index 03a579d..36a5241 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -20,9 +20,6 @@ local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE local SV_Q_DATA = svqtypes.SV_Q_DATA --- local println = util.println -local println = function (str) end - -- retry time constants in ms -- local INITIAL_WAIT = 1500 local RETRY_PERIOD = 1000 @@ -52,7 +49,11 @@ local PERIODICS = { ---@param out_queue mqueue out message queue ---@param timeout number communications timeout ---@param facility facility facility data table -function coordinator.new_session(id, in_queue, out_queue, timeout, facility) +---@param fp_ok boolean if the front panel UI is running +function coordinator.new_session(id, in_queue, out_queue, timeout, facility, fp_ok) + -- print a log message to the terminal as long as the UI isn't running + local function println(message) if not fp_ok then util.println_ts(message) end end + local log_header = "crdn_session(" .. id .. "): " local self = { diff --git a/supervisor/session/plc.lua b/supervisor/session/plc.lua index 0fb987a..1436534 100644 --- a/supervisor/session/plc.lua +++ b/supervisor/session/plc.lua @@ -16,9 +16,6 @@ local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local PLC_AUTO_ACK = comms.PLC_AUTO_ACK local UNIT_COMMAND = comms.UNIT_COMMAND --- local println = util.println -local println = function (str) end - -- retry time constants in ms local INITIAL_WAIT = 1500 local INITIAL_AUTO_WAIT = 1000 @@ -52,7 +49,11 @@ local PERIODICS = { ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout -function plc.new_session(id, reactor_id, in_queue, out_queue, timeout) +---@param fp_ok boolean if the front panel UI is running +function plc.new_session(id, reactor_id, in_queue, out_queue, timeout, fp_ok) + -- print a log message to the terminal as long as the UI isn't running + local function println(message) if not fp_ok then util.println_ts(message) end end + local log_header = "plc_session(" .. id .. "): " local self = { diff --git a/supervisor/session/pocket.lua b/supervisor/session/pocket.lua index 175a235..ba6d179 100644 --- a/supervisor/session/pocket.lua +++ b/supervisor/session/pocket.lua @@ -9,9 +9,6 @@ local pocket = {} local PROTOCOL = comms.PROTOCOL local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE --- local println = util.println -local println = function (str) end - -- retry time constants in ms -- local INITIAL_WAIT = 1500 -- local RETRY_PERIOD = 1000 @@ -35,7 +32,11 @@ local PERIODICS = { ---@param in_queue mqueue in message queue ---@param out_queue mqueue out message queue ---@param timeout number communications timeout -function pocket.new_session(id, in_queue, out_queue, timeout) +---@param fp_ok boolean if the front panel UI is running +function pocket.new_session(id, in_queue, out_queue, timeout, fp_ok) + -- print a log message to the terminal as long as the UI isn't running + local function println(message) if not fp_ok then util.println_ts(message) end end + local log_header = "pdg_session(" .. id .. "): " local self = { diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 40b47ed..2f7091c 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -24,9 +24,6 @@ local PROTOCOL = comms.PROTOCOL local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE --- local println = util.println -local println = function (str) end - local PERIODICS = { KEEP_ALIVE = 2000 } @@ -39,7 +36,11 @@ local PERIODICS = { ---@param timeout number communications timeout ---@param advertisement table RTU device advertisement ---@param facility facility facility data table -function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facility) +---@param fp_ok boolean if the front panel UI is running +function rtu.new_session(id, in_queue, out_queue, timeout, advertisement, facility, fp_ok) + -- print a log message to the terminal as long as the UI isn't running + local function println(message) if not fp_ok then util.println_ts(message) end end + local log_header = "rtu_session(" .. id .. "): " local self = { diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index ecd8928..a41658a 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -35,6 +35,7 @@ svsessions.SESSION_TYPE = SESSION_TYPE local self = { modem = nil, ---@type table|nil + fp_ok = false, num_reactors = 0, facility = nil, ---@type facility|nil sessions = { rtu = {}, plc = {}, coord = {}, pdg = {} }, @@ -196,11 +197,13 @@ end -- PUBLIC FUNCTIONS -- -- initialize svsessions ----@param modem table ----@param num_reactors integer ----@param cooling_conf table -function svsessions.init(modem, num_reactors, cooling_conf) +---@param modem table modem device +---@param fp_ok boolean front panel active +---@param num_reactors integer number of reactors +---@param cooling_conf table cooling configuration definition +function svsessions.init(modem, fp_ok, num_reactors, cooling_conf) self.modem = modem + self.fp_ok = fp_ok self.num_reactors = num_reactors self.facility = facility.new(num_reactors, cooling_conf) end @@ -308,13 +311,15 @@ function svsessions.establish_plc_session(local_port, remote_port, for_reactor, instance = nil ---@type plc_session } - plc_s.instance = plc.new_session(self.next_ids.plc, for_reactor, plc_s.in_queue, plc_s.out_queue, config.PLC_TIMEOUT) + plc_s.instance = plc.new_session(self.next_ids.plc, for_reactor, plc_s.in_queue, plc_s.out_queue, + config.PLC_TIMEOUT, self.fp_ok) table.insert(self.sessions.plc, plc_s) local units = self.facility.get_units() units[for_reactor].link_plc_session(plc_s) - log.debug(util.c("established new PLC session to ", remote_port, " with ID ", self.next_ids.plc, " for reactor ", for_reactor)) + log.debug(util.c("established new PLC session to ", remote_port, " with ID ", self.next_ids.plc, + " for reactor ", for_reactor)) databus.tx_plc_connected(for_reactor, version, remote_port) @@ -348,7 +353,8 @@ function svsessions.establish_rtu_session(local_port, remote_port, advertisement instance = nil ---@type rtu_session } - rtu_s.instance = rtu.new_session(self.next_ids.rtu, rtu_s.in_queue, rtu_s.out_queue, config.RTU_TIMEOUT, advertisement, self.facility) + rtu_s.instance = rtu.new_session(self.next_ids.rtu, rtu_s.in_queue, rtu_s.out_queue, config.RTU_TIMEOUT, advertisement, + self.facility, self.fp_ok) table.insert(self.sessions.rtu, rtu_s) log.debug("established new RTU session to " .. remote_port .. " with ID " .. self.next_ids.rtu) @@ -381,7 +387,8 @@ function svsessions.establish_coord_session(local_port, remote_port, version) instance = nil ---@type coord_session } - coord_s.instance = coordinator.new_session(self.next_ids.coord, coord_s.in_queue, coord_s.out_queue, config.CRD_TIMEOUT, self.facility) + coord_s.instance = coordinator.new_session(self.next_ids.coord, coord_s.in_queue, coord_s.out_queue, config.CRD_TIMEOUT, + self.facility, self.fp_ok) table.insert(self.sessions.coord, coord_s) log.debug("established new coordinator session to " .. remote_port .. " with ID " .. self.next_ids.coord) @@ -417,7 +424,7 @@ function svsessions.establish_pdg_session(local_port, remote_port, version) instance = nil ---@type pdg_session } - pdg_s.instance = pocket.new_session(self.next_ids.pdg, pdg_s.in_queue, pdg_s.out_queue, config.PKT_TIMEOUT) + pdg_s.instance = pocket.new_session(self.next_ids.pdg, pdg_s.in_queue, pdg_s.out_queue, config.PKT_TIMEOUT, self.fp_ok) table.insert(self.sessions.pdg, pdg_s) log.debug("established new pocket diagnostics session to " .. remote_port .. " with ID " .. self.next_ids.pdg) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index cdae63e..d0f95a8 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -108,93 +108,104 @@ local function main() println_ts(util.c("UI error: ", message)) log.error(util.c("GUI crashed with error ", message)) else - -- start comms, open all channels - local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem, - config.SCADA_DEV_LISTEN, config.SCADA_SV_CTL_LISTEN, config.TRUSTED_RANGE) - - -- base loop clock (6.67Hz, 3 ticks) - local MAIN_CLOCK = 0.15 - local loop_clock = util.new_clock(MAIN_CLOCK) - - -- start clock - loop_clock.start() - - -- event loop - while true do - local event, param1, param2, param3, param4, param5 = util.pull_event() - - -- handle event - if event == "peripheral_detach" then - local type, device = ppm.handle_unmount(param1) - - if type ~= nil and device ~= nil then - if type == "modem" then - -- we only care if this is our wireless modem - if device == modem then - log.warning("comms modem disconnected") - databus.tx_hw_modem(false) - else - log.warning("non-comms modem disconnected") - end - end - end - elseif event == "peripheral" then - local type, device = ppm.mount(param1) - - if type ~= nil and device ~= nil then - if type == "modem" then - if device.isWireless() then - -- reconnected modem - modem = device - superv_comms.reconnect_modem(modem) - - log.info("comms modem reconnected") - - databus.tx_hw_modem(true) - else - log.info("wired modem reconnected") - end - end - end - elseif event == "timer" and loop_clock.is_clock(param1) then - -- main loop tick - databus.heartbeat() - - -- iterate sessions - svsessions.iterate_all() - - -- free any closed sessions - svsessions.free_all_closed() - - loop_clock.start() - elseif event == "timer" then - -- a non-clock timer event, check watchdogs - svsessions.check_all_watchdogs(param1) - - -- notify timer callback dispatcher - tcd.handle(param1) - elseif event == "modem_message" then - -- got a packet - local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5) - superv_comms.handle_packet(packet) - elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then - -- handle a mouse event - renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) - end - - -- check for termination request - if event == "terminate" or ppm.should_terminate() then - log.info("terminate requested, closing sessions...") - svsessions.close_all() - log.info("sessions closed") - break - end - end - - renderer.close_ui() + -- redefine println_ts local to not print as we have the front panel running + println_ts = function (_) end end - println_ts("exited") + -- start comms, open all channels + local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem, + config.SCADA_DEV_LISTEN, config.SCADA_SV_CTL_LISTEN, config.TRUSTED_RANGE, fp_ok) + + -- base loop clock (6.67Hz, 3 ticks) + local MAIN_CLOCK = 0.15 + local loop_clock = util.new_clock(MAIN_CLOCK) + + -- start clock + loop_clock.start() + + -- halve the rate heartbeat LED flash + local heartbeat_toggle = true + + -- event loop + while true do + local event, param1, param2, param3, param4, param5 = util.pull_event() + + -- handle event + if event == "peripheral_detach" then + local type, device = ppm.handle_unmount(param1) + + if type ~= nil and device ~= nil then + if type == "modem" then + -- we only care if this is our wireless modem + if device == modem then + println_ts("wireless modem disconnected!") + log.warning("comms modem disconnected") + databus.tx_hw_modem(false) + else + log.warning("non-comms modem disconnected") + end + end + end + elseif event == "peripheral" then + local type, device = ppm.mount(param1) + + if type ~= nil and device ~= nil then + if type == "modem" then + if device.isWireless() then + -- reconnected modem + modem = device + superv_comms.reconnect_modem(modem) + + println_ts("wireless modem reconnected.") + log.info("comms modem reconnected") + + databus.tx_hw_modem(true) + else + log.info("wired modem reconnected") + end + end + end + elseif event == "timer" and loop_clock.is_clock(param1) then + -- main loop tick + + if heartbeat_toggle then databus.heartbeat() end + heartbeat_toggle = not heartbeat_toggle + + -- iterate sessions + svsessions.iterate_all() + + -- free any closed sessions + svsessions.free_all_closed() + + loop_clock.start() + elseif event == "timer" then + -- a non-clock timer event, check watchdogs + svsessions.check_all_watchdogs(param1) + + -- notify timer callback dispatcher + tcd.handle(param1) + elseif event == "modem_message" then + -- got a packet + local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5) + superv_comms.handle_packet(packet) + elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then + -- handle a mouse event + renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3)) + end + + -- check for termination request + if event == "terminate" or ppm.should_terminate() then + println_ts("closing sesssions...") + log.info("terminate requested, closing sessions...") + svsessions.close_all() + log.info("sessions closed") + break + end + end + + renderer.close_ui() + + util.println_ts("exited") log.info("exited") end diff --git a/supervisor/supervisor.lua b/supervisor/supervisor.lua index 0424b53..6a27c0e 100644 --- a/supervisor/supervisor.lua +++ b/supervisor/supervisor.lua @@ -11,9 +11,6 @@ local DEVICE_TYPE = comms.DEVICE_TYPE local ESTABLISH_ACK = comms.ESTABLISH_ACK local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE --- local println = util.println -local println = function (str) end - -- supervisory controller communications ---@nodiscard ---@param _version string supervisor version @@ -23,8 +20,12 @@ local println = function (str) end ---@param dev_listen integer listening port for PLC/RTU devices ---@param svctl_listen integer listening port for supervisor access ---@param range integer trusted device connection range +---@param fp_ok boolean if the front panel UI is running ---@diagnostic disable-next-line: unused-local -function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_listen, svctl_listen, range) +function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_listen, svctl_listen, range, fp_ok) + -- print a log message to the terminal as long as the UI isn't running + local function println(message) if not fp_ok then util.println_ts(message) end end + local self = { last_est_acks = {} } @@ -42,8 +43,8 @@ function supervisor.comms(_version, num_reactors, cooling_conf, modem, dev_liste _conf_channels() - -- link modem to svsessions - svsessions.init(modem, num_reactors, cooling_conf) + -- pass modem, status, and config data to svsessions + svsessions.init(modem, fp_ok, num_reactors, cooling_conf) -- send an establish request response to a PLC/RTU ---@param dest integer