#498 pocket facility scram and ack all alarms

This commit is contained in:
Mikayla Fischler
2024-09-06 23:31:01 -04:00
parent d6a9f9c5f3
commit 5b311fcfbc
4 changed files with 92 additions and 50 deletions

View File

@@ -11,6 +11,7 @@ local pocket = {}
local PROTOCOL = comms.PROTOCOL local PROTOCOL = comms.PROTOCOL
local CRDN_TYPE = comms.CRDN_TYPE local CRDN_TYPE = comms.CRDN_TYPE
local MGMT_TYPE = comms.MGMT_TYPE local MGMT_TYPE = comms.MGMT_TYPE
local FAC_COMMAND = comms.FAC_COMMAND
local UNIT_COMMAND = comms.UNIT_COMMAND local UNIT_COMMAND = comms.UNIT_COMMAND
-- retry time constants in ms -- retry time constants in ms
@@ -39,7 +40,7 @@ local PERIODICS = {
---@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, i_seq_num, 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_tag = "pkt_session(" .. id .. "): "
local self = { local self = {
-- connection properties -- connection properties
@@ -106,6 +107,10 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
end end
-- link callback transmissions -- link callback transmissions
self.proc_handle.fac_ack.on_scram = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.SCRAM, success }) end
self.proc_handle.fac_ack.on_ack_alarms = function (success) _send(CRDN_TYPE.FAC_CMD, { FAC_COMMAND.ACK_ALL_ALARMS, success }) end
for u = 1, iocontrol.get_db().facility.num_units do for u = 1, iocontrol.get_db().facility.num_units do
self.proc_handle.unit_ack[u].on_start = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.START, u, success }) end self.proc_handle.unit_ack[u].on_start = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.START, u, success }) end
self.proc_handle.unit_ack[u].on_scram = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.SCRAM, u, success }) end self.proc_handle.unit_ack[u].on_scram = function (success) _send(CRDN_TYPE.UNIT_CMD, { UNIT_COMMAND.SCRAM, u, success }) end
@@ -118,7 +123,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
local function _handle_packet(pkt) local function _handle_packet(pkt)
-- check sequence number -- check sequence number
if self.r_seq_num ~= pkt.scada_frame.seq_num() then if self.r_seq_num ~= pkt.scada_frame.seq_num() then
log.warning(log_header .. "sequence out-of-order: next = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num()) log.warning(log_tag .. "sequence out-of-order: next = " .. self.r_seq_num .. ", new = " .. pkt.scada_frame.seq_num())
return return
else else
self.r_seq_num = pkt.scada_frame.seq_num() + 1 self.r_seq_num = pkt.scada_frame.seq_num() + 1
@@ -134,7 +139,28 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
local db = iocontrol.get_db() local db = iocontrol.get_db()
-- handle packet by type -- handle packet by type
if pkt.type == CRDN_TYPE.UNIT_CMD then if pkt.type == CRDN_TYPE.FAC_CMD then
if pkt.length >= 1 then
local cmd = pkt.data[1]
if cmd == FAC_COMMAND.SCRAM_ALL then
log.info(log_tag .. "FAC SCRAM ALL")
self.proc_handle.fac_scram()
elseif cmd == FAC_COMMAND.STOP then
elseif cmd == FAC_COMMAND.START then
elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then
log.info(log_tag .. "FAC ACK ALL ALARMS")
self.proc_handle.fac_ack_alarms()
elseif cmd == FAC_COMMAND.SET_WASTE_MODE then
elseif cmd == FAC_COMMAND.SET_PU_FB then
elseif cmd == FAC_COMMAND.SET_SPS_LP then
else
log.debug(log_tag .. "CRDN facility command unknown")
end
else
log.debug(log_tag .. "CRDN facility command packet length mismatch")
end
elseif pkt.type == CRDN_TYPE.UNIT_CMD then
if pkt.length >= 2 then if pkt.length >= 2 then
-- get command and unit id -- get command and unit id
local cmd = pkt.data[1] local cmd = pkt.data[1]
@@ -143,56 +169,36 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
-- continue if valid unit id -- continue if valid unit id
if util.is_int(uid) and uid > 0 and uid <= #db.units then if util.is_int(uid) and uid > 0 and uid <= #db.units then
if cmd == UNIT_COMMAND.START then if cmd == UNIT_COMMAND.START then
log.info(util.c(log_header, "UNIT[", uid, "] START")) log.info(util.c(log_tag, "UNIT[", uid, "] START"))
self.proc_handle.start(uid) self.proc_handle.start(uid)
elseif cmd == UNIT_COMMAND.SCRAM then elseif cmd == UNIT_COMMAND.SCRAM then
log.info(util.c(log_header, "UNIT[", uid, "] SCRAM")) log.info(util.c(log_tag, "UNIT[", uid, "] SCRAM"))
self.proc_handle.scram(uid) self.proc_handle.scram(uid)
elseif cmd == UNIT_COMMAND.RESET_RPS then elseif cmd == UNIT_COMMAND.RESET_RPS then
log.info(util.c(log_header, "UNIT[", uid, "] RESET RPS")) log.info(util.c(log_tag, "UNIT[", uid, "] RESET RPS"))
self.proc_handle.reset_rps(uid) self.proc_handle.reset_rps(uid)
elseif cmd == UNIT_COMMAND.SET_BURN then elseif cmd == UNIT_COMMAND.SET_BURN then
if pkt.length == 3 then if pkt.length == 3 then
log.info(util.c(log_header, "UNIT[", uid, "] SET BURN ", pkt.data[3])) log.info(util.c(log_tag, "UNIT[", uid, "] SET BURN ", pkt.data[3]))
process.set_rate(uid, pkt.data[3]) process.set_rate(uid, pkt.data[3])
else else
log.debug(log_header .. "CRDN unit command burn rate missing option") log.debug(log_tag .. "CRDN unit command burn rate missing option")
end end
elseif cmd == UNIT_COMMAND.SET_WASTE then elseif cmd == UNIT_COMMAND.SET_WASTE then
-- if (pkt.length == 3) and (type(pkt.data[3]) == "number") and (pkt.data[3] > 0) and (pkt.data[3] <= 4) then
-- process.set_unit_waste(uid, pkt.data[3])
-- else
-- log.debug(log_header .. "CRDN unit command set waste missing/invalid option")
-- end
elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then
log.info(util.c(log_header, "UNIT[", uid, "] ACK ALL ALARMS")) log.info(util.c(log_tag, "UNIT[", uid, "] ACK ALL ALARMS"))
self.proc_handle.ack_all_alarms(uid) self.proc_handle.ack_all_alarms(uid)
elseif cmd == UNIT_COMMAND.ACK_ALARM then elseif cmd == UNIT_COMMAND.ACK_ALARM then
-- if pkt.length == 3 then
-- process.ack_alarm(uid, pkt.data[3])
-- else
-- log.debug(log_header .. "CRDN unit command ack alarm missing alarm id")
-- end
elseif cmd == UNIT_COMMAND.RESET_ALARM then elseif cmd == UNIT_COMMAND.RESET_ALARM then
-- if pkt.length == 3 then
-- process.reset_alarm(uid, pkt.data[3])
-- else
-- log.debug(log_header .. "CRDN unit command reset alarm missing alarm id")
-- end
elseif cmd == UNIT_COMMAND.SET_GROUP then elseif cmd == UNIT_COMMAND.SET_GROUP then
-- if (pkt.length == 3) and (type(pkt.data[3]) == "number") and (pkt.data[3] >= 0) and (pkt.data[3] <= 4) then
-- process.set_group(uid, pkt.data[3])
-- else
-- log.debug(log_header .. "CRDN unit command set group missing group id")
-- end
else else
log.debug(log_header .. "CRDN unit command unknown") log.debug(log_tag .. "CRDN unit command unknown")
end end
else else
log.debug(log_header .. "CRDN unit command invalid") log.debug(log_tag .. "CRDN unit command invalid")
end end
else else
log.debug(log_header .. "CRDN unit command packet length mismatch") log.debug(log_tag .. "CRDN unit command packet length mismatch")
end end
elseif pkt.type == CRDN_TYPE.API_GET_FAC then elseif pkt.type == CRDN_TYPE.API_GET_FAC then
local fac = db.facility local fac = db.facility
@@ -232,7 +238,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
end end
end end
else else
log.debug(log_header .. "handler received unsupported CRDN packet type " .. pkt.type) log.debug(log_tag .. "handler received unsupported CRDN packet type " .. pkt.type)
end end
elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then elseif pkt.scada_frame.protocol() == PROTOCOL.SCADA_MGMT then
---@cast pkt mgmt_frame ---@cast pkt mgmt_frame
@@ -245,7 +251,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
self.last_rtt = srv_now - srv_start self.last_rtt = srv_now - srv_start
if self.last_rtt > 750 then if self.last_rtt > 750 then
log.warning(log_header .. "PKT KEEP_ALIVE round trip time > 750ms (" .. self.last_rtt .. "ms)") log.warning(log_tag .. "PKT KEEP_ALIVE round trip time > 750ms (" .. self.last_rtt .. "ms)")
end end
-- log.debug(log_header .. "PKT RTT = " .. self.last_rtt .. "ms") -- log.debug(log_header .. "PKT RTT = " .. self.last_rtt .. "ms")
@@ -253,7 +259,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
iocontrol.fp_pkt_rtt(id, self.last_rtt) iocontrol.fp_pkt_rtt(id, self.last_rtt)
else else
log.debug(log_header .. "SCADA keep alive packet length mismatch") log.debug(log_tag .. "SCADA keep alive packet length mismatch")
end end
elseif pkt.type == MGMT_TYPE.CLOSE then elseif pkt.type == MGMT_TYPE.CLOSE then
-- close the session -- close the session
@@ -261,9 +267,9 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
elseif pkt.type == MGMT_TYPE.ESTABLISH then elseif pkt.type == MGMT_TYPE.ESTABLISH then
-- something is wrong, kill the session -- something is wrong, kill the session
_close() _close()
log.warning(log_header .. "terminated session due to an unexpected ESTABLISH packet") log.warning(log_tag .. "terminated session due to an unexpected ESTABLISH packet")
else else
log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type) log.debug(log_tag .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type)
end end
end end
end end
@@ -288,7 +294,7 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
function public.close() function public.close()
_close() _close()
_send_mgmt(MGMT_TYPE.CLOSE, {}) _send_mgmt(MGMT_TYPE.CLOSE, {})
log.info(log_header .. "session closed by server") log.info(log_tag .. "session closed by server")
end end
-- iterate the session -- iterate the session
@@ -319,14 +325,14 @@ function pocket.new_session(id, s_addr, i_seq_num, in_queue, out_queue, timeout)
-- max 100ms spent processing queue -- max 100ms spent processing queue
if util.time() - handle_start > 100 then if util.time() - handle_start > 100 then
log.warning(log_header .. "exceeded 100ms queue process limit") log.warning(log_tag .. "exceeded 100ms queue process limit")
break break
end end
end end
-- exit if connection was closed -- exit if connection was closed
if not self.connected then if not self.connected then
log.info(log_header .. "session closed by remote host") log.info(log_tag .. "session closed by remote host")
return self.connected return self.connected
end end

