mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#130 facility data object, some code cleanup, comms protocol changed from 1.0.1 to 1.1.0
This commit is contained in:
parent
41913441d5
commit
03f0216d51
@ -18,7 +18,7 @@ local DEVICE_TYPES = comms.DEVICE_TYPES
|
||||
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
||||
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 coordinator = {}
|
||||
|
||||
@ -309,11 +309,11 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
|
||||
end
|
||||
|
||||
-- send a unit command
|
||||
---@param cmd CRDN_COMMANDS command
|
||||
---@param cmd UNIT_COMMANDS command
|
||||
---@param unit integer unit ID
|
||||
---@param option any? optional options (like burn rate)
|
||||
function public.send_command(cmd, unit, option)
|
||||
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.COMMAND_UNIT, { cmd, unit, option })
|
||||
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.UNIT_CMD, { cmd, unit, option })
|
||||
end
|
||||
|
||||
-- parse a packet
|
||||
@ -388,20 +388,35 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
|
||||
-- handle packet
|
||||
if protocol == PROTOCOLS.SCADA_CRDN then
|
||||
if self.sv_linked then
|
||||
if packet.type == SCADA_CRDN_TYPES.STRUCT_BUILDS then
|
||||
-- record builds
|
||||
if iocontrol.record_builds(packet.data) then
|
||||
if packet.type == SCADA_CRDN_TYPES.FAC_BUILDS then
|
||||
-- record facility builds
|
||||
if iocontrol.record_facility_builds(packet.data) then
|
||||
-- acknowledge receipt of builds
|
||||
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.STRUCT_BUILDS, {})
|
||||
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.FAC_BUILDS, {})
|
||||
else
|
||||
log.error("received invalid SCADA_CRDN build packet")
|
||||
log.error("received invalid FAC_BUILDS packet")
|
||||
end
|
||||
elseif packet.type == SCADA_CRDN_TYPES.FAC_STATUS then
|
||||
-- update facility status
|
||||
if not iocontrol.update_facility_status(packet.data) then
|
||||
log.error("received invalid FAC_STATUS packet")
|
||||
end
|
||||
elseif packet.type == SCADA_CRDN_TYPES.FAC_CMD then
|
||||
-- facility command acknowledgement
|
||||
elseif packet.type == SCADA_CRDN_TYPES.UNIT_BUILDS then
|
||||
-- record builds
|
||||
if iocontrol.record_unit_builds(packet.data) then
|
||||
-- acknowledge receipt of builds
|
||||
_send_sv(PROTOCOLS.SCADA_CRDN, SCADA_CRDN_TYPES.UNIT_BUILDS, {})
|
||||
else
|
||||
log.error("received invalid UNIT_BUILDS packet")
|
||||
end
|
||||
elseif packet.type == SCADA_CRDN_TYPES.UNIT_STATUSES then
|
||||
-- update statuses
|
||||
if not iocontrol.update_statuses(packet.data) then
|
||||
log.error("received invalid SCADA_CRDN unit statuses packet")
|
||||
if not iocontrol.update_unit_statuses(packet.data) then
|
||||
log.error("received invalid UNIT_STATUSES packet")
|
||||
end
|
||||
elseif packet.type == SCADA_CRDN_TYPES.COMMAND_UNIT then
|
||||
elseif packet.type == SCADA_CRDN_TYPES.UNIT_CMD then
|
||||
-- unit command acknowledgement
|
||||
if packet.length == 3 then
|
||||
local cmd = packet.data[1]
|
||||
@ -411,17 +426,17 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
|
||||
local unit = iocontrol.get_db().units[unit_id] ---@type ioctl_entry
|
||||
|
||||
if unit ~= nil then
|
||||
if cmd == CRDN_COMMANDS.SCRAM then
|
||||
if cmd == UNIT_COMMANDS.SCRAM then
|
||||
unit.scram_ack(ack)
|
||||
elseif cmd == CRDN_COMMANDS.START then
|
||||
elseif cmd == UNIT_COMMANDS.START then
|
||||
unit.start_ack(ack)
|
||||
elseif cmd == CRDN_COMMANDS.RESET_RPS then
|
||||
elseif cmd == UNIT_COMMANDS.RESET_RPS then
|
||||
unit.reset_rps_ack(ack)
|
||||
elseif cmd == CRDN_COMMANDS.SET_BURN then
|
||||
elseif cmd == UNIT_COMMANDS.SET_BURN then
|
||||
unit.set_burn_ack(ack)
|
||||
elseif cmd == CRDN_COMMANDS.SET_WASTE then
|
||||
elseif cmd == UNIT_COMMANDS.SET_WASTE then
|
||||
unit.set_waste_ack(ack)
|
||||
elseif cmd == CRDN_COMMANDS.ACK_ALL_ALARMS then
|
||||
elseif cmd == UNIT_COMMANDS.ACK_ALL_ALARMS then
|
||||
unit.ack_alarms_ack(ack)
|
||||
else
|
||||
log.debug(util.c("received command ack with unknown command ", cmd))
|
||||
@ -432,8 +447,6 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa
|
||||
else
|
||||
log.debug("SCADA_CRDN unit command ack packet length mismatch")
|
||||
end
|
||||
elseif packet.type == SCADA_CRDN_TYPES.ALARM then
|
||||
---@todo alarm/architecture handling
|
||||
else
|
||||
log.warning("received unknown SCADA_CRDN packet type " .. packet.type)
|
||||
end
|
||||
|
@ -6,7 +6,7 @@ local util = require("scada-common.util")
|
||||
|
||||
local sounder = require("coordinator.sounder")
|
||||
|
||||
local CRDN_COMMANDS = comms.CRDN_COMMANDS
|
||||
local UNIT_COMMANDS = comms.UNIT_COMMANDS
|
||||
|
||||
local ALARM_STATE = types.ALARM_STATE
|
||||
|
||||
@ -22,19 +22,29 @@ local io = {}
|
||||
function iocontrol.init(conf, comms)
|
||||
io.facility = {
|
||||
scram = false,
|
||||
num_units = conf.num_units,
|
||||
ps = psil.create()
|
||||
num_units = conf.num_units, ---@type integer
|
||||
ps = psil.create(),
|
||||
|
||||
induction_ps_tbl = {},
|
||||
induction_data_tbl = {}
|
||||
}
|
||||
|
||||
-- create induction tables (max 1 per unit, preferably 1 total)
|
||||
for _ = 1, conf.num_units do
|
||||
local data = {} ---@type imatrix_session_db
|
||||
table.insert(io.facility.induction_ps_tbl, psil.create())
|
||||
table.insert(io.facility.induction_data_tbl, data)
|
||||
end
|
||||
|
||||
io.units = {}
|
||||
for i = 1, conf.num_units do
|
||||
local function ack(alarm)
|
||||
comms.send_command(CRDN_COMMANDS.ACK_ALARM, i, alarm)
|
||||
comms.send_command(UNIT_COMMANDS.ACK_ALARM, i, alarm)
|
||||
log.debug(util.c("UNIT[", i, "]: ACK ALARM ", alarm))
|
||||
end
|
||||
|
||||
local function reset(alarm)
|
||||
comms.send_command(CRDN_COMMANDS.RESET_ALARM, i, alarm)
|
||||
comms.send_command(UNIT_COMMANDS.RESET_ALARM, i, alarm)
|
||||
log.debug(util.c("UNIT[", i, "]: RESET ALARM ", alarm))
|
||||
end
|
||||
|
||||
@ -107,33 +117,33 @@ function iocontrol.init(conf, comms)
|
||||
|
||||
function entry.start()
|
||||
entry.control_state = true
|
||||
comms.send_command(CRDN_COMMANDS.START, i)
|
||||
comms.send_command(UNIT_COMMANDS.START, i)
|
||||
log.debug(util.c("UNIT[", i, "]: START"))
|
||||
end
|
||||
|
||||
function entry.scram()
|
||||
entry.control_state = false
|
||||
comms.send_command(CRDN_COMMANDS.SCRAM, i)
|
||||
comms.send_command(UNIT_COMMANDS.SCRAM, i)
|
||||
log.debug(util.c("UNIT[", i, "]: SCRAM"))
|
||||
end
|
||||
|
||||
function entry.reset_rps()
|
||||
comms.send_command(CRDN_COMMANDS.RESET_RPS, i)
|
||||
comms.send_command(UNIT_COMMANDS.RESET_RPS, i)
|
||||
log.debug(util.c("UNIT[", i, "]: RESET_RPS"))
|
||||
end
|
||||
|
||||
function entry.ack_alarms()
|
||||
comms.send_command(CRDN_COMMANDS.ACK_ALL_ALARMS, i)
|
||||
comms.send_command(UNIT_COMMANDS.ACK_ALL_ALARMS, i)
|
||||
log.debug(util.c("UNIT[", i, "]: ACK_ALL_ALARMS"))
|
||||
end
|
||||
|
||||
function entry.set_burn(rate)
|
||||
comms.send_command(CRDN_COMMANDS.SET_BURN, i, rate)
|
||||
comms.send_command(UNIT_COMMANDS.SET_BURN, i, rate)
|
||||
log.debug(util.c("UNIT[", i, "]: SET_BURN = ", rate))
|
||||
end
|
||||
|
||||
function entry.set_waste(mode)
|
||||
comms.send_command(CRDN_COMMANDS.SET_WASTE, i, mode)
|
||||
comms.send_command(UNIT_COMMANDS.SET_WASTE, i, mode)
|
||||
log.debug(util.c("UNIT[", i, "]: SET_WASTE = ", mode))
|
||||
end
|
||||
|
||||
@ -158,18 +168,55 @@ function iocontrol.init(conf, comms)
|
||||
end
|
||||
end
|
||||
|
||||
-- populate structure builds
|
||||
-- populate facility structure builds
|
||||
---@param build table
|
||||
---@return boolean valid
|
||||
function iocontrol.record_facility_builds(build)
|
||||
if type(build) == "table" then
|
||||
local fac = io.facility
|
||||
|
||||
-- induction matricies
|
||||
if type(build.induction) == "table" then
|
||||
for id, matrix in pairs(build.induction) do
|
||||
if type(fac.induction_data_tbl[id]) == "table" then
|
||||
fac.induction_data_tbl[id].formed = matrix[1] ---@type boolean
|
||||
fac.induction_data_tbl[id].build = matrix[2] ---@type table
|
||||
|
||||
fac.induction_ps_tbl[id].publish("formed", matrix[1])
|
||||
|
||||
for key, val in pairs(fac.induction_data_tbl[id].build) do
|
||||
fac.induction_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c("iocontrol.record_facility_builds: invalid induction matrix id ", id))
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
log.error("facility builds not a table")
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- populate unit structure builds
|
||||
---@param builds table
|
||||
---@return boolean valid
|
||||
function iocontrol.record_builds(builds)
|
||||
if #builds ~= #io.units then
|
||||
log.error("number of provided unit builds does not match expected number of units")
|
||||
return false
|
||||
else
|
||||
function iocontrol.record_unit_builds(builds)
|
||||
-- note: if not all units and RTUs are connected, some will be nil
|
||||
for i = 1, #builds do
|
||||
local unit = io.units[i] ---@type ioctl_entry
|
||||
local build = builds[i]
|
||||
for id, build in pairs(builds) do
|
||||
local unit = io.units[id] ---@type ioctl_entry
|
||||
|
||||
if type(build) ~= "table" then
|
||||
log.error(util.c("corrupted unit builds provided, unit ", id, " not a table"))
|
||||
return false
|
||||
elseif type(unit) ~= "table" then
|
||||
log.error(util.c("corrupted unit builds provided, invalid unit ", id))
|
||||
return false
|
||||
end
|
||||
|
||||
local log_header = util.c("iocontrol.record_unit_builds[unit ", id, "]: ")
|
||||
|
||||
-- reactor build
|
||||
if type(build.reactor) == "table" then
|
||||
@ -186,32 +233,110 @@ function iocontrol.record_builds(builds)
|
||||
|
||||
-- boiler builds
|
||||
if type(build.boilers) == "table" then
|
||||
for id, boiler in pairs(build.boilers) do
|
||||
unit.boiler_data_tbl[id].formed = boiler[1] ---@type boolean
|
||||
unit.boiler_data_tbl[id].build = boiler[2] ---@type table
|
||||
for b_id, boiler in pairs(build.boilers) do
|
||||
if type(unit.boiler_data_tbl[b_id]) == "table" then
|
||||
unit.boiler_data_tbl[b_id].formed = boiler[1] ---@type boolean
|
||||
unit.boiler_data_tbl[b_id].build = boiler[2] ---@type table
|
||||
|
||||
unit.boiler_ps_tbl[id].publish("formed", boiler[1])
|
||||
unit.boiler_ps_tbl[b_id].publish("formed", boiler[1])
|
||||
|
||||
for key, val in pairs(unit.boiler_data_tbl[id].build) do
|
||||
unit.boiler_ps_tbl[id].publish(key, val)
|
||||
for key, val in pairs(unit.boiler_data_tbl[b_id].build) do
|
||||
unit.boiler_ps_tbl[b_id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid boiler id ", b_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- turbine builds
|
||||
if type(build.turbines) == "table" then
|
||||
for id, turbine in pairs(build.turbines) do
|
||||
unit.turbine_data_tbl[id].formed = turbine[1] ---@type boolean
|
||||
unit.turbine_data_tbl[id].build = turbine[2] ---@type table
|
||||
for t_id, turbine in pairs(build.turbines) do
|
||||
if type(unit.turbine_data_tbl[t_id]) == "table" then
|
||||
unit.turbine_data_tbl[t_id].formed = turbine[1] ---@type boolean
|
||||
unit.turbine_data_tbl[t_id].build = turbine[2] ---@type table
|
||||
|
||||
unit.turbine_ps_tbl[id].publish("formed", turbine[1])
|
||||
unit.turbine_ps_tbl[t_id].publish("formed", turbine[1])
|
||||
|
||||
for key, val in pairs(unit.turbine_data_tbl[id].build) do
|
||||
unit.turbine_ps_tbl[id].publish(key, val)
|
||||
for key, val in pairs(unit.turbine_data_tbl[t_id].build) do
|
||||
unit.turbine_ps_tbl[t_id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid turbine id ", t_id))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- update facility status
|
||||
---@param status table
|
||||
---@return boolean valid
|
||||
function iocontrol.update_facility_status(status)
|
||||
local log_header = util.c("iocontrol.update_facility_status: ")
|
||||
if type(status) ~= "table" then
|
||||
log.debug(log_header .. "status not a table")
|
||||
return false
|
||||
else
|
||||
local fac = io.facility
|
||||
|
||||
-- RTU statuses
|
||||
|
||||
local rtu_statuses = status[1]
|
||||
|
||||
if type(rtu_statuses) == "table" then
|
||||
-- induction matricies statuses
|
||||
if type(rtu_statuses.induction) == "table" then
|
||||
for id = 1, #fac.induction_ps_tbl do
|
||||
if rtu_statuses.induction[id] == nil then
|
||||
-- disconnected
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 1)
|
||||
end
|
||||
end
|
||||
|
||||
for id, matrix in pairs(rtu_statuses.induction) do
|
||||
if type(fac.induction_data_tbl[id]) == "table" then
|
||||
local rtu_faulted = matrix[1] ---@type boolean
|
||||
fac.induction_data_tbl[id].formed = matrix[2] ---@type boolean
|
||||
fac.induction_data_tbl[id].state = matrix[3] ---@type table
|
||||
fac.induction_data_tbl[id].tanks = matrix[4] ---@type table
|
||||
|
||||
local data = fac.induction_data_tbl[id] ---@type imatrix_session_db
|
||||
|
||||
fac.induction_ps_tbl[id].publish("formed", data.formed)
|
||||
fac.induction_ps_tbl[id].publish("faulted", rtu_faulted)
|
||||
|
||||
if data.formed then
|
||||
if rtu_faulted then
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
elseif data.tanks.energy_fill >= 0.99 then
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 6) -- full
|
||||
elseif data.tanks.energy_fill <= 0.01 then
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 5) -- empty
|
||||
else
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 4) -- on-line
|
||||
end
|
||||
else
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(fac.induction_data_tbl[id].state) do
|
||||
fac.induction_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
|
||||
for key, val in pairs(fac.induction_data_tbl[id].tanks) do
|
||||
fac.induction_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid induction matrix id ", id))
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug(log_header .. "induction matrix list not a table")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
@ -220,16 +345,17 @@ end
|
||||
-- update unit statuses
|
||||
---@param statuses table
|
||||
---@return boolean valid
|
||||
function iocontrol.update_statuses(statuses)
|
||||
function iocontrol.update_unit_statuses(statuses)
|
||||
if type(statuses) ~= "table" then
|
||||
log.debug("iocontrol.update_statuses: unit statuses not a table")
|
||||
log.debug("iocontrol.update_unit_statuses: unit statuses not a table")
|
||||
return false
|
||||
elseif #statuses ~= #io.units then
|
||||
log.debug("iocontrol.update_statuses: number of provided unit statuses does not match expected number of units")
|
||||
log.debug("iocontrol.update_unit_statuses: number of provided unit statuses does not match expected number of units")
|
||||
return false
|
||||
else
|
||||
-- get all unit statuses
|
||||
for i = 1, #statuses do
|
||||
local log_header = util.c("iocontrol.update_statuses[unit ", i, "]: ")
|
||||
local log_header = util.c("iocontrol.update_unit_statuses[unit ", i, "]: ")
|
||||
local unit = io.units[i] ---@type ioctl_entry
|
||||
local status = statuses[i]
|
||||
|
||||
@ -264,18 +390,18 @@ function iocontrol.update_statuses(statuses)
|
||||
unit.reactor_data.mek_status = mek_status ---@type mek_status
|
||||
|
||||
if unit.reactor_data.mek_status.status then
|
||||
unit.reactor_ps.publish("computed_status", 3) -- running
|
||||
unit.reactor_ps.publish("computed_status", 5) -- running
|
||||
else
|
||||
if unit.reactor_data.no_reactor then
|
||||
unit.reactor_ps.publish("computed_status", 5) -- faulted
|
||||
unit.reactor_ps.publish("computed_status", 3) -- faulted
|
||||
elseif not unit.reactor_data.formed then
|
||||
unit.reactor_ps.publish("computed_status", 6) -- multiblock not formed
|
||||
unit.reactor_ps.publish("computed_status", 2) -- multiblock not formed
|
||||
elseif unit.reactor_data.rps_status.force_dis then
|
||||
unit.reactor_ps.publish("computed_status", 7) -- reactor force disabled
|
||||
elseif unit.reactor_data.rps_tripped and unit.reactor_data.rps_trip_cause ~= "manual" then
|
||||
unit.reactor_ps.publish("computed_status", 4) -- SCRAM
|
||||
unit.reactor_ps.publish("computed_status", 6) -- SCRAM
|
||||
else
|
||||
unit.reactor_ps.publish("computed_status", 2) -- disabled
|
||||
unit.reactor_ps.publish("computed_status", 4) -- disabled
|
||||
end
|
||||
end
|
||||
|
||||
@ -307,7 +433,7 @@ function iocontrol.update_statuses(statuses)
|
||||
if type(rtu_statuses) == "table" then
|
||||
-- boiler statuses
|
||||
if type(rtu_statuses.boilers) == "table" then
|
||||
for id = 1, #unit.boiler_data_tbl do
|
||||
for id = 1, #unit.boiler_ps_tbl do
|
||||
if rtu_statuses.boilers[i] == nil then
|
||||
-- disconnected
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 1)
|
||||
@ -315,6 +441,7 @@ function iocontrol.update_statuses(statuses)
|
||||
end
|
||||
|
||||
for id, boiler in pairs(rtu_statuses.boilers) do
|
||||
if type(unit.boiler_data_tbl[id]) == "table" then
|
||||
local rtu_faulted = boiler[1] ---@type boolean
|
||||
unit.boiler_data_tbl[id].formed = boiler[2] ---@type boolean
|
||||
unit.boiler_data_tbl[id].state = boiler[3] ---@type table
|
||||
@ -327,14 +454,14 @@ function iocontrol.update_statuses(statuses)
|
||||
|
||||
if data.formed then
|
||||
if rtu_faulted then
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 4) -- faulted
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
elseif data.state.boil_rate > 0 then
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 3) -- active
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 5) -- active
|
||||
else
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 2) -- idle
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 4) -- idle
|
||||
end
|
||||
else
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 5) -- not formed
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.boiler_data_tbl[id].state) do
|
||||
@ -344,6 +471,9 @@ function iocontrol.update_statuses(statuses)
|
||||
for key, val in pairs(unit.boiler_data_tbl[id].tanks) do
|
||||
unit.boiler_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid boiler id ", id))
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug(log_header .. "boiler list not a table")
|
||||
@ -359,6 +489,7 @@ function iocontrol.update_statuses(statuses)
|
||||
end
|
||||
|
||||
for id, turbine in pairs(rtu_statuses.turbines) do
|
||||
if type(unit.turbine_data_tbl[id]) == "table" then
|
||||
local rtu_faulted = turbine[1] ---@type boolean
|
||||
unit.turbine_data_tbl[id].formed = turbine[2] ---@type boolean
|
||||
unit.turbine_data_tbl[id].state = turbine[3] ---@type table
|
||||
@ -371,16 +502,16 @@ function iocontrol.update_statuses(statuses)
|
||||
|
||||
if data.formed then
|
||||
if data.tanks.energy_fill >= 0.99 then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 4) -- trip
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 6) -- trip
|
||||
elseif rtu_faulted then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 5) -- faulted
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
elseif data.state.flow_rate < 100 then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 2) -- idle
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 4) -- idle
|
||||
else
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 3) -- active
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 5) -- active
|
||||
end
|
||||
else
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 6) -- not formed
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.turbine_data_tbl[id].state) do
|
||||
@ -390,6 +521,9 @@ function iocontrol.update_statuses(statuses)
|
||||
for key, val in pairs(unit.turbine_data_tbl[id].tanks) do
|
||||
unit.turbine_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid turbine id ", id))
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug(log_header .. "turbine list not a table")
|
||||
|
@ -19,7 +19,7 @@ local iocontrol = require("coordinator.iocontrol")
|
||||
local renderer = require("coordinator.renderer")
|
||||
local sounder = require("coordinator.sounder")
|
||||
|
||||
local COORDINATOR_VERSION = "beta-v0.7.7"
|
||||
local COORDINATOR_VERSION = "beta-v0.8.0"
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
@ -365,4 +365,8 @@ local function main()
|
||||
log.info("exited")
|
||||
end
|
||||
|
||||
if not xpcall(main, crash.handler) then crash.exit() end
|
||||
if not xpcall(main, crash.handler) then
|
||||
pcall(renderer.close_ui)
|
||||
pcall(sounder.stop)
|
||||
crash.exit()
|
||||
end
|
||||
|
@ -39,6 +39,14 @@ style.reactor = {
|
||||
color = cpair(colors.black, colors.yellow),
|
||||
text = "PLC OFF-LINE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "PLC FAULT"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.white, colors.gray),
|
||||
text = "DISABLED"
|
||||
@ -51,14 +59,6 @@ style.reactor = {
|
||||
color = cpair(colors.black, colors.red),
|
||||
text = "SCRAMMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "PLC FAULT"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.red),
|
||||
text = "FORCE DISABLED"
|
||||
@ -73,14 +73,6 @@ style.boiler = {
|
||||
color = cpair(colors.black, colors.yellow),
|
||||
text = "OFF-LINE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.white, colors.gray),
|
||||
text = "IDLE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.green),
|
||||
text = "ACTIVE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "RTU FAULT"
|
||||
@ -88,6 +80,14 @@ style.boiler = {
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.white, colors.gray),
|
||||
text = "IDLE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.green),
|
||||
text = "ACTIVE"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,6 +99,14 @@ style.turbine = {
|
||||
color = cpair(colors.black, colors.yellow),
|
||||
text = "OFF-LINE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "RTU FAULT"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.white, colors.gray),
|
||||
text = "IDLE"
|
||||
@ -110,15 +118,37 @@ style.turbine = {
|
||||
{
|
||||
color = cpair(colors.black, colors.red),
|
||||
text = "TRIP"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
style.imatrix = {
|
||||
-- induction matrix states
|
||||
states = {
|
||||
{
|
||||
color = cpair(colors.black, colors.yellow),
|
||||
text = "OFF-LINE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "RTU FAULT"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.orange),
|
||||
text = "NOT FORMED"
|
||||
}
|
||||
color = cpair(colors.white, colors.green),
|
||||
text = "ONLINE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.yellow),
|
||||
text = "LOW CHARGE"
|
||||
},
|
||||
{
|
||||
color = cpair(colors.black, colors.red),
|
||||
text = "HIGH CHARGE"
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ local config = require("reactor-plc.config")
|
||||
local plc = require("reactor-plc.plc")
|
||||
local threads = require("reactor-plc.threads")
|
||||
|
||||
local R_PLC_VERSION = "beta-v0.9.9"
|
||||
local R_PLC_VERSION = "beta-v0.9.10"
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
|
@ -25,7 +25,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.9.9"
|
||||
local RTU_VERSION = "beta-v0.9.10"
|
||||
|
||||
local rtu_t = types.rtu_t
|
||||
|
||||
|
@ -12,7 +12,7 @@ local rtu_t = types.rtu_t
|
||||
|
||||
local insert = table.insert
|
||||
|
||||
comms.version = "1.0.1"
|
||||
comms.version = "1.1.0"
|
||||
|
||||
---@alias PROTOCOLS integer
|
||||
local PROTOCOLS = {
|
||||
@ -62,14 +62,16 @@ local ESTABLISH_ACK = {
|
||||
|
||||
---@alias SCADA_CRDN_TYPES integer
|
||||
local SCADA_CRDN_TYPES = {
|
||||
STRUCT_BUILDS = 0, -- mekanism structure builds
|
||||
UNIT_STATUSES = 1, -- state of reactor units
|
||||
COMMAND_UNIT = 2, -- command a reactor unit
|
||||
ALARM = 3 -- alarm signaling
|
||||
FAC_BUILDS = 0, -- facility RTU builds
|
||||
FAC_STATUS = 1, -- state of facility and facility devices
|
||||
FAC_CMD = 2, -- faility command
|
||||
UNIT_BUILDS = 3, -- build of each reactor unit (reactor + RTUs)
|
||||
UNIT_STATUSES = 4, -- state of each of the reactor units
|
||||
UNIT_CMD = 5 -- command a reactor unit
|
||||
}
|
||||
|
||||
---@alias CRDN_COMMANDS integer
|
||||
local CRDN_COMMANDS = {
|
||||
---@alias UNIT_COMMANDS integer
|
||||
local UNIT_COMMANDS = {
|
||||
SCRAM = 0, -- SCRAM the reactor
|
||||
START = 1, -- start the reactor
|
||||
RESET_RPS = 2, -- reset the RPS
|
||||
@ -102,7 +104,7 @@ comms.RPLC_TYPES = RPLC_TYPES
|
||||
comms.ESTABLISH_ACK = ESTABLISH_ACK
|
||||
comms.SCADA_MGMT_TYPES = SCADA_MGMT_TYPES
|
||||
comms.SCADA_CRDN_TYPES = SCADA_CRDN_TYPES
|
||||
comms.CRDN_COMMANDS = CRDN_COMMANDS
|
||||
comms.UNIT_COMMANDS = UNIT_COMMANDS
|
||||
comms.CAPI_TYPES = CAPI_TYPES
|
||||
comms.RTU_UNIT_TYPES = RTU_UNIT_TYPES
|
||||
|
||||
@ -484,10 +486,12 @@ function comms.crdn_packet()
|
||||
|
||||
-- check that type is known
|
||||
local function _crdn_type_valid()
|
||||
return self.type == SCADA_CRDN_TYPES.STRUCT_BUILDS or
|
||||
return self.type == SCADA_CRDN_TYPES.FAC_BUILDS or
|
||||
self.type == SCADA_CRDN_TYPES.FAC_STATUS or
|
||||
self.type == SCADA_CRDN_TYPES.FAC_CMD or
|
||||
self.type == SCADA_CRDN_TYPES.UNIT_BUILDS or
|
||||
self.type == SCADA_CRDN_TYPES.UNIT_STATUSES or
|
||||
self.type == SCADA_CRDN_TYPES.COMMAND_UNIT or
|
||||
self.type == SCADA_CRDN_TYPES.ALARM
|
||||
self.type == SCADA_CRDN_TYPES.UNIT_STATUSES
|
||||
end
|
||||
|
||||
-- make a coordinator packet
|
||||
|
@ -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
|
||||
|
100
supervisor/session/facility.lua
Normal file
100
supervisor/session/facility.lua
Normal 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
|
@ -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
|
||||
|
39
supervisor/session/rsctl.lua
Normal file
39
supervisor/session/rsctl.lua
Normal 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
|
@ -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)
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
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
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
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(self.id, i, unit_advert, self.modbus_q)
|
||||
unit = svrs_envd.new(id, i, unit_advert, self.modbus_q)
|
||||
else
|
||||
log.error(log_header .. "bad advertisement: encountered unsupported RTU type")
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -3,7 +3,7 @@ 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()
|
||||
|
@ -14,7 +14,7 @@ local svsessions = require("supervisor.session.svsessions")
|
||||
local config = require("supervisor.config")
|
||||
local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local SUPERVISOR_VERSION = "beta-v0.8.5"
|
||||
local SUPERVISOR_VERSION = "beta-v0.9.0"
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
|
Loading…
Reference in New Issue
Block a user