#107, #121 RTU build changes, formed handling

This commit is contained in:
Mikayla Fischler
2022-11-11 14:59:53 -05:00
parent bc63a06b09
commit 83cf645da4
20 changed files with 282 additions and 39 deletions

View File

@@ -271,6 +271,12 @@ function rtu.comms(version, modem, local_port, server_port, conn_watchdog)
_send(SCADA_MGMT_TYPES.RTU_ADVERT, advertisement)
end
-- notify that a peripheral was remounted
---@param unit_index integer RTU unit ID
function public.send_remounted(unit_index)
_send(SCADA_MGMT_TYPES.RTU_DEV_REMOUNT, { unit_index })
end
-- parse a MODBUS/SCADA packet
---@param side string
---@param sender integer
@@ -400,6 +406,8 @@ function rtu.comms(version, modem, local_port, server_port, conn_watchdog)
-- acknowledgement
rtu_state.linked = true
self.r_seq_num = nil
println_ts("supervisor connection established")
log.info("supervisor connection established")
elseif packet.type == SCADA_MGMT_TYPES.RTU_ADVERT then
-- request for capabilities again
public.send_advertisement(units)

View File

@@ -24,7 +24,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local RTU_VERSION = "beta-v0.8.2"
local RTU_VERSION = "beta-v0.9.0"
local rtu_t = types.rtu_t
@@ -116,8 +116,8 @@ local function configure()
-- redstone interfaces
for entry_idx = 1, #rtu_redstone do
local rs_rtu = redstone_rtu.new()
local io_table = rtu_redstone[entry_idx].io
local io_reactor = rtu_redstone[entry_idx].for_reactor
local io_table = rtu_redstone[entry_idx].io ---@type table
local io_reactor = rtu_redstone[entry_idx].for_reactor ---@type integer
-- CHECK: reactor ID must be >= to 1
if (not util.is_int(io_reactor)) or (io_reactor <= 0) then
@@ -218,6 +218,7 @@ local function configure()
index = entry_idx,
reactor = io_reactor,
device = capabilities, -- use device field for redstone channels
formed = nil, ---@type boolean|nil
rtu = rs_rtu, ---@type rtu_device|rtu_rs_device
modbus_io = modbus.new(rs_rtu, false),
pkt_queue = nil, ---@type mqueue|nil
@@ -265,26 +266,55 @@ local function configure()
local type = ppm.get_type(name)
local rtu_iface = nil ---@type rtu_device
local rtu_type = ""
local formed = nil ---@type boolean|nil
if type == "boilerValve" then
-- boiler multiblock
rtu_type = rtu_t.boiler_valve
rtu_iface = boilerv_rtu.new(device)
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
log.fatal(util.c("configure> failed to check if '", name, "' is a formed boiler multiblock"))
return false
end
elseif type == "turbineValve" then
-- turbine multiblock
rtu_type = rtu_t.turbine_valve
rtu_iface = turbinev_rtu.new(device)
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
log.fatal(util.c("configure> failed to check if '", name, "' is a formed turbine multiblock"))
return false
end
elseif type == "inductionPort" then
-- induction matrix multiblock
rtu_type = rtu_t.induction_matrix
rtu_iface = imatrix_rtu.new(device)
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
log.fatal(util.c("configure> failed to check if '", name, "' is a formed induction matrix multiblock"))
return false
end
elseif type == "spsPort" then
-- SPS multiblock
rtu_type = rtu_t.sps
rtu_iface = sps_rtu.new(device)
formed = device.isFormed()
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
log.fatal(util.c("configure> failed to check if '", name, "' is a formed SPS multiblock"))
return false
end
elseif type == "solarNeutronActivator" then
-- SNA
rtu_type = rtu_t.sps
rtu_type = rtu_t.sna
rtu_iface = sna_rtu.new(device)
elseif type == "environmentDetector" then
-- advanced peripherals environment detector
@@ -305,6 +335,7 @@ local function configure()
index = index,
reactor = for_reactor,
device = device,
formed = formed,
rtu = rtu_iface, ---@type rtu_device|rtu_rs_device
modbus_io = modbus.new(rtu_iface, true),
pkt_queue = mqueue.new(), ---@type mqueue|nil

View File

