From 5ce3f84dfa03fd0a50ff7fcec26cfc31ef415ef2 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Mon, 2 May 2022 12:06:04 -0400 Subject: [PATCH] #41 PLC connection closing --- reactor-plc/plc.lua | 52 ++++++++++++++++++++++++++++++++++------- reactor-plc/startup.lua | 2 +- reactor-plc/threads.lua | 22 ++++++++++++++--- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 52bd16c..5aabaae 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -197,6 +197,7 @@ end function comms_init(id, modem, local_port, server_port, reactor, iss) local self = { id = id, + open = false, seq_num = 0, r_seq_num = nil, modem = modem, @@ -218,14 +219,29 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) -- PRIVATE FUNCTIONS -- local _send = function (msg_type, msg) - local s_pkt = comms.scada_packet() - local r_pkt = comms.rplc_packet() + if self.open then + local s_pkt = comms.scada_packet() + local r_pkt = comms.rplc_packet() - r_pkt.make(self.id, msg_type, msg) - s_pkt.make(self.seq_num, PROTOCOLS.RPLC, r_pkt.raw_sendable()) + r_pkt.make(self.id, msg_type, msg) + s_pkt.make(self.seq_num, PROTOCOLS.RPLC, r_pkt.raw_sendable()) - self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable()) - self.seq_num = self.seq_num + 1 + self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable()) + self.seq_num = self.seq_num + 1 + end + end + + local _send_mgmt = function (msg_type, msg) + if self.open then + 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.RPLC, m_pkt.raw_sendable()) + + self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable()) + self.seq_num = self.seq_num + 1 + end end -- variable reactor status information, excluding heating rate @@ -425,6 +441,9 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) self.r_seq_num = packet.scada_frame.seq_num() end + -- mark connection as open + self.open = true + -- feed the watchdog first so it doesn't uhh...eat our packets conn_watchdog.feed() @@ -555,19 +574,34 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) log._debug("discarding non-link packet before linked") end elseif packet.scada_frame.protocol() == PROTOCOLS.SCADA_MGMT then - -- @todo + -- handle session close + if packet.type == SCADA_MGMT_TYPES.CLOSE then + self.open = false + conn_watchdog.cancel() + unlink() + else + log._warning("received unknown SCADA_MGMT packet type " .. packet.type) + end end end end local is_scrammed = function () return self.scrammed end local is_linked = function () return self.linked end + local is_closed = function () return not self.open end local unlink = function () self.linked = false self.r_seq_num = nil end + local close = function () + self.open = false + conn_watchdog.cancel() + unlink() + _send_mgmt(SCADA_MGMT_TYPES.CLOSE, {}) + end + return { reconnect_modem = reconnect_modem, reconnect_reactor = reconnect_reactor, @@ -579,6 +613,8 @@ function comms_init(id, modem, local_port, server_port, reactor, iss) send_iss_alarm = send_iss_alarm, is_scrammed = is_scrammed, is_linked = is_linked, - unlink = unlink + is_closed = is_closed, + unlink = unlink, + close = close } end diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 17e4a2e..8070ea1 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -12,7 +12,7 @@ os.loadAPI("config.lua") os.loadAPI("plc.lua") os.loadAPI("threads.lua") -local R_PLC_VERSION = "alpha-v0.4.13" +local R_PLC_VERSION = "alpha-v0.4.14" local print = util.print local println = util.println diff --git a/reactor-plc/threads.lua b/reactor-plc/threads.lua index 36d3179..3e9ee2e 100644 --- a/reactor-plc/threads.lua +++ b/reactor-plc/threads.lua @@ -169,9 +169,11 @@ function thread__main(smem, init) -- check for termination request if event == "terminate" or ppm.should_terminate() then + log._info("terminate requested, main thread exiting") -- iss handles reactor shutdown plc_state.shutdown = true - log._info("terminate requested, main thread exiting") + -- close connection + plc_comms.close() break end end @@ -195,6 +197,7 @@ function thread__iss(smem) local iss_queue = smem.q.mq_iss + local was_closed = true local last_update = util.time() -- thread loop @@ -203,6 +206,19 @@ function thread__iss(smem) -- ISS checks if plc_state.init_ok then + -- SCRAM if no open connection + if networked and plc_comms.is_closed() then + plc_state.scram = true + if not was_closed then + was_closed = true + iss.trip_timeout() + println_ts("server connection closed by remote host") + log._warning("server connection closed by remote host") + end + else + was_closed = false + end + -- if we tried to SCRAM but failed, keep trying -- in that case, SCRAM won't be called until it reconnects (this is the expected use of this check) if not plc_state.no_reactor and plc_state.scram and reactor.getStatus() then @@ -217,13 +233,13 @@ function thread__iss(smem) end -- check safety (SCRAM occurs if tripped) - if not plc_state.degraded then + if not plc_state.no_reactor then local iss_tripped, iss_status_string, iss_first = iss.check() plc_state.scram = plc_state.scram or iss_tripped if iss_first then println_ts("[ISS] SCRAM! safety trip: " .. iss_status_string) - if networked then + if networked and not plc_state.no_modem then plc_comms.send_iss_alarm(iss_status_string) end end