From e54d5b3d85c44addf41314c2d0e54d4a293d021b Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 25 Jun 2022 13:39:47 -0400 Subject: [PATCH] #74 coordinator comms and work on database --- coordinator/apisessions.lua | 7 ++ coordinator/coordinator.lua | 166 ++++++++++++++++++++++-------------- scada-common/comms.lua | 10 ++- 3 files changed, 114 insertions(+), 69 deletions(-) create mode 100644 coordinator/apisessions.lua diff --git a/coordinator/apisessions.lua b/coordinator/apisessions.lua new file mode 100644 index 0000000..2141e77 --- /dev/null +++ b/coordinator/apisessions.lua @@ -0,0 +1,7 @@ +local apisessions = {} + +---@param packet capi_frame +function apisessions.handle_packet(packet) +end + +return apisessions diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 5dbb39e..c917681 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -1,13 +1,19 @@ local comms = require("scada-common.comms") -local log = require("scada-common.log") -local ppm = require("scada-common.ppm") -local util = require("scada-common.util") +local log = require("scada-common.log") +local ppm = require("scada-common.ppm") +local psil = require("scada-common.psil") +local util = require("scada-common.util") + +local apisessions = require("coordinator.apisessions") local dialog = require("coordinator.util.dialog") local coordinator = {} +---@class coord_db +local db = {} + local print = util.print local println = util.println local print_ts = util.print_ts @@ -142,12 +148,43 @@ function coordinator.configure_monitors(num_units) return true, monitors end +-- initialize the coordinator database +---@param num_units integer number of units expected +function coordinator.init_database(num_units) + db.facility = { + num_units = num_units, + ps = psil.create() + } + + db.units = {} + for i = 1, num_units do + table.insert(db.units, { + uint_id = i, + initialized = false, + + reactor_ps = psil.create(), + reactor_data = {}, + + boiler_ps_tbl = {}, + boiler_data_tbl = {}, + + turbine_ps_tbl = {}, + turbine_data_tbl = {} + }) + end +end + -- coordinator communications ----@param conn_watchdog watchdog -function coordinator.coord_comms(version, num_reactors, modem, sv_port, sv_listen, api_listen, conn_watchdog) +---@param version string +---@param modem table +---@param sv_port integer +---@param sv_listen integer +---@param api_listen integer +---@param sv_watchdog watchdog +function coordinator.coord_comms(version, modem, sv_port, sv_listen, api_listen, sv_watchdog) local self = { - seq_num = 0, - r_seq_num = nil, + sv_seq_num = 0, + sv_r_seq_num = nil, modem = modem, connected = false } @@ -171,32 +208,26 @@ function coordinator.coord_comms(version, num_reactors, modem, sv_port, sv_liste -- open at construct time _open_channels() - -- send a coordinator packet - ---@param msg_type COORD_TYPES - ---@param msg string - local function _send(msg_type, msg) + -- send a packet to the supervisor + ---@param msg_type SCADA_MGMT_TYPES|COORD_TYPES + ---@param msg table + local function _send_sv(protocol, msg_type, msg) local s_pkt = comms.scada_packet() - local c_pkt = comms.coord_packet() + local pkt = nil ---@type mgmt_packet|coord_packet - c_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOLS.COORD_DATA, c_pkt.raw_sendable()) + if protocol == PROTOCOLS.SCADA_MGMT then + pkt = comms.mgmt_packet() + elseif protocol == PROTOCOLS.COORD_DATA then + pkt = comms.coord_packet() + else + return + end + + pkt.make(msg_type, msg) + s_pkt.make(self.sv_seq_num, protocol, pkt.raw_sendable()) self.modem.transmit(sv_port, sv_listen, s_pkt.raw_sendable()) - self.seq_num = self.seq_num + 1 - end - - -- send a SCADA management packet - ---@param msg_type SCADA_MGMT_TYPES - ---@param msg string - local function _send_mgmt(msg_type, msg) - local s_pkt = comms.scada_packet() - local m_pkt = comms.mgmt_packet() - - m_pkt.make(msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOLS.SCADA_MGMT, m_pkt.raw_sendable()) - - self.modem.transmit(sv_port, sv_listen, s_pkt.raw_sendable()) - self.seq_num = self.seq_num + 1 + self.sv_seq_num = self.sv_seq_num + 1 end -- PUBLIC FUNCTIONS -- @@ -254,46 +285,49 @@ function coordinator.coord_comms(version, num_reactors, modem, sv_port, sv_liste ---@param packet mgmt_frame|coord_frame|capi_frame function public.handle_packet(packet) if packet ~= nil then - -- check sequence number - if self.r_seq_num == nil then - self.r_seq_num = packet.scada_frame.seq_num() - elseif self.connected and self.r_seq_num >= packet.scada_frame.seq_num() then - log.warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) - return - else - self.r_seq_num = packet.scada_frame.seq_num() - end - - -- feed watchdog on valid sequence number - conn_watchdog.feed() - local protocol = packet.scada_frame.protocol() - -- handle packet - if protocol == PROTOCOLS.COORD_DATA then - if packet.type == COORD_TYPES.ESTABLISH then - elseif packet.type == COORD_TYPES.QUERY_UNIT then - elseif packet.type == COORD_TYPES.QUERY_FACILITY then - elseif packet.type == COORD_TYPES.COMMAND_UNIT then - elseif packet.type == COORD_TYPES.ALARM then - else - log.warning("received unknown coordinator data packet type " .. packet.type) - end - elseif protocol == PROTOCOLS.COORD_API then - elseif protocol == PROTOCOLS.SCADA_MGMT then - if packet.type == SCADA_MGMT_TYPES.KEEP_ALIVE then - -- keep alive response received - elseif packet.type == SCADA_MGMT_TYPES.CLOSE then - -- handle session close - conn_watchdog.cancel() - println_ts("server connection closed by remote host") - log.warning("server connection closed by remote host") - else - log.warning("received unknown SCADA_MGMT packet type " .. packet.type) - end + if protocol == PROTOCOLS.COORD_API then + apisessions.handle_packet(packet) else - -- should be unreachable assuming packet is from parse_packet() - log.error("illegal packet type " .. protocol, true) + -- check sequence number + if self.sv_r_seq_num == nil then + self.sv_r_seq_num = packet.scada_frame.seq_num() + elseif self.connected and self.sv_r_seq_num >= packet.scada_frame.seq_num() then + log.warning("sequence out-of-order: last = " .. self.sv_r_seq_num .. ", new = " .. packet.scada_frame.seq_num()) + return + else + self.sv_r_seq_num = packet.scada_frame.seq_num() + end + + -- feed watchdog on valid sequence number + sv_watchdog.feed() + + -- handle packet + if protocol == PROTOCOLS.COORD_DATA then + if packet.type == COORD_TYPES.ESTABLISH then + elseif packet.type == COORD_TYPES.QUERY_UNIT then + elseif packet.type == COORD_TYPES.QUERY_FACILITY then + elseif packet.type == COORD_TYPES.COMMAND_UNIT then + elseif packet.type == COORD_TYPES.ALARM then + else + log.warning("received unknown COORD_DATA packet type " .. packet.type) + end + elseif protocol == PROTOCOLS.SCADA_MGMT then + if packet.type == SCADA_MGMT_TYPES.KEEP_ALIVE then + -- keep alive response received + elseif packet.type == SCADA_MGMT_TYPES.CLOSE then + -- handle session close + sv_watchdog.cancel() + println_ts("server connection closed by remote host") + log.warning("server connection closed by remote host") + else + log.warning("received unknown SCADA_MGMT packet type " .. packet.type) + end + else + -- should be unreachable assuming packet is from parse_packet() + log.error("illegal packet type " .. protocol, true) + end end end end diff --git a/scada-common/comms.lua b/scada-common/comms.lua index e834d1e..1943baa 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -57,6 +57,11 @@ local COORD_TYPES = { ALARM = 4 -- alarm signaling } +---@alias CAPI_TYPES integer +local CAPI_TYPES = { + ESTABLISH = 0 -- initial greeting +} + ---@alias RTU_UNIT_TYPES integer local RTU_UNIT_TYPES = { REDSTONE = 0, -- redstone I/O @@ -433,7 +438,6 @@ function comms.mgmt_packet() end -- SCADA coordinator packet --- @todo function comms.coord_packet() local self = { frame = nil, @@ -456,7 +460,7 @@ function comms.coord_packet() end -- make a coordinator packet - ---@param packet_type any + ---@param packet_type COORD_TYPES ---@param data table function public.make(packet_type, data) if type(data) == "table" then @@ -541,7 +545,7 @@ function comms.capi_packet() end -- make a coordinator API packet - ---@param packet_type any + ---@param packet_type CAPI_TYPES ---@param data table function public.make(packet_type, data) if type(data) == "table" then