@@ -5,7 +5,10 @@ local types = require("scada-common.types")
local util = require("scada-common.util")
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
local envd_rtu = require("rtu.dev.envd_rtu")
local imatrix_rtu = require("rtu.dev.imatrix_rtu")
local sna_rtu = require("rtu.dev.sna_rtu")
local sps_rtu = require("rtu.dev.sps_rtu")
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
local modbus = require("rtu.modbus")
@@ -117,20 +120,35 @@ function threads.thread__main(smem)
local unit = units[i] ---@type rtu_unit_registry_entry
-- find disconnected device to reconnect
-- note: cannot check isFormed as that would yield this coroutine and consume events
if unit.name == param1 then
-- found, re-link
unit.device = device
if unit.type == rtu_t.boiler_valve then
unit.rtu = boilerv_rtu.new(device)
unit.formed = true
elseif unit.type == rtu_t.turbine_valve then
unit.rtu = turbinev_rtu.new(device)
unit.formed = true
elseif unit.type == rtu_t.induction_matrix then
unit.rtu = imatrix_rtu.new(device)
unit.formed = true
elseif unit.type == rtu_t.sps then
unit.rtu = sps_rtu.new(device)
unit.formed = true
elseif unit.type == rtu_t.sna then
unit.rtu = sna_rtu.new(device)
elseif unit.type == rtu_t.env_detector then
unit.rtu = envd_rtu.new(device)
else
log.error(util.c("unreachable case occured trying to identify reconnected RTU unit type (", unit.name, ")"), true)
end
unit.modbus_io = modbus.new(unit.rtu, true)
rtu_comms.send_remounted(unit.index)
println_ts("reconnected the " .. unit.type .. " on interface " .. unit.name)
end
end
@@ -256,6 +274,12 @@ function threads.thread__unit_comms(smem, unit)
local last_update = util.time()
local check_formed = type(unit.formed) == "boolean"
local last_f_check = 0
local detail_name = util.c(unit.type, " (", unit.name, ") [", unit.index, "] for reactor ", unit.reactor)
local short_name = util.c(unit.type, " (", unit.name, ")")
if packet_queue == nil then
log.error("rtu unit thread created without a message queue, exiting...", true)
return
@@ -283,9 +307,64 @@ function threads.thread__unit_comms(smem, unit)
util.nop()
end
-- check if multiblocks is still formed
if check_formed and (util.time() - last_f_check > 1000) then
if (not unit.formed) and unit.device.isFormed() then
-- newly re-formed
local iface = ppm.get_iface(unit.device)
if iface then
log.info(util.c("unmounting and remounting reformed RTU unit ", detail_name))
ppm.unmount(unit.device)
local type, device = ppm.mount(iface)
if device ~= nil then
if type == "boilerValve" and unit.type == rtu_t.boiler_valve then
-- boiler multiblock
unit.device = device
unit.rtu = boilerv_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "turbineValve" and unit.type == rtu_t.turbine_valve then
-- turbine multiblock
unit.device = device
unit.rtu = turbinev_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "inductionPort" and unit.type == rtu_t.induction_matrix then
-- induction matrix multiblock
unit.device = device
unit.rtu = imatrix_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
elseif type == "spsPort" and unit.type == rtu_t.sps then
-- SPS multiblock
unit.device = device
unit.rtu = sps_rtu.new(device)
unit.formed = device.isFormed()
unit.modbus_io = modbus.new(unit.rtu, true)
else
log.error("illegal remount of non-multiblock RTU attempted for " .. short_name, true)
end
rtu_comms.send_remounted(unit.index)
else
-- fully lost the peripheral now :(
log.error(util.c(unit.name, " lost (failed reconnect)"))
end
log.info("reconnected the " .. unit.type .. " on interface " .. unit.name)
else
log.error("failed to get interface of previously connected RTU unit " .. detail_name, true)
end
end
end
-- check for termination request
if rtu_state.shutdown then
log.info("rtu unit thread exiting -> " .. unit.type .. "(" .. unit.name .. ")")
log.info("rtu unit thread exiting -> " .. short_name)
break
end
@@ -305,7 +384,7 @@ function threads.thread__unit_comms(smem, unit)
end
if not rtu_state.shutdown then
log.info(util.c("rtu unit thread ", unit.type, "(", unit.name, ") restarting in 5 seconds..."))
log.info(util.c("rtu unit thread ", unit.type, "(", unit.name, " restarting in 5 seconds..."))
util.psleep(5)
end
end