@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user