From a77946ce2ceb5a4a7c9ebb7ded831677123f8daf Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 2 Apr 2022 11:22:44 -0400 Subject: [PATCH] #1 PLC does not shut down if failed link, repeatedly tries to maintain link as part of main loop --- reactor-plc/plc.lua | 134 ++++++++++++++++++---------------------- reactor-plc/startup.lua | 45 ++++++++------ 2 files changed, 87 insertions(+), 92 deletions(-) diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 8212875..bbe2021 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -1,53 +1,5 @@ -- #REQUIRES comms.lua -function scada_link(plc_comms) - local linked = false - local link_timeout = os.startTimer(5) - - plc_comms.send_link_req() - print_ts("sent link request") - - repeat - local event, p1, p2, p3, p4, p5 = os.pullEvent() - - -- handle event - if event == "timer" and param1 == link_timeout then - -- no response yet - print("...no response"); - elseif event == "modem_message" then - -- server response? cancel timeout - if link_timeout ~= nil then - os.cancelTimer(link_timeout) - end - - local packet = plc_comms.parse_packet(p1, p2, p3, p4, p5) - if packet then - -- handle response - local response = plc_comms.handle_link(packet) - if response == nil then - print_ts("invalid link response, bad channel?\n") - break - elseif response == comms.RPLC_LINKING.COLLISION then - print_ts("...reactor PLC ID collision (check config), exiting...\n") - break - elseif response == comms.RPLC_LINKING.ALLOW then - print_ts("...linked!\n") - linked = true - plc_comms.send_rs_io_conns() - plc_comms.send_struct() - plc_comms.send_status() - print_ts("sent initial data\n") - else - print_ts("...denied, exiting...\n") - break - end - end - end - until linked - - return linked -end - -- Internal Safety System -- identifies dangerous states and SCRAMs reactor if warranted -- autonomous from main control @@ -253,7 +205,8 @@ function rplc_comms(id, modem, local_port, server_port, reactor) s_port = server_port, l_port = local_port, reactor = reactor, - status_cache = nil + status_cache = nil, + linked = false } -- PRIVATE FUNCTIONS -- @@ -342,32 +295,60 @@ function rplc_comms(id, modem, local_port, server_port, reactor) return pkt end - -- handle a linking packet - local handle_link = function (packet) - if packet.type == RPLC_TYPES.LINK_REQ then - return packet.data[1] == RPLC_LINKING.ALLOW - else - return nil - end - end - -- handle an RPLC packet local handle_packet = function (packet) - if packet.type == RPLC_TYPES.KEEP_ALIVE then - -- keep alive request received, nothing to do except feed watchdog - elseif packet.type == RPLC_TYPES.MEK_STRUCT then - -- request for physical structure - send_struct() - elseif packet.type == RPLC_TYPES.RS_IO_CONNS then - -- request for redstone connections - send_rs_io_conns() - elseif packet.type == RPLC_TYPES.RS_IO_GET then - elseif packet.type == RPLC_TYPES.RS_IO_SET then - elseif packet.type == RPLC_TYPES.MEK_SCRAM then - elseif packet.type == RPLC_TYPES.MEK_ENABLE then - elseif packet.type == RPLC_TYPES.MEK_BURN_RATE then - elseif packet.type == RPLC_TYPES.ISS_GET then - elseif packet.type == RPLC_TYPES.ISS_CLEAR then + if packet ~= nil then + if packet.scada_frame.protocol() == PROTOCOLS.RPLC then + if self.linked then + if packet.type == RPLC_TYPES.KEEP_ALIVE then + -- keep alive request received, nothing to do except feed watchdog + elseif packet.type == RPLC_TYPES.LINK_REQ then + -- link request confirmation + log._debug("received link request response after already being linked") + elseif packet.type == RPLC_TYPES.MEK_STRUCT then + -- request for physical structure + send_struct() + elseif packet.type == RPLC_TYPES.RS_IO_CONNS then + -- request for redstone connections + send_rs_io_conns() + elseif packet.type == RPLC_TYPES.RS_IO_GET then + elseif packet.type == RPLC_TYPES.RS_IO_SET then + elseif packet.type == RPLC_TYPES.MEK_SCRAM then + elseif packet.type == RPLC_TYPES.MEK_ENABLE then + elseif packet.type == RPLC_TYPES.MEK_BURN_RATE then + elseif packet.type == RPLC_TYPES.ISS_GET then + elseif packet.type == RPLC_TYPES.ISS_CLEAR then + end + elseif packet.type == RPLC_TYPES.LINK_REQ then + -- link request confirmation + local link_ack = packet.data[1] + + if link_ack == RPLC_LINKING.ALLOW then + print_ts("...linked!\n") + log._debug("rplc link request approved") + + plc_comms.send_rs_io_conns() + plc_comms.send_struct() + plc_comms.send_status() + + log._debug("sent initial status data") + elseif link_ack == RPLC_LINKING.DENY then + print_ts("...denied, retrying...\n") + log._debug("rplc link request denied") + elseif link_ack == RPLC_LINKING.COLLISION then + print_ts("reactor PLC ID collision (check config), retrying...\n") + log._warning("rplc link request collision") + else + print_ts("invalid link response, bad channel? retrying...\n") + log._error("unknown rplc link request response") + end + + self.linked = link_ack == RPLC_LINKING.ALLOW + else + log._("discarding non-link packet before linked") + end + elseif packet.scada_frame.protocol() == PROTOCOLS.SCADA_MGMT then + end end end @@ -427,12 +408,17 @@ function rplc_comms(id, modem, local_port, server_port, reactor) _send(sys_status) end + local linked = function () return self.linked end + local unlink = function () self.linked = false end + return { parse_packet = parse_packet, handle_link = handle_link, handle_packet = handle_packet, send_link_req = send_link_req, send_struct = send_struct, - send_status = send_status + send_status = send_status, + linked = linked, + unlink = unlink } end diff --git a/reactor-plc/startup.lua b/reactor-plc/startup.lua index 6875bcb..0554d26 100644 --- a/reactor-plc/startup.lua +++ b/reactor-plc/startup.lua @@ -14,13 +14,14 @@ local R_PLC_VERSION = "alpha-v0.1.0" local print_ts = util.print_ts +print(">> Reactor PLC " .. R_PLC_VERSION .. " <<") + +-- mount connected devices ppm.mount_all() local reactor = ppm.get_device("fissionReactor") local modem = ppm.get_device("modem") -print(">> Reactor PLC " .. R_PLC_VERSION .. " <<") - -- we need a reactor and a modem if reactor == nil then print("Fission reactor not found, exiting..."); @@ -44,19 +45,19 @@ end local plc_comms = plc.rplc_comms(config.REACTOR_ID, modem, config.LISTEN_PORT, config.SERVER_PORT, reactor) --- attempt server connection --- exit application if connection is denied -if ~plc.scada_link(plc_comms) then - return -end - -- comms watchdog, 3 second timeout local conn_watchdog = watchdog.new_watchdog(3) -- loop clock (10Hz, 2 ticks) --- send status updates at 4Hz (every 5 ticks) local loop_tick = os.startTimer(0.05) -local ticks_to_update = 5 + +-- send status updates at ~3.33Hz (every 6 server ticks) (every 3 loop ticks) +-- send link requests at 0.5Hz (every 40 server ticks) (every 20 loop ticks) +local UPDATE_TICKS = 3 +local LINK_TICKS = 20 + +-- start by linking +local ticks_to_update = LINK_TICKS -- runtime variables local control_state = false @@ -70,13 +71,12 @@ while true do -- try to scram reactor if it is still connected if reactor.scram() then - print_ts("[fatal] PLC lost a peripheral: successful SCRAM, now exiting...\n") + print_ts("[fatal] PLC lost a peripheral: successful SCRAM\n") else - print_ts("[fatal] PLC lost a peripheral: failed SCRAM, now exiting...\n") + print_ts("[fatal] PLC lost a peripheral: failed SCRAM\n") end -- send an alarm: plc_comms.send_alarm(ALARMS.PLC_PERI_DC) ? - return end -- check safety (SCRAM occurs if tripped) @@ -87,11 +87,20 @@ while true do -- handle event if event == "timer" and param1 == loop_tick then - -- basic event tick, send updated data if it is time (4Hz) + -- basic event tick, send updated data if it is time (~3.33Hz) + -- iss was already checked (main reason for this tick rate) ticks_to_update = ticks_to_update - 1 - if ticks_to_update == 0 then - plc_comms.send_status(control_state, iss_tripped) - ticks_to_update = 5 + + if plc_comms.linked() then + if ticks_to_update <= 0 then + plc_comms.send_status(control_state, iss_tripped) + ticks_to_update = UPDATE_TICKS + end + else + if ticks_to_update <= 0 then + plc_comms.send_link_req() + ticks_to_update = LINK_TICKS + end end elseif event == "modem_message" then -- got a packet @@ -100,9 +109,9 @@ while true do local packet = plc_comms.parse_packet(p1, p2, p3, p4, p5) plc_comms.handle_packet(packet) - elseif event == "timer" and param1 == conn_watchdog.get_timer() then -- haven't heard from server recently? shutdown reactor + plc_comms.unlink() iss.trip_timeout() print_ts("[alert] server timeout, reactor disabled\n") elseif event == "terminate" then