View File

@@ -167,6 +167,11 @@ function iocontrol.init_fac(conf)
radiation = types.new_zero_radiation_reading(), radiation = types.new_zero_radiation_reading(),
start_ack = __generic_ack,
stop_ack = __generic_ack,
scram_ack = __generic_ack,
ack_alarms_ack = __generic_ack,
ps = psil.create(), ps = psil.create(),
induction_ps_tbl = {}, induction_ps_tbl = {},

View File

@@ -654,7 +654,28 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
if protocol == PROTOCOL.SCADA_CRDN then if protocol == PROTOCOL.SCADA_CRDN then
---@cast packet crdn_frame ---@cast packet crdn_frame
if self.api.linked then if self.api.linked then
if packet.type == CRDN_TYPE.UNIT_CMD then if packet.type == CRDN_TYPE.FAC_CMD then
-- facility command acknowledgement
if packet.length >= 2 then
local cmd = packet.data[1]
local ack = packet.data[2] == true
if cmd == FAC_COMMAND.SCRAM_ALL then
iocontrol.get_db().facility.scram_ack(ack)
elseif cmd == FAC_COMMAND.STOP then
elseif cmd == FAC_COMMAND.START then
elseif cmd == FAC_COMMAND.ACK_ALL_ALARMS then
iocontrol.get_db().facility.ack_alarms_ack(ack)
elseif cmd == FAC_COMMAND.SET_WASTE_MODE then
elseif cmd == FAC_COMMAND.SET_PU_FB then
elseif cmd == FAC_COMMAND.SET_SPS_LP then
else
log.debug(util.c("received facility command ack with unknown command ", cmd))
end
else
log.debug("SCADA_CRDN facility command ack packet length mismatch")
end
elseif packet.type == CRDN_TYPE.UNIT_CMD then
-- unit command acknowledgement -- unit command acknowledgement
if packet.length == 3 then if packet.length == 3 then
local cmd = packet.data[1] local cmd = packet.data[1]
@@ -670,16 +691,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog, nav)
unit.start_ack(ack) unit.start_ack(ack)
elseif cmd == UNIT_COMMAND.RESET_RPS then elseif cmd == UNIT_COMMAND.RESET_RPS then
unit.reset_rps_ack(ack) unit.reset_rps_ack(ack)
elseif cmd == UNIT_COMMAND.SET_BURN then
-- this also doesn't exist
elseif cmd == UNIT_COMMAND.SET_WASTE then
-- updated by unit updates
elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then elseif cmd == UNIT_COMMAND.ACK_ALL_ALARMS then
unit.ack_alarms_ack(ack) unit.ack_alarms_ack(ack)
elseif cmd == UNIT_COMMAND.SET_GROUP then
-- updated by unit updates
else else
log.debug(util.c("received unit command ack with unknown command ", cmd)) log.debug(util.c("received unsupported unit command ack for command ", cmd))
end end
end end
end end

