#130 facility data object, some code cleanup, comms protocol changed from 1.0.1 to 1.1.0

This commit is contained in:
Mikayla Fischler
2022-12-10 13:58:17 -05:00
parent 41913441d5
commit 03f0216d51
15 changed files with 635 additions and 283 deletions

View File

@@ -10,7 +10,7 @@ local coordinator = {}
local PROTOCOLS = comms.PROTOCOLS
local SCADA_MGMT_TYPES = comms.SCADA_MGMT_TYPES
local SCADA_CRDN_TYPES = comms.SCADA_CRDN_TYPES
local CRDN_COMMANDS = comms.CRDN_COMMANDS
local UNIT_COMMANDS = comms.UNIT_COMMANDS
local SV_Q_CMDS = svqtypes.SV_Q_CMDS
local SV_Q_DATA = svqtypes.SV_Q_DATA
@@ -44,15 +44,14 @@ local PERIODICS = {
---@param id integer
---@param in_queue mqueue
---@param out_queue mqueue
---@param facility_units table
function coordinator.new_session(id, in_queue, out_queue, facility_units)
---@param facility facility
---@param units table
function coordinator.new_session(id, in_queue, out_queue, facility, units)
local log_header = "crdn_session(" .. id .. "): "
local self = {
id = id,
in_q = in_queue,
out_q = out_queue,
units = facility_units,
-- connection properties
seq_num = 0,
r_seq_num = nil,
@@ -67,11 +66,13 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
},
-- when to next retry one of these messages
retry_times = {
builds_packet = 0
f_builds_packet = 0,
u_builds_packet = 0
},
-- message acknowledgements
acks = {
builds = false
fac_builds = false,
unit_builds = false
}
}
@@ -109,26 +110,41 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
self.seq_num = self.seq_num + 1
end
-- send facility builds
local function _send_fac_builds()
self.acks.fac_builds = false
_send(SCADA_CRDN_TYPES.FAC_BUILDS, facility.get_build())
end
-- send unit builds
local function _send_builds()
self.acks.builds = false
local function _send_unit_builds()
self.acks.unit_builds = false
local builds = {}
for i = 1, #self.units do
local unit = self.units[i] ---@type reactor_unit
for i = 1, #units do
local unit = units[i] ---@type reactor_unit
builds[unit.get_id()] = unit.get_build()
end
_send(SCADA_CRDN_TYPES.STRUCT_BUILDS, builds)
_send(SCADA_CRDN_TYPES.UNIT_BUILDS, builds)
end
-- send facility status
local function _send_fac_status()
local status = {
facility.get_rtu_statuses()
}
_send(SCADA_CRDN_TYPES.FAC_STATUS, status)
end
-- send unit statuses
local function _send_status()
local function _send_unit_statuses()
local status = {}
for i = 1, #self.units do
local unit = self.units[i] ---@type reactor_unit
for i = 1, #units do
local unit = units[i] ---@type reactor_unit
status[unit.get_id()] = {
unit.get_reactor_status(),
unit.get_rtu_statuses(),
@@ -183,10 +199,13 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type)
end
elseif pkt.scada_frame.protocol() == PROTOCOLS.SCADA_CRDN then
if pkt.type == SCADA_CRDN_TYPES.STRUCT_BUILDS then
if pkt.type == SCADA_CRDN_TYPES.FAC_BUILDS then
-- acknowledgement to coordinator receiving builds
self.acks.builds = true
elseif pkt.type == SCADA_CRDN_TYPES.COMMAND_UNIT then
self.acks.fac_builds = true
elseif pkt.type == SCADA_CRDN_TYPES.UNIT_BUILDS then
-- acknowledgement to coordinator receiving builds
self.acks.unit_builds = true
elseif pkt.type == SCADA_CRDN_TYPES.UNIT_CMD then
if pkt.length >= 2 then
-- get command and unit id
local cmd = pkt.data[1]
@@ -196,37 +215,37 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
local data = { uid, pkt.data[3] }
-- continue if valid unit id
if util.is_int(uid) and uid > 0 and uid <= #self.units then
local unit = self.units[uid] ---@type reactor_unit
if util.is_int(uid) and uid > 0 and uid <= #units then
local unit = units[uid] ---@type reactor_unit
if cmd == CRDN_COMMANDS.START then
if cmd == UNIT_COMMANDS.START then
self.out_q.push_data(SV_Q_DATA.START, data)
elseif cmd == CRDN_COMMANDS.SCRAM then
elseif cmd == UNIT_COMMANDS.SCRAM then
self.out_q.push_data(SV_Q_DATA.SCRAM, data)
elseif cmd == CRDN_COMMANDS.RESET_RPS then
elseif cmd == UNIT_COMMANDS.RESET_RPS then
self.out_q.push_data(SV_Q_DATA.RESET_RPS, data)
elseif cmd == CRDN_COMMANDS.SET_BURN then
elseif cmd == UNIT_COMMANDS.SET_BURN then
if pkt.length == 3 then
self.out_q.push_data(SV_Q_DATA.SET_BURN, data)
else
log.debug(log_header .. "CRDN command unit burn rate missing option")
end
elseif cmd == CRDN_COMMANDS.SET_WASTE then
elseif cmd == UNIT_COMMANDS.SET_WASTE then
if pkt.length == 3 then
unit.set_waste(pkt.data[3])
else
log.debug(log_header .. "CRDN command unit set waste missing option")
end
elseif cmd == CRDN_COMMANDS.ACK_ALL_ALARMS then
elseif cmd == UNIT_COMMANDS.ACK_ALL_ALARMS then
unit.ack_all()
_send(SCADA_CRDN_TYPES.COMMAND_UNIT, { cmd, uid, true })
elseif cmd == CRDN_COMMANDS.ACK_ALARM then
_send(SCADA_CRDN_TYPES.UNIT_CMD, { cmd, uid, true })
elseif cmd == UNIT_COMMANDS.ACK_ALARM then
if pkt.length == 3 then
unit.ack_alarm(pkt.data[3])
else
log.debug(log_header .. "CRDN command unit ack alarm missing id")
end
elseif cmd == CRDN_COMMANDS.RESET_ALARM then
elseif cmd == UNIT_COMMANDS.RESET_ALARM then
if pkt.length == 3 then
unit.reset_alarm(pkt.data[3])
else
@@ -251,7 +270,7 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
local public = {}
-- get the session ID
function public.get_id() return self.id end
function public.get_id() return id end
-- check if a timer matches this session's watchdog
function public.check_wd(timer)
@@ -262,7 +281,7 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
function public.close()
_close()
_send_mgmt(SCADA_MGMT_TYPES.CLOSE, {})
println("connection to coordinator " .. self.id .. " closed by server")
println("connection to coordinator " .. id .. " closed by server")
log.info(log_header .. "session closed by server")
end
@@ -289,9 +308,9 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
local cmd = message.message
if cmd == CRD_S_CMDS.RESEND_BUILDS then
-- re-send builds
self.acks.builds = false
self.retry_times.builds_packet = util.time() + RETRY_PERIOD
_send_builds()
_send_fac_builds()
_send_unit_builds()
end
elseif message.qtype == mqueue.TYPE.DATA then
-- instruction with body
@@ -299,7 +318,7 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
if cmd.key == CRD_S_DATA.CMD_ACK then
local ack = cmd.val ---@type coord_ack
_send(SCADA_CRDN_TYPES.COMMAND_UNIT, { ack.cmd, ack.unit, ack.ack })
_send(SCADA_CRDN_TYPES.UNIT_CMD, { ack.cmd, ack.unit, ack.ack })
end
end
end
@@ -313,7 +332,7 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
-- exit if connection was closed
if not self.connected then
println("connection to coordinator " .. self.id .. " closed by remote host")
println("connection to coordinator " .. id .. " closed by remote host")
log.info(log_header .. "session closed by remote host")
return self.connected
end
@@ -334,11 +353,12 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
periodics.keep_alive = 0
end
-- unit statuses to coordinator
-- statuses to coordinator
periodics.status_packet = periodics.status_packet + elapsed
if periodics.status_packet >= PERIODICS.STATUS then
_send_status()
_send_fac_status()
_send_unit_statuses()
periodics.status_packet = 0
end
@@ -350,12 +370,19 @@ function coordinator.new_session(id, in_queue, out_queue, facility_units)
local rtimes = self.retry_times
-- builds packet retry
-- builds packet retries
if not self.acks.builds then
if rtimes.builds_packet - util.time() <= 0 then
_send_builds()
rtimes.builds_packet = util.time() + RETRY_PERIOD
if not self.acks.fac_builds then
if rtimes.f_builds_packet - util.time() <= 0 then
_send_fac_builds()
rtimes.f_builds_packet = util.time() + RETRY_PERIOD
end
end
if not self.acks.unit_builds then
if rtimes.u_builds_packet - util.time() <= 0 then
_send_unit_builds()
rtimes.u_builds_packet = util.time() + RETRY_PERIOD
end
end
end