View File

@@ -6,13 +6,13 @@ local util = require("scada-common.util")
local iocontrol = require("pocket.iocontrol") local iocontrol = require("pocket.iocontrol")
local pocket = require("pocket.pocket") local pocket = require("pocket.pocket")
local process = require("pocket.process")
local style = require("pocket.ui.style") local style = require("pocket.ui.style")
local core = require("graphics.core") local core = require("graphics.core")
local Div = require("graphics.elements.div") local Div = require("graphics.elements.div")
local ListBox = require("graphics.elements.listbox")
local MultiPane = require("graphics.elements.multipane") local MultiPane = require("graphics.elements.multipane")
local TextBox = require("graphics.elements.textbox") local TextBox = require("graphics.elements.textbox")
@@ -77,6 +77,7 @@ local function new_view(root)
local function set_sidebar() local function set_sidebar()
local list = { local list = {
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end }, { label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(APP_ID.ROOT) end },
{ label = "FAC", color = core.cpair(colors.black, colors.orange), callback = function () app.switcher(db.facility.num_units + 1) end }
} }
for i = 1, db.facility.num_units do for i = 1, db.facility.num_units do
@@ -95,7 +96,7 @@ local function new_view(root)
local active_unit = 1 local active_unit = 1
-- create all page divs -- create all page divs
for _ = 1, db.facility.num_units do for _ = 1, db.facility.num_units + 1 do
local div = Div{parent=page_div} local div = Div{parent=page_div}
table.insert(panes, div) table.insert(panes, div)
end end
@@ -188,6 +189,21 @@ local function new_view(root)
util.nop() util.nop()
end end
-- facility controls
local f_pane = panes[db.facility.num_units + 1]
local f_div = Div{parent=f_pane,x=2,width=main.get_width()-2}
app.new_page(nil, db.facility.num_units + 1)
TextBox{parent=f_div,y=1,text="Facility Commands",alignment=ALIGN.CENTER}
local scram = HazardButton{parent=f_div,x=5,y=6,text="FAC SCRAM",accent=colors.yellow,dis_colors=dis_colors,callback=process.fac_scram,fg_bg=hzd_fg_bg}
local ack_a = HazardButton{parent=f_div,x=7,y=11,text="ACK \x13",accent=colors.orange,dis_colors=dis_colors,callback=process.fac_ack_alarms,fg_bg=hzd_fg_bg}
db.facility.scram_ack = scram.on_response
db.facility.ack_alarms_ack = ack_a.on_response
-- setup multipane -- setup multipane
local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes} local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
app.set_root_pane(u_pane) app.set_root_pane(u_pane)