View File

@@ -0,0 +1,100 @@
local log = require("scada-common.log")
local rsio = require("scada-common.rsio")
local util = require("scada-common.util")
local rsctl = require("supervisor.session.rsctl")
---@class facility_management
local facility = {}
-- create a new facility management object
function facility.new()
local self = {
induction = {},
redstone = {}
}
-- init redstone RTU I/O controller
local rs_rtu_io_ctl = rsctl.new(self.redstone)
-- unlink disconnected units
---@param sessions table
local function _unlink_disconnected_units(sessions)
util.filter_table(sessions, function (u) return u.is_connected() end)
end
-- PUBLIC FUNCTIONS --
---@class facility
local public = {}
-- ADD/LINK DEVICES --
-- link a redstone RTU session
---@param rs_unit unit_session
function public.add_redstone(rs_unit)
table.insert(self.redstone, rs_unit)
end
-- link an imatrix RTU session
---@param imatrix unit_session
function public.add_imatrix(imatrix)
table.insert(self.induction, imatrix)
end
-- purge devices associated with the given RTU session ID
---@param session integer RTU session ID
function public.purge_rtu_devices(session)
util.filter_table(self.redstone, function (s) return s.get_session_id() ~= session end)
util.filter_table(self.induction, function (s) return s.get_session_id() ~= session end)
end
-- UPDATE --
-- update (iterate) the facility management
function public.update()
-- unlink RTU unit sessions if they are closed
_unlink_disconnected_units(self.induction)
_unlink_disconnected_units(self.redstone)
end
-- READ STATES/PROPERTIES --
-- get build properties of all machines
function public.get_build()
local build = {}
build.induction = {}
for i = 1, #self.induction do
local matrix = self.induction[i] ---@type unit_session
build.induction[matrix.get_device_idx()] = { matrix.get_db().formed, matrix.get_db().build }
end
return build
end
-- get RTU statuses
function public.get_rtu_statuses()
local status = {}
-- status of induction matricies (including tanks)
status.induction = {}
for i = 1, #self.induction do
local matrix = self.induction[i] ---@type unit_session
status.induction[matrix.get_device_idx()] = {
matrix.is_faulted(),
matrix.get_db().formed,
matrix.get_db().state,
matrix.get_db().tanks
}
end
---@todo other RTU statuses
return status
end
return public
end
return facility

View File

@@ -11,7 +11,7 @@ local PROTOCOLS = comms.PROTOCOLS
local RPLC_TYPES = comms.RPLC_TYPES
local SCADA_MGMT_TYPES = comms.SCADA_MGMT_TYPES
local CRDN_COMMANDS = comms.CRDN_COMMANDS
local UNIT_COMMANDS = comms.UNIT_COMMANDS
local print = util.print
local println = util.println
@@ -352,7 +352,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
-- send acknowledgement to coordinator
self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, {
unit = self.for_reactor,
cmd = CRDN_COMMANDS.SET_BURN,
cmd = UNIT_COMMANDS.SET_BURN,
ack = ack
})
elseif pkt.type == RPLC_TYPES.RPS_ENABLE then
@@ -367,7 +367,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
-- send acknowledgement to coordinator
self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, {
unit = self.for_reactor,
cmd = CRDN_COMMANDS.START,
cmd = UNIT_COMMANDS.START,
ack = ack
})
elseif pkt.type == RPLC_TYPES.RPS_SCRAM then
@@ -383,7 +383,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
-- send acknowledgement to coordinator
self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, {
unit = self.for_reactor,
cmd = CRDN_COMMANDS.SCRAM,
cmd = UNIT_COMMANDS.SCRAM,
ack = ack
})
elseif pkt.type == RPLC_TYPES.RPS_ASCRAM then
@@ -435,7 +435,7 @@ function plc.new_session(id, for_reactor, in_queue, out_queue)
-- send acknowledgement to coordinator
self.out_q.push_data(svqtypes.SV_Q_DATA.CRDN_ACK, {
unit = self.for_reactor,
cmd = CRDN_COMMANDS.RESET_RPS,
cmd = UNIT_COMMANDS.RESET_RPS,
ack = ack
})
else

View File

@@ -0,0 +1,39 @@
--
-- Redstone RTU Session I/O Controller
--
local rsctl = {}
-- create a new redstone RTU I/O controller
---@param redstone_rtus table redstone RTU sessions
function rsctl.new(redstone_rtus)
---@class rs_controller
local public = {}
-- write to a digital redstone port (applies to all RTUs)
---@param port IO_PORT
---@param value boolean
function public.digital_write(port, value)
for i = 1, #redstone_rtus do
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
local io = db.io[port] ---@type rs_db_dig_io|nil
if io ~= nil then io.write(value) end
end
end
-- read a digital redstone port<br>
-- this will read from the first one encountered if there are multiple, because there should not be multiple
---@param port IO_PORT
---@return boolean|nil
function public.digital_read(port)
for i = 1, #redstone_rtus do
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
local io = db.io[port] ---@type rs_db_dig_io|nil
if io ~= nil then return io.read() end
end
end
return public
end
return rsctl

View File

@@ -36,16 +36,15 @@ local PERIODICS = {
---@param in_queue mqueue
---@param out_queue mqueue
---@param advertisement table
---@param facility facility
---@param facility_units table
function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units)
function rtu.new_session(id, in_queue, out_queue, advertisement, facility, facility_units)
local log_header = "rtu_session(" .. id .. "): "
local self = {
id = id,
in_q = in_queue,
out_q = out_queue,
modbus_q = mqueue.new(),
f_units = facility_units,
advert = advertisement,
-- connection properties
seq_num = 0,
@@ -72,9 +71,10 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units)
local function _handle_advertisement()
_reset_config()
for i = 1, #self.f_units do
local unit = self.f_units[i] ---@type reactor_unit
unit.purge_rtu_devices(self.id)
for i = 1, #facility_units do
local unit = facility_units[i] ---@type reactor_unit
unit.purge_rtu_devices(id)
facility.purge_rtu_devices(id)
end
for i = 1, #self.advert do
@@ -104,47 +104,61 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units)
if advert_validator.valid() then
advert_validator.assert_min(unit_advert.index, 1)
advert_validator.assert_min(unit_advert.reactor, 1)
advert_validator.assert_max(unit_advert.reactor, #self.f_units)
advert_validator.assert_min(unit_advert.reactor, 0)
advert_validator.assert_max(unit_advert.reactor, #facility_units)
if not advert_validator.valid() then u_type = false end
else
u_type = false
end
local type_string = util.strval(u_type)
if type(u_type) == "number" then type_string = util.strval(comms.advert_type_to_rtu_t(u_type)) end
-- create unit by type
if u_type == false then
-- validation fail
log.debug(log_header .. "advertisement unit validation failure")
else
local target_unit = self.f_units[unit_advert.reactor] ---@type reactor_unit
if unit_advert.reactor > 0 then
local target_unit = facility_units[unit_advert.reactor] ---@type reactor_unit
if u_type == RTU_UNIT_TYPES.REDSTONE then
-- redstone
unit = svrs_redstone.new(self.id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_redstone(unit) end
elseif u_type == RTU_UNIT_TYPES.BOILER_VALVE then
-- boiler (Mekanism 10.1+)
unit = svrs_boilerv.new(self.id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_boiler(unit) end
elseif u_type == RTU_UNIT_TYPES.TURBINE_VALVE then
-- turbine (Mekanism 10.1+)
unit = svrs_turbinev.new(self.id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_turbine(unit) end
elseif u_type == RTU_UNIT_TYPES.IMATRIX then
-- induction matrix
unit = svrs_imatrix.new(self.id, i, unit_advert, self.modbus_q)
elseif u_type == RTU_UNIT_TYPES.SPS then
-- super-critical phase shifter
unit = svrs_sps.new(self.id, i, unit_advert, self.modbus_q)
elseif u_type == RTU_UNIT_TYPES.SNA then
-- solar neutron activator
unit = svrs_sna.new(self.id, i, unit_advert, self.modbus_q)
elseif u_type == RTU_UNIT_TYPES.ENV_DETECTOR then
-- environment detector
unit = svrs_envd.new(self.id, i, unit_advert, self.modbus_q)
if u_type == RTU_UNIT_TYPES.REDSTONE then
-- redstone
unit = svrs_redstone.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_redstone(unit) end
elseif u_type == RTU_UNIT_TYPES.BOILER_VALVE then
-- boiler (Mekanism 10.1+)
unit = svrs_boilerv.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_boiler(unit) end
elseif u_type == RTU_UNIT_TYPES.TURBINE_VALVE then
-- turbine (Mekanism 10.1+)
unit = svrs_turbinev.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then target_unit.add_turbine(unit) end
else
log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-specific RTU type ", type_string))
end
else
log.error(log_header .. "bad advertisement: encountered unsupported RTU type")
if u_type == RTU_UNIT_TYPES.REDSTONE then
-- redstone
unit = svrs_redstone.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then facility.add_redstone(unit) end
elseif u_type == RTU_UNIT_TYPES.IMATRIX then
-- induction matrix
unit = svrs_imatrix.new(id, i, unit_advert, self.modbus_q)
if type(unit) ~= "nil" then facility.add_imatrix(unit) end
elseif u_type == RTU_UNIT_TYPES.SPS then
-- super-critical phase shifter
unit = svrs_sps.new(id, i, unit_advert, self.modbus_q)
elseif u_type == RTU_UNIT_TYPES.SNA then
-- solar neutron activator
unit = svrs_sna.new(id, i, unit_advert, self.modbus_q)
elseif u_type == RTU_UNIT_TYPES.ENV_DETECTOR then
-- environment detector
unit = svrs_envd.new(id, i, unit_advert, self.modbus_q)
else
log.error(util.c(log_header, "bad advertisement: encountered unsupported reactor-independent RTU type ", type_string))
end
end
end
@@ -152,10 +166,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units)
table.insert(self.units, unit)
else
_reset_config()
if type(u_type) == "number" then
local type_string = util.strval(comms.advert_type_to_rtu_t(u_type))
log.error(log_header .. "bad advertisement: error occured while creating a unit (type is " .. type_string .. ")")
end
log.error(util.c(log_header, "bad advertisement: error occured while creating a unit (type is ", type_string, ")"))
break
end
end
@@ -271,7 +282,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units)
-- PUBLIC FUNCTIONS --
-- get the session ID
function public.get_id() return self.id end
function public.get_id() return id end
-- check if a timer matches this session's watchdog
---@param timer number

View File

@@ -2,6 +2,7 @@ local log = require("scada-common.log")
local mqueue = require("scada-common.mqueue")
local util = require("scada-common.util")
local facility = require("supervisor.session.facility")
local svqtypes = require("supervisor.session.svqtypes")
local unit = require("supervisor.session.unit")
@@ -32,7 +33,8 @@ svsessions.SESSION_TYPE = SESSION_TYPE
local self = {
modem = nil,
num_reactors = 0,
facility_units = {},
facility = facility.new(),
units = {},
rtu_sessions = {},
plc_sessions = {},
coord_sessions = {},
@@ -197,10 +199,10 @@ end
function svsessions.init(modem, num_reactors, cooling_conf)
self.modem = modem
self.num_reactors = num_reactors
self.facility_units = {}
self.units = {}
for i = 1, self.num_reactors do
table.insert(self.facility_units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES))
table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES))
end
end
@@ -297,7 +299,7 @@ function svsessions.establish_plc_session(local_port, remote_port, for_reactor,
plc_s.instance = plc.new_session(self.next_plc_id, for_reactor, plc_s.in_queue, plc_s.out_queue)
table.insert(self.plc_sessions, plc_s)
self.facility_units[for_reactor].link_plc_session(plc_s)
self.units[for_reactor].link_plc_session(plc_s)
log.debug("established new PLC session to " .. remote_port .. " with ID " .. self.next_plc_id)
@@ -330,7 +332,7 @@ function svsessions.establish_rtu_session(local_port, remote_port, advertisement
instance = nil ---@type rtu_session
}
rtu_s.instance = rtu.new_session(self.next_rtu_id, rtu_s.in_queue, rtu_s.out_queue, advertisement, self.facility_units)
rtu_s.instance = rtu.new_session(self.next_rtu_id, rtu_s.in_queue, rtu_s.out_queue, advertisement, self.facility, self.units)
table.insert(self.rtu_sessions, rtu_s)
log.debug("established new RTU session to " .. remote_port .. " with ID " .. self.next_rtu_id)
@@ -360,7 +362,7 @@ function svsessions.establish_coord_session(local_port, remote_port, version)
instance = nil ---@type coord_session
}
coord_s.instance = coordinator.new_session(self.next_coord_id, coord_s.in_queue, coord_s.out_queue, self.facility_units)
coord_s.instance = coordinator.new_session(self.next_coord_id, coord_s.in_queue, coord_s.out_queue, self.facility, self.units)
table.insert(self.coord_sessions, coord_s)
log.debug("established new coordinator session to " .. remote_port .. " with ID " .. self.next_coord_id)
@@ -399,9 +401,12 @@ function svsessions.iterate_all()
-- iterate coordinator sessions
_iterate(self.coord_sessions)
-- iterate facility
self.facility.update()
-- iterate units
for i = 1, #self.facility_units do
local u = self.facility_units[i] ---@type reactor_unit
for i = 1, #self.units do
local u = self.units[i] ---@type reactor_unit
u.update()
end
end

View File

@@ -1,9 +1,9 @@
local log = require("scada-common.log")
local rsio = require("scada-common.rsio")
local types = require("scada-common.types")
local util = require("scada-common.util")
local log = require("scada-common.log")
local rsio = require("scada-common.rsio")
local types = require("scada-common.types")
local util = require("scada-common.util")
local qtypes = require("supervisor.session.rtu.qtypes")
local rsctl = require("supervisor.session.rsctl")
---@class reactor_control_unit
local unit = {}
@@ -178,6 +178,9 @@ function unit.new(for_reactor, num_boilers, num_turbines)
}
}
-- init redstone RTU I/O controller
local rs_rtu_io_ctl = rsctl.new(self.redstone)
-- init boiler table fields
for _ = 1, num_boilers do
table.insert(self.db.annunciator.BoilerOnline, false)
@@ -192,9 +195,6 @@ function unit.new(for_reactor, num_boilers, num_turbines)
table.insert(self.db.annunciator.TurbineTrip, false)
end
---@class reactor_unit
local public = {}
-- PRIVATE FUNCTIONS --
--#region time derivative utility functions
@@ -237,26 +237,8 @@ function unit.new(for_reactor, num_boilers, num_turbines)
--#region redstone I/O
-- write to a redstone port
local function __rs_w(port, value)
for i = 1, #self.redstone do
local db = self.redstone[i].get_db() ---@type redstone_session_db
local io = db.io[port] ---@type rs_db_dig_io|nil
if io ~= nil then io.write(value) end
end
end
-- read a redstone port<br>
-- this will read from the first one encountered if there are multiple, because there should not be multiple
---@param port IO_PORT
---@return boolean|nil
local function __rs_r(port)
for i = 1, #self.redstone do
local db = self.redstone[i].get_db() ---@type redstone_session_db
local io = db.io[port] ---@type rs_db_dig_io|nil
if io ~= nil then return io.read() end
end
end
local __rs_w = rs_rtu_io_ctl.digital_write
local __rs_r = rs_rtu_io_ctl.digital_read
-- waste valves
local waste_pu = { open = function () __rs_w(IO.WASTE_PU, true) end, close = function () __rs_w(IO.WASTE_PU, false) end }
@@ -702,6 +684,9 @@ function unit.new(for_reactor, num_boilers, num_turbines)
-- PUBLIC FUNCTIONS --
---@class reactor_unit
local public = {}
-- ADD/LINK DEVICES --
-- link the PLC
@@ -722,7 +707,6 @@ function unit.new(for_reactor, num_boilers, num_turbines)
-- link a redstone RTU session
---@param rs_unit unit_session
function public.add_redstone(rs_unit)
-- insert into list
table.insert(self.redstone, rs_unit)
end
@@ -781,6 +765,7 @@ function unit.new(for_reactor, num_boilers, num_turbines)
-- unlink RTU unit sessions if they are closed
_unlink_disconnected_units(self.boilers)
_unlink_disconnected_units(self.turbines)
_unlink_disconnected_units(self.redstone)
-- update annunciator logic
_update_annunciator()