mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#272 basic dynamic tank data in supervisor and coordinator
This commit is contained in:
parent
a164c18a50
commit
47bda73afe
@ -91,6 +91,9 @@ function iocontrol.init(conf, comms)
|
||||
sps_ps_tbl = {},
|
||||
sps_data_tbl = {},
|
||||
|
||||
tank_ps_tbl = {},
|
||||
tank_data_tbl = {},
|
||||
|
||||
env_d_ps = psil.create(),
|
||||
env_d_data = {}
|
||||
}
|
||||
@ -181,7 +184,10 @@ function iocontrol.init(conf, comms)
|
||||
boiler_data_tbl = {},
|
||||
|
||||
turbine_ps_tbl = {},
|
||||
turbine_data_tbl = {}
|
||||
turbine_data_tbl = {},
|
||||
|
||||
tank_ps_tbl = {},
|
||||
tank_data_tbl = {}
|
||||
}
|
||||
|
||||
-- create boiler tables
|
||||
@ -208,6 +214,8 @@ function iocontrol.init(conf, comms)
|
||||
process.init(io, comms)
|
||||
end
|
||||
|
||||
--#region Front Panel PSIL
|
||||
|
||||
-- toggle heartbeat indicator
|
||||
function iocontrol.heartbeat() io.fp.ps.toggle("heartbeat") end
|
||||
|
||||
@ -262,6 +270,36 @@ function iocontrol.fp_pkt_rtt(session_id, rtt)
|
||||
end
|
||||
end
|
||||
|
||||
--#endregion
|
||||
|
||||
--#region Builds
|
||||
|
||||
-- record and publish multiblock RTU build data
|
||||
---@param id integer
|
||||
---@param entry table
|
||||
---@param data_tbl table
|
||||
---@param ps_tbl table
|
||||
---@param create boolean? true to create an entry if non exists, false to fail on missing
|
||||
---@return boolean ok true if data saved, false if invalid ID
|
||||
local function _record_multiblock_build(id, entry, data_tbl, ps_tbl, create)
|
||||
local exists = type(data_tbl[id]) == "table"
|
||||
if exists or create then
|
||||
if not exists then
|
||||
ps_tbl[id] = psil.create()
|
||||
data_tbl[id] = {}
|
||||
end
|
||||
|
||||
data_tbl[id].formed = entry[1] ---@type boolean
|
||||
data_tbl[id].build = entry[2] ---@type table
|
||||
|
||||
ps_tbl[id].publish("formed", entry[1])
|
||||
|
||||
for key, val in pairs(data_tbl[id].build) do ps_tbl[id].publish(key, val) end
|
||||
end
|
||||
|
||||
return exists or (create == true)
|
||||
end
|
||||
|
||||
-- populate facility structure builds
|
||||
---@param build table
|
||||
---@return boolean valid
|
||||
@ -274,16 +312,7 @@ function iocontrol.record_facility_builds(build)
|
||||
-- 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
|
||||
if not _record_multiblock_build(id, matrix, fac.induction_data_tbl, fac.induction_ps_tbl) then
|
||||
log.debug(util.c("iocontrol.record_facility_builds: invalid induction matrix id ", id))
|
||||
valid = false
|
||||
end
|
||||
@ -293,21 +322,19 @@ function iocontrol.record_facility_builds(build)
|
||||
-- SPS
|
||||
if type(build.sps) == "table" then
|
||||
for id, sps in pairs(build.sps) do
|
||||
if type(fac.sps_data_tbl[id]) == "table" then
|
||||
fac.sps_data_tbl[id].formed = sps[1] ---@type boolean
|
||||
fac.sps_data_tbl[id].build = sps[2] ---@type table
|
||||
|
||||
fac.sps_ps_tbl[id].publish("formed", sps[1])
|
||||
|
||||
for key, val in pairs(fac.sps_data_tbl[id].build) do
|
||||
fac.sps_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
else
|
||||
if not _record_multiblock_build(id, sps, fac.sps_data_tbl, fac.sps_ps_tbl) then
|
||||
log.debug(util.c("iocontrol.record_facility_builds: invalid SPS id ", id))
|
||||
valid = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- dynamic tanks
|
||||
if type(build.tanks) == "table" then
|
||||
for id, tank in pairs(build.tanks) do
|
||||
_record_multiblock_build(id, tank, fac.tank_data_tbl, fac.tank_ps_tbl, true)
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug("facility builds not a table")
|
||||
valid = false
|
||||
@ -351,16 +378,7 @@ function iocontrol.record_unit_builds(builds)
|
||||
-- boiler builds
|
||||
if type(build.boilers) == "table" then
|
||||
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[b_id].publish("formed", boiler[1])
|
||||
|
||||
for key, val in pairs(unit.boiler_data_tbl[b_id].build) do
|
||||
unit.boiler_ps_tbl[b_id].publish(key, val)
|
||||
end
|
||||
else
|
||||
if not _record_multiblock_build(b_id, boiler, unit.boiler_data_tbl, unit.boiler_ps_tbl) then
|
||||
log.debug(util.c(log_header, "invalid boiler id ", b_id))
|
||||
valid = false
|
||||
end
|
||||
@ -370,27 +388,49 @@ function iocontrol.record_unit_builds(builds)
|
||||
-- turbine builds
|
||||
if type(build.turbines) == "table" then
|
||||
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[t_id].publish("formed", turbine[1])
|
||||
|
||||
for key, val in pairs(unit.turbine_data_tbl[t_id].build) do
|
||||
unit.turbine_ps_tbl[t_id].publish(key, val)
|
||||
end
|
||||
else
|
||||
if not _record_multiblock_build(t_id, turbine, unit.turbine_data_tbl, unit.turbine_ps_tbl) then
|
||||
log.debug(util.c(log_header, "invalid turbine id ", t_id))
|
||||
valid = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- dynamic tank builds
|
||||
if type(build.tanks) == "table" then
|
||||
for d_id, d_tank in pairs(build.tanks) do
|
||||
_record_multiblock_build(d_id, d_tank, unit.tank_data_tbl, unit.tank_ps_tbl, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return valid
|
||||
end
|
||||
|
||||
--#endregion
|
||||
|
||||
--#region Statuses
|
||||
|
||||
-- record and publish multiblock status data
|
||||
---@param entry any
|
||||
---@param data imatrix_session_db|sps_session_db|dynamicv_session_db|turbinev_session_db|boilerv_session_db
|
||||
---@param ps psil
|
||||
---@return boolean is_faulted
|
||||
local function _record_multiblock_status(entry, data, ps)
|
||||
local is_faulted = entry[1] ---@type boolean
|
||||
data.formed = entry[2] ---@type boolean
|
||||
data.state = entry[3] ---@type table
|
||||
data.tanks = entry[4] ---@type table
|
||||
|
||||
ps.publish("formed", data.formed)
|
||||
ps.publish("faulted", is_faulted)
|
||||
|
||||
for key, val in pairs(data.state) do ps.publish(key, val) end
|
||||
for key, val in pairs(data.tanks) do ps.publish(key, val) end
|
||||
|
||||
return is_faulted
|
||||
end
|
||||
|
||||
-- update facility status
|
||||
---@param status table
|
||||
---@return boolean valid
|
||||
@ -498,36 +538,23 @@ function iocontrol.update_facility_status(status)
|
||||
|
||||
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
|
||||
local ps = fac.induction_ps_tbl[id] ---@type psil
|
||||
|
||||
local data = fac.induction_data_tbl[id] ---@type imatrix_session_db
|
||||
local rtu_faulted = _record_multiblock_status(matrix, data, ps)
|
||||
|
||||
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
|
||||
if rtu_faulted then
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.tanks.energy_fill >= 0.99 then
|
||||
ps.publish("computed_status", 6) -- full
|
||||
elseif data.tanks.energy_fill <= 0.01 then
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 5) -- empty
|
||||
ps.publish("computed_status", 5) -- empty
|
||||
else
|
||||
fac.induction_ps_tbl[id].publish("computed_status", 4) -- on-line
|
||||
ps.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)
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid induction matrix id ", id))
|
||||
@ -549,31 +576,23 @@ function iocontrol.update_facility_status(status)
|
||||
|
||||
for id, sps in pairs(rtu_statuses.sps) do
|
||||
if type(fac.sps_data_tbl[id]) == "table" then
|
||||
local rtu_faulted = sps[1] ---@type boolean
|
||||
fac.sps_data_tbl[id].formed = sps[2] ---@type boolean
|
||||
fac.sps_data_tbl[id].state = sps[3] ---@type table
|
||||
fac.sps_data_tbl[id].tanks = sps[4] ---@type table
|
||||
local data = fac.sps_data_tbl[id] ---@type sps_session_db
|
||||
local ps = fac.sps_ps_tbl[id] ---@type psil
|
||||
|
||||
local data = fac.sps_data_tbl[id] ---@type sps_session_db
|
||||
local rtu_faulted = _record_multiblock_status(sps, data, ps)
|
||||
|
||||
fac.sps_ps_tbl[id].publish("formed", data.formed)
|
||||
fac.sps_ps_tbl[id].publish("faulted", rtu_faulted)
|
||||
|
||||
if data.formed then
|
||||
if rtu_faulted then
|
||||
fac.sps_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
elseif data.state.process_rate > 0 then
|
||||
fac.sps_ps_tbl[id].publish("computed_status", 5) -- active
|
||||
if rtu_faulted then
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.state.process_rate > 0 then
|
||||
ps.publish("computed_status", 5) -- active
|
||||
else
|
||||
fac.sps_ps_tbl[id].publish("computed_status", 4) -- idle
|
||||
ps.publish("computed_status", 4) -- idle
|
||||
end
|
||||
else
|
||||
fac.sps_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(data.state) do fac.sps_ps_tbl[id].publish(key, val) end
|
||||
for key, val in pairs(data.tanks) do fac.sps_ps_tbl[id].publish(key, val) end
|
||||
|
||||
io.facility.ps.publish("am_rate", data.state.process_rate * 1000)
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid sps id ", id))
|
||||
@ -584,6 +603,44 @@ function iocontrol.update_facility_status(status)
|
||||
valid = false
|
||||
end
|
||||
|
||||
-- dynamic tank statuses
|
||||
if type(rtu_statuses.tanks) == "table" then
|
||||
for id = 1, #fac.tank_ps_tbl do
|
||||
if rtu_statuses.tanks[id] == nil then
|
||||
-- disconnected
|
||||
fac.tank_ps_tbl[id].publish("computed_status", 1)
|
||||
end
|
||||
end
|
||||
|
||||
for id, tank in pairs(rtu_statuses.tanks) do
|
||||
if type(fac.tank_data_tbl[id]) == "table" then
|
||||
local data = fac.tank_data_tbl[id] ---@type dynamicv_session_db
|
||||
local ps = fac.tank_ps_tbl[id] ---@type psil
|
||||
|
||||
local rtu_faulted = _record_multiblock_status(tank, data, ps)
|
||||
|
||||
if rtu_faulted then
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.tanks.fill >= 0.99 then
|
||||
ps.publish("computed_status", 6) -- full
|
||||
elseif data.tanks.fill < 0.20 then
|
||||
ps.publish("computed_status", 5) -- low
|
||||
else
|
||||
ps.publish("computed_status", 4) -- on-line
|
||||
end
|
||||
else
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid dynamic tank id ", id))
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug(log_header .. "dyanmic tank list not a table")
|
||||
valid = false
|
||||
end
|
||||
|
||||
-- environment detector status
|
||||
if type(rtu_statuses.rad_mon) == "table" then
|
||||
if #rtu_statuses.rad_mon > 0 then
|
||||
@ -731,34 +788,21 @@ function iocontrol.update_unit_statuses(statuses)
|
||||
|
||||
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
|
||||
unit.boiler_data_tbl[id].tanks = boiler[4] ---@type table
|
||||
local data = unit.boiler_data_tbl[id] ---@type boilerv_session_db
|
||||
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
||||
|
||||
local data = unit.boiler_data_tbl[id] ---@type boilerv_session_db
|
||||
|
||||
unit.boiler_ps_tbl[id].publish("formed", data.formed)
|
||||
unit.boiler_ps_tbl[id].publish("faulted", rtu_faulted)
|
||||
local rtu_faulted = _record_multiblock_status(boiler, data, ps)
|
||||
|
||||
if rtu_faulted then
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.state.boil_rate > 0 then
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 5) -- active
|
||||
ps.publish("computed_status", 5) -- active
|
||||
else
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 4) -- idle
|
||||
ps.publish("computed_status", 4) -- idle
|
||||
end
|
||||
else
|
||||
unit.boiler_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.boiler_data_tbl[id].state) do
|
||||
unit.boiler_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.boiler_data_tbl[id].tanks) do
|
||||
unit.boiler_ps_tbl[id].publish(key, val)
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid boiler id ", id))
|
||||
@ -781,36 +825,23 @@ function iocontrol.update_unit_statuses(statuses)
|
||||
|
||||
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
|
||||
unit.turbine_data_tbl[id].tanks = turbine[4] ---@type table
|
||||
|
||||
local data = unit.turbine_data_tbl[id] ---@type turbinev_session_db
|
||||
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
||||
|
||||
unit.turbine_ps_tbl[id].publish("formed", data.formed)
|
||||
unit.turbine_ps_tbl[id].publish("faulted", rtu_faulted)
|
||||
local rtu_faulted = _record_multiblock_status(turbine, data, ps)
|
||||
|
||||
if rtu_faulted then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 3) -- faulted
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.tanks.energy_fill >= 0.99 then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 6) -- trip
|
||||
ps.publish("computed_status", 6) -- trip
|
||||
elseif data.state.flow_rate < 100 then
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 4) -- idle
|
||||
ps.publish("computed_status", 4) -- idle
|
||||
else
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 5) -- active
|
||||
ps.publish("computed_status", 5) -- active
|
||||
end
|
||||
else
|
||||
unit.turbine_ps_tbl[id].publish("computed_status", 2) -- not formed
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.turbine_data_tbl[id].state) do
|
||||
unit.turbine_ps_tbl[id].publish(key, val)
|
||||
end
|
||||
|
||||
for key, val in pairs(unit.turbine_data_tbl[id].tanks) do
|
||||
unit.turbine_ps_tbl[id].publish(key, val)
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid turbine id ", id))
|
||||
@ -822,6 +853,45 @@ function iocontrol.update_unit_statuses(statuses)
|
||||
valid = false
|
||||
end
|
||||
|
||||
-- dynamic tank statuses
|
||||
if type(rtu_statuses.tanks) == "table" then
|
||||
for id = 1, #unit.tank_ps_tbl do
|
||||
if rtu_statuses.tanks[i] == nil then
|
||||
-- disconnected
|
||||
unit.tank_ps_tbl[id].publish("computed_status", 1)
|
||||
end
|
||||
end
|
||||
|
||||
for id, tank in pairs(rtu_statuses.tanks) do
|
||||
if type(unit.tank_data_tbl[id]) == "table" then
|
||||
local data = unit.tank_data_tbl[id] ---@type dynamicv_session_db
|
||||
local ps = unit.tank_ps_tbl[id] ---@type psil
|
||||
|
||||
local rtu_faulted = _record_multiblock_status(tank, data, ps)
|
||||
|
||||
if rtu_faulted then
|
||||
ps.publish("computed_status", 3) -- faulted
|
||||
elseif data.formed then
|
||||
if data.tanks.fill >= 0.99 then
|
||||
ps.publish("computed_status", 6) -- full
|
||||
elseif data.tanks.fill < 0.20 then
|
||||
ps.publish("computed_status", 5) -- low
|
||||
else
|
||||
ps.publish("computed_status", 5) -- active
|
||||
end
|
||||
else
|
||||
ps.publish("computed_status", 2) -- not formed
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_header, "invalid dynamic tank id ", id))
|
||||
valid = false
|
||||
end
|
||||
end
|
||||
else
|
||||
log.debug(log_header .. "dynamic tank list not a table")
|
||||
valid = false
|
||||
end
|
||||
|
||||
-- solar neutron activator status info
|
||||
if type(rtu_statuses.sna) == "table" then
|
||||
unit.num_snas = rtu_statuses.sna[1] ---@type integer
|
||||
@ -951,6 +1021,8 @@ function iocontrol.update_unit_statuses(statuses)
|
||||
return valid
|
||||
end
|
||||
|
||||
--#endregion
|
||||
|
||||
-- get the IO controller database
|
||||
function iocontrol.get_db() return io end
|
||||
|
||||
|
@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder")
|
||||
|
||||
local apisessions = require("coordinator.session.apisessions")
|
||||
|
||||
local COORDINATOR_VERSION = "v0.20.3"
|
||||
local COORDINATOR_VERSION = "v0.21.0"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
File diff suppressed because one or more lines are too long
@ -62,9 +62,11 @@ function facility.new(num_reactors, cooling_conf)
|
||||
all_sys_ok = false,
|
||||
-- rtus
|
||||
rtu_conn_count = 0,
|
||||
rtu_list = {},
|
||||
redstone = {},
|
||||
induction = {},
|
||||
sps = {},
|
||||
tanks = {},
|
||||
envd = {},
|
||||
-- redstone I/O control
|
||||
io_ctl = nil, ---@type rs_controller
|
||||
@ -120,15 +122,12 @@ function facility.new(num_reactors, cooling_conf)
|
||||
table.insert(self.group_map, 0)
|
||||
end
|
||||
|
||||
-- list for RTU session management
|
||||
self.rtu_list = { self.redstone, self.induction, self.sps, self.tanks, self.envd }
|
||||
|
||||
-- init redstone RTU I/O controller
|
||||
self.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
|
||||
|
||||
-- check if all auto-controlled units completed ramping
|
||||
---@nodiscard
|
||||
local function _all_units_ramped()
|
||||
@ -215,29 +214,48 @@ function facility.new(num_reactors, cooling_conf)
|
||||
|
||||
-- link an induction matrix RTU session
|
||||
---@param imatrix unit_session
|
||||
---@return boolean linked induction matrix accepted (max 1)
|
||||
function public.add_imatrix(imatrix)
|
||||
table.insert(self.induction, imatrix)
|
||||
if #self.induction == 0 then
|
||||
table.insert(self.induction, imatrix)
|
||||
return true
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link an SPS RTU session
|
||||
---@param sps unit_session
|
||||
---@return boolean linked SPS accepted (max 1)
|
||||
function public.add_sps(sps)
|
||||
table.insert(self.sps, sps)
|
||||
if #self.sps == 0 then
|
||||
table.insert(self.sps, sps)
|
||||
return true
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link a dynamic tank RTU session
|
||||
---@param dynamic_tank unit_session
|
||||
---@return boolean linked dynamic tank accepted (max 1)
|
||||
function public.add_tank(dynamic_tank)
|
||||
if #self.tanks == 0 then
|
||||
table.insert(self.tanks, dynamic_tank)
|
||||
return true
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link an environment detector RTU session
|
||||
---@param envd unit_session
|
||||
---@return boolean linked environment detector accepted (max 1)
|
||||
function public.add_envd(envd)
|
||||
table.insert(self.envd, envd)
|
||||
if #self.envd == 0 then
|
||||
table.insert(self.envd, envd)
|
||||
return true
|
||||
else return false end
|
||||
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)
|
||||
util.filter_table(self.sps, function (s) return s.get_session_id() ~= session end)
|
||||
util.filter_table(self.envd, function (s) return s.get_session_id() ~= session end)
|
||||
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
||||
end
|
||||
|
||||
-- UPDATE --
|
||||
@ -251,10 +269,7 @@ function facility.new(num_reactors, cooling_conf)
|
||||
-- update (iterate) the facility management
|
||||
function public.update()
|
||||
-- unlink RTU unit sessions if they are closed
|
||||
_unlink_disconnected_units(self.redstone)
|
||||
_unlink_disconnected_units(self.induction)
|
||||
_unlink_disconnected_units(self.sps)
|
||||
_unlink_disconnected_units(self.envd)
|
||||
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (u) return u.is_connected() end) end
|
||||
|
||||
-- current state for process control
|
||||
local charge_update = 0
|
||||
@ -814,11 +829,9 @@ function facility.new(num_reactors, cooling_conf)
|
||||
|
||||
ready = self.mode_set > 0
|
||||
|
||||
if (self.mode_set == PROCESS.CHARGE) and (self.charge_setpoint <= 0) then
|
||||
ready = false
|
||||
elseif (self.mode_set == PROCESS.GEN_RATE) and (self.gen_rate_setpoint <= 0) then
|
||||
ready = false
|
||||
elseif (self.mode_set == PROCESS.BURN_RATE) and (self.burn_target < 0.1) then
|
||||
if (self.mode_set == PROCESS.CHARGE) and (self.charge_setpoint <= 0) or
|
||||
(self.mode_set == PROCESS.GEN_RATE) and (self.gen_rate_setpoint <= 0) or
|
||||
(self.mode_set == PROCESS.BURN_RATE) and (self.burn_target < 0.1) then
|
||||
ready = false
|
||||
end
|
||||
|
||||
@ -903,6 +916,14 @@ function facility.new(num_reactors, cooling_conf)
|
||||
end
|
||||
end
|
||||
|
||||
if all or type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
||||
build.tanks = {}
|
||||
for i = 1, #self.tanks do
|
||||
local tank = self.tanks[i] ---@type unit_session
|
||||
build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build }
|
||||
end
|
||||
end
|
||||
|
||||
return build
|
||||
end
|
||||
|
||||
@ -948,35 +969,32 @@ function facility.new(num_reactors, cooling_conf)
|
||||
-- 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
|
||||
}
|
||||
local matrix = self.induction[i] ---@type unit_session
|
||||
local db = matrix.get_db() ---@type imatrix_session_db
|
||||
status.induction[matrix.get_device_idx()] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- status of sps
|
||||
status.sps = {}
|
||||
for i = 1, #self.sps do
|
||||
local sps = self.sps[i] ---@type unit_session
|
||||
status.sps[sps.get_device_idx()] = {
|
||||
sps.is_faulted(),
|
||||
sps.get_db().formed,
|
||||
sps.get_db().state,
|
||||
sps.get_db().tanks
|
||||
}
|
||||
local sps = self.sps[i] ---@type unit_session
|
||||
local db = sps.get_db() ---@type sps_session_db
|
||||
status.sps[sps.get_device_idx()] = { sps.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- status of dynamic tanks
|
||||
status.tanks = {}
|
||||
for i = 1, #self.tanks do
|
||||
local tank = self.tanks[i] ---@type unit_session
|
||||
local db = tank.get_db() ---@type dynamicv_session_db
|
||||
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- radiation monitors (environment detectors)
|
||||
status.rad_mon = {}
|
||||
for i = 1, #self.envd do
|
||||
local envd = self.envd[i] ---@type unit_session
|
||||
status.rad_mon[envd.get_device_idx()] = {
|
||||
envd.is_faulted(),
|
||||
envd.get_db().radiation
|
||||
}
|
||||
status.rad_mon[envd.get_device_idx()] = { envd.is_faulted(), envd.get_db().radiation }
|
||||
end
|
||||
|
||||
return status
|
||||
|
@ -406,7 +406,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
||||
local builds = {}
|
||||
|
||||
local unit = self.units[unit_id] ---@type reactor_unit
|
||||
builds[unit_id] = unit.get_build(true, false, false)
|
||||
builds[unit_id] = unit.get_build(-1)
|
||||
|
||||
_send(SCADA_CRDN_TYPE.UNIT_BUILDS, { builds })
|
||||
elseif cmd.key == CRD_S_DATA.RESEND_RTU_BUILD then
|
||||
@ -420,7 +420,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
||||
local builds = {}
|
||||
|
||||
local unit = self.units[unit_id] ---@type reactor_unit
|
||||
builds[unit_id] = unit.get_build(false, cmd.val.type == RTU_UNIT_TYPE.BOILER_VALVE, cmd.val.type == RTU_UNIT_TYPE.TURBINE_VALVE)
|
||||
builds[unit_id] = unit.get_build(cmd.val.type)
|
||||
|
||||
_send(SCADA_CRDN_TYPE.UNIT_BUILDS, { builds })
|
||||
else
|
||||
|
@ -11,6 +11,7 @@ local svqtypes = require("supervisor.session.svqtypes")
|
||||
-- supervisor rtu sessions (svrs)
|
||||
local unit_session = require("supervisor.session.rtu.unit_session")
|
||||
local svrs_boilerv = require("supervisor.session.rtu.boilerv")
|
||||
local svrs_dynamicv = require("supervisor.session.rtu.dynamicv")
|
||||
local svrs_envd = require("supervisor.session.rtu.envd")
|
||||
local svrs_imatrix = require("supervisor.session.rtu.imatrix")
|
||||
local svrs_redstone = require("supervisor.session.rtu.redstone")
|
||||
@ -138,6 +139,10 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
||||
-- turbine
|
||||
unit = svrs_turbinev.new(id, i, unit_advert, self.modbus_q)
|
||||
if type(unit) ~= "nil" then target_unit.add_turbine(unit) end
|
||||
elseif u_type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
||||
-- dynamic tank
|
||||
unit = svrs_dynamicv.new(id, i, unit_advert, self.modbus_q)
|
||||
if type(unit) ~= "nil" then target_unit.add_tank(unit) end
|
||||
elseif u_type == RTU_UNIT_TYPE.SNA then
|
||||
-- solar neutron activator
|
||||
unit = svrs_sna.new(id, i, unit_advert, self.modbus_q)
|
||||
@ -166,6 +171,10 @@ function rtu.new_session(id, s_addr, in_queue, out_queue, timeout, advertisement
|
||||
-- super-critical phase shifter
|
||||
unit = svrs_sps.new(id, i, unit_advert, self.modbus_q)
|
||||
if type(unit) ~= "nil" then facility.add_sps(unit) end
|
||||
elseif u_type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
||||
-- dynamic tank
|
||||
unit = svrs_dynamicv.new(id, i, unit_advert, self.modbus_q)
|
||||
if type(unit) ~= "nil" then facility.add_tank(unit) end
|
||||
elseif u_type == RTU_UNIT_TYPE.ENV_DETECTOR then
|
||||
-- environment detector
|
||||
unit = svrs_envd.new(id, i, unit_advert, self.modbus_q)
|
||||
|
289
supervisor/session/rtu/dynamicv.lua
Normal file
289
supervisor/session/rtu/dynamicv.lua
Normal file
@ -0,0 +1,289 @@
|
||||
local log = require("scada-common.log")
|
||||
local mqueue = require("scada-common.mqueue")
|
||||
local types = require("scada-common.types")
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local qtypes = require("supervisor.session.rtu.qtypes")
|
||||
local unit_session = require("supervisor.session.rtu.unit_session")
|
||||
|
||||
local dynamicv = {}
|
||||
|
||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||
local CONTAINER_MODE = types.CONTAINER_MODE
|
||||
local MODBUS_FCODE = types.MODBUS_FCODE
|
||||
|
||||
local DTV_RTU_S_CMDS = qtypes.DTV_RTU_S_CMDS
|
||||
local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA
|
||||
|
||||
local TXN_TYPES = {
|
||||
FORMED = 1,
|
||||
BUILD = 2,
|
||||
STATE = 3,
|
||||
TANKS = 4,
|
||||
INC_CONT = 5,
|
||||
DEC_CONT = 6,
|
||||
SET_CONT = 7
|
||||
}
|
||||
|
||||
local TXN_TAGS = {
|
||||
"dynamicv.formed",
|
||||
"dynamicv.build",
|
||||
"dynamicv.state",
|
||||
"dynamicv.tanks",
|
||||
"dynamicv.inc_cont_mode",
|
||||
"dynamicv.dec_cont_mode",
|
||||
"dynamicv.set_cont_mode"
|
||||
}
|
||||
|
||||
local PERIODICS = {
|
||||
FORMED = 2000,
|
||||
BUILD = 1000,
|
||||
STATE = 1000,
|
||||
TANKS = 500
|
||||
}
|
||||
|
||||
-- create a new dynamicv rtu session runner
|
||||
---@nodiscard
|
||||
---@param session_id integer RTU session ID
|
||||
---@param unit_id integer RTU unit ID
|
||||
---@param advert rtu_advertisement RTU advertisement table
|
||||
---@param out_queue mqueue RTU unit message out queue
|
||||
function dynamicv.new(session_id, unit_id, advert, out_queue)
|
||||
-- type check
|
||||
if advert.type ~= RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
||||
log.error("attempt to instantiate dynamicv RTU for type '" .. types.rtu_type_to_string(advert.type) .. "'. this is a bug.")
|
||||
return nil
|
||||
end
|
||||
|
||||
local log_tag = "session.rtu(" .. session_id .. ").dynamicv(" .. advert.index .. "): "
|
||||
|
||||
local self = {
|
||||
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),
|
||||
has_build = false,
|
||||
periodics = {
|
||||
next_formed_req = 0,
|
||||
next_build_req = 0,
|
||||
next_state_req = 0,
|
||||
next_tanks_req = 0
|
||||
},
|
||||
---@class dynamicv_session_db
|
||||
db = {
|
||||
formed = false,
|
||||
build = {
|
||||
last_update = 0,
|
||||
length = 0,
|
||||
width = 0,
|
||||
height = 0,
|
||||
min_pos = types.new_zero_coordinate(),
|
||||
max_pos = types.new_zero_coordinate(),
|
||||
tank_capacity = 0,
|
||||
chem_tank_capacity = 0
|
||||
},
|
||||
state = {
|
||||
last_update = 0,
|
||||
container_mode = CONTAINER_MODE.BOTH ---@type container_mode
|
||||
},
|
||||
tanks = {
|
||||
last_update = 0,
|
||||
stored = types.new_empty_gas(),
|
||||
fill = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local public = self.session.get()
|
||||
|
||||
-- PRIVATE FUNCTIONS --
|
||||
|
||||
-- increment the container mode
|
||||
local function _inc_cont_mode()
|
||||
-- write coil 1 with unused value 0
|
||||
self.session.send_request(TXN_TYPES.INC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 1, 0 })
|
||||
end
|
||||
|
||||
-- decrement the container mode
|
||||
local function _dec_cont_mode()
|
||||
-- write coil 2 with unused value 0
|
||||
self.session.send_request(TXN_TYPES.DEC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 2, 0 })
|
||||
end
|
||||
|
||||
-- set the container mode
|
||||
---@param mode container_mode
|
||||
local function _set_cont_mode(mode)
|
||||
-- write holding register 1
|
||||
self.session.send_request(TXN_TYPES.SET_CONT, MODBUS_FCODE.WRITE_SINGLE_HOLD_REG, { 1, mode })
|
||||
end
|
||||
|
||||
-- query if the multiblock is formed
|
||||
local function _request_formed()
|
||||
-- read discrete input 1 (start = 1, count = 1)
|
||||
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 })
|
||||
end
|
||||
|
||||
-- query the build of the device
|
||||
local function _request_build()
|
||||
-- read input registers 1 through 7 (start = 1, count = 7)
|
||||
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 7 })
|
||||
end
|
||||
|
||||
-- query the state of the device
|
||||
local function _request_state()
|
||||
-- read holding register 1 (start = 1, count = 1)
|
||||
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_MUL_HOLD_REGS, { 1, 1 })
|
||||
end
|
||||
|
||||
-- query the tanks of the device
|
||||
local function _request_tanks()
|
||||
-- read input registers 8 through 9 (start = 8, count = 2)
|
||||
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 8, 2 })
|
||||
end
|
||||
|
||||
-- PUBLIC FUNCTIONS --
|
||||
|
||||
-- handle a packet
|
||||
---@param m_pkt modbus_frame
|
||||
function public.handle_packet(m_pkt)
|
||||
local txn_type = self.session.try_resolve(m_pkt)
|
||||
if txn_type == false then
|
||||
-- nothing to do
|
||||
elseif txn_type == TXN_TYPES.FORMED then
|
||||
-- formed response
|
||||
-- load in data if correct length
|
||||
if m_pkt.length == 1 then
|
||||
self.db.formed = m_pkt.data[1]
|
||||
|
||||
if not self.db.formed then self.has_build = false end
|
||||
else
|
||||
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
|
||||
end
|
||||
elseif txn_type == TXN_TYPES.BUILD then
|
||||
-- build response
|
||||
if m_pkt.length == 7 then
|
||||
self.db.build.last_update = util.time_ms()
|
||||
self.db.build.length = m_pkt.data[1]
|
||||
self.db.build.width = m_pkt.data[2]
|
||||
self.db.build.height = m_pkt.data[3]
|
||||
self.db.build.min_pos = m_pkt.data[4]
|
||||
self.db.build.max_pos = m_pkt.data[5]
|
||||
self.db.build.tank_capacity = m_pkt.data[6]
|
||||
self.db.build.chem_tank_capacity = m_pkt.data[7]
|
||||
self.has_build = true
|
||||
|
||||
out_queue.push_data(unit_session.RTU_US_DATA.BUILD_CHANGED, { unit = advert.reactor, type = advert.type })
|
||||
else
|
||||
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
|
||||
end
|
||||
elseif txn_type == TXN_TYPES.STATE then
|
||||
-- state response
|
||||
if m_pkt.length == 1 then
|
||||
self.db.state.last_update = util.time_ms()
|
||||
self.db.state.container_mode = m_pkt.data[1]
|
||||
else
|
||||
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
|
||||
end
|
||||
elseif txn_type == TXN_TYPES.TANKS then
|
||||
-- tanks response
|
||||
if m_pkt.length == 2 then
|
||||
self.db.tanks.last_update = util.time_ms()
|
||||
self.db.tanks.stored = m_pkt.data[1]
|
||||
self.db.tanks.fill = m_pkt.data[2]
|
||||
else
|
||||
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
|
||||
end
|
||||
elseif txn_type == TXN_TYPES.INC_CONT or txn_type == TXN_TYPES.DEC_CONT or txn_type == TXN_TYPES.SET_CONT then
|
||||
-- successful acknowledgement
|
||||
elseif txn_type == nil then
|
||||
log.error(log_tag .. "unknown transaction reply")
|
||||
else
|
||||
log.error(log_tag .. "unknown transaction type " .. txn_type)
|
||||
end
|
||||
end
|
||||
|
||||
-- update this runner
|
||||
---@param time_now integer milliseconds
|
||||
function public.update(time_now)
|
||||
-- check command queue
|
||||
while self.session.in_q.ready() do
|
||||
-- get a new message to process
|
||||
local msg = self.session.in_q.pop()
|
||||
|
||||
if msg ~= nil then
|
||||
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||
-- instruction
|
||||
local cmd = msg.message
|
||||
|
||||
if cmd == DTV_RTU_S_CMDS.INC_CONT_MODE then
|
||||
_inc_cont_mode()
|
||||
elseif cmd == DTV_RTU_S_CMDS.DEC_CONT_MODE then
|
||||
_dec_cont_mode()
|
||||
else
|
||||
log.debug(util.c(log_tag, "unrecognized in-queue command ", cmd))
|
||||
end
|
||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||
-- instruction with body
|
||||
local cmd = msg.message ---@type queue_data
|
||||
if cmd.key == DTV_RTU_S_DATA.SET_CONT_MODE then
|
||||
if cmd.val == types.CONTAINER_MODE.BOTH or
|
||||
cmd.val == types.CONTAINER_MODE.FILL or
|
||||
cmd.val == types.CONTAINER_MODE.EMPTY then
|
||||
_set_cont_mode(cmd.val)
|
||||
else
|
||||
log.debug(util.c(log_tag, "unrecognized container mode \"", cmd.val, "\""))
|
||||
end
|
||||
else
|
||||
log.debug(util.c(log_tag, "unrecognized in-queue data ", cmd.key))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- max 100ms spent processing queue
|
||||
if util.time() - time_now > 100 then
|
||||
log.warning(log_tag .. "exceeded 100ms queue process limit")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
time_now = util.time()
|
||||
|
||||
-- handle periodics
|
||||
|
||||
if self.periodics.next_formed_req <= time_now then
|
||||
_request_formed()
|
||||
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
|
||||
end
|
||||
|
||||
if self.db.formed then
|
||||
if not self.has_build and self.periodics.next_build_req <= time_now then
|
||||
_request_build()
|
||||
self.periodics.next_build_req = time_now + PERIODICS.BUILD
|
||||
end
|
||||
|
||||
if self.periodics.next_state_req <= time_now then
|
||||
_request_state()
|
||||
self.periodics.next_state_req = time_now + PERIODICS.STATE
|
||||
end
|
||||
|
||||
if self.periodics.next_tanks_req <= time_now then
|
||||
_request_tanks()
|
||||
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
|
||||
end
|
||||
end
|
||||
|
||||
self.session.post_update()
|
||||
end
|
||||
|
||||
-- invalidate build cache
|
||||
function public.invalidate_cache()
|
||||
self.periodics.next_formed_req = 0
|
||||
self.periodics.next_build_req = 0
|
||||
self.has_build = false
|
||||
end
|
||||
|
||||
-- get the unit session database
|
||||
---@nodiscard
|
||||
function public.get_db() return self.db end
|
||||
|
||||
return public
|
||||
end
|
||||
|
||||
return dynamicv
|
@ -1,16 +1,31 @@
|
||||
---@class rtu_unit_qtypes
|
||||
local qtypes = {}
|
||||
|
||||
-- turbine valve rtu session commands
|
||||
local TBV_RTU_S_CMDS = {
|
||||
INC_DUMP_MODE = 1,
|
||||
DEC_DUMP_MODE = 2
|
||||
}
|
||||
|
||||
-- turbine valve rtu session commands w/ parameters
|
||||
local TBV_RTU_S_DATA = {
|
||||
SET_DUMP_MODE = 1
|
||||
}
|
||||
|
||||
-- dynamic tank valve rtu session commands
|
||||
local DTV_RTU_S_CMDS = {
|
||||
INC_CONT_MODE = 1,
|
||||
DEC_CONT_MODE = 2
|
||||
}
|
||||
|
||||
-- dynamic tank valve rtu session commands w/ parameters
|
||||
local DTV_RTU_S_DATA = {
|
||||
SET_CONT_MODE = 1
|
||||
}
|
||||
|
||||
qtypes.TBV_RTU_S_CMDS = TBV_RTU_S_CMDS
|
||||
qtypes.TBV_RTU_S_DATA = TBV_RTU_S_DATA
|
||||
qtypes.DTV_RTU_S_CMDS = DTV_RTU_S_CMDS
|
||||
qtypes.DTV_RTU_S_DATA = DTV_RTU_S_DATA
|
||||
|
||||
return qtypes
|
||||
|
@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local svsessions = require("supervisor.session.svsessions")
|
||||
|
||||
local SUPERVISOR_VERSION = "v0.19.4"
|
||||
local SUPERVISOR_VERSION = "v0.20.0"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
@ -11,12 +11,13 @@ local rsctl = require("supervisor.session.rsctl")
|
||||
---@class reactor_control_unit
|
||||
local unit = {}
|
||||
|
||||
local WASTE_MODE = types.WASTE_MODE
|
||||
local WASTE = types.WASTE_PRODUCT
|
||||
local ALARM = types.ALARM
|
||||
local PRIO = types.ALARM_PRIORITY
|
||||
local ALARM_STATE = types.ALARM_STATE
|
||||
local TRI_FAIL = types.TRI_FAIL
|
||||
local WASTE_MODE = types.WASTE_MODE
|
||||
local WASTE = types.WASTE_PRODUCT
|
||||
local ALARM = types.ALARM
|
||||
local PRIO = types.ALARM_PRIORITY
|
||||
local ALARM_STATE = types.ALARM_STATE
|
||||
local TRI_FAIL = types.TRI_FAIL
|
||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||
|
||||
local PLC_S_CMDS = plc.PLC_S_CMDS
|
||||
|
||||
@ -69,10 +70,12 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
num_turbines = num_turbines,
|
||||
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
||||
-- rtus
|
||||
rtu_list = {},
|
||||
redstone = {},
|
||||
boilers = {},
|
||||
turbines = {},
|
||||
sna = {},
|
||||
tanks = {},
|
||||
snas = {},
|
||||
envd = {},
|
||||
sna_prod_rate = 0,
|
||||
-- redstone control
|
||||
@ -230,6 +233,9 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
}
|
||||
}
|
||||
|
||||
-- list for RTU session management
|
||||
self.rtu_list = { self.redstone, self.boilers, self.turbines, self.tanks, self.snas, self.envd }
|
||||
|
||||
-- init redstone RTU I/O controller
|
||||
self.io_ctl = rsctl.new(self.redstone)
|
||||
|
||||
@ -373,12 +379,6 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
|
||||
--#endregion
|
||||
|
||||
-- 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 reactor_unit
|
||||
@ -413,6 +413,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
|
||||
-- link a turbine RTU session
|
||||
---@param turbine unit_session
|
||||
---@return boolean linked turbine accepted to associated device slot
|
||||
function public.add_turbine(turbine)
|
||||
if #self.turbines < num_turbines and turbine.get_device_idx() <= num_turbines then
|
||||
table.insert(self.turbines, turbine)
|
||||
@ -422,13 +423,12 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
_reset_dt(DT_KEYS.TurbinePower .. turbine.get_device_idx())
|
||||
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link a boiler RTU session
|
||||
---@param boiler unit_session
|
||||
---@return boolean linked boiler accepted to associated device slot
|
||||
function public.add_boiler(boiler)
|
||||
if #self.boilers < num_boilers and boiler.get_device_idx() <= num_boilers then
|
||||
table.insert(self.boilers, boiler)
|
||||
@ -440,31 +440,37 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
_reset_dt(DT_KEYS.BoilerHCool .. boiler.get_device_idx())
|
||||
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link a dynamic tank RTU session
|
||||
---@param dynamic_tank unit_session
|
||||
---@return boolean linked dynamic tank accepted (max 1)
|
||||
function public.add_tank(dynamic_tank)
|
||||
if #self.tanks == 0 then
|
||||
table.insert(self.tanks, dynamic_tank)
|
||||
return true
|
||||
else return false end
|
||||
end
|
||||
|
||||
-- link a solar neutron activator RTU session
|
||||
---@param sna unit_session
|
||||
function public.add_sna(sna)
|
||||
table.insert(self.sna, sna)
|
||||
end
|
||||
function public.add_sna(sna) table.insert(self.snas, sna) end
|
||||
|
||||
-- link an environment detector RTU session
|
||||
---@param envd unit_session
|
||||
---@return boolean linked environment detector accepted (max 1)
|
||||
function public.add_envd(envd)
|
||||
table.insert(self.envd, envd)
|
||||
if #self.envd == 0 then
|
||||
table.insert(self.envd, envd)
|
||||
return true
|
||||
else return false end
|
||||
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.boilers, function (s) return s.get_session_id() ~= session end)
|
||||
util.filter_table(self.turbines, function (s) return s.get_session_id() ~= session end)
|
||||
util.filter_table(self.sna, function (s) return s.get_session_id() ~= session end)
|
||||
util.filter_table(self.envd, function (s) return s.get_session_id() ~= session end)
|
||||
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
||||
end
|
||||
|
||||
--#endregion
|
||||
@ -482,11 +488,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
end
|
||||
|
||||
-- unlink RTU unit sessions if they are closed
|
||||
_unlink_disconnected_units(self.redstone)
|
||||
_unlink_disconnected_units(self.boilers)
|
||||
_unlink_disconnected_units(self.turbines)
|
||||
_unlink_disconnected_units(self.sna)
|
||||
_unlink_disconnected_units(self.envd)
|
||||
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (u) return u.is_connected() end) end
|
||||
|
||||
-- update degraded state for auto control
|
||||
self.db.control.degraded = (#self.boilers ~= num_boilers) or (#self.turbines ~= num_turbines) or (self.plc_i == nil)
|
||||
@ -717,21 +719,25 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
return false
|
||||
end
|
||||
|
||||
-- get build properties of all machines
|
||||
-- get build properties of machines
|
||||
--
|
||||
-- filter options
|
||||
-- - nil to include all builds
|
||||
-- - -1 to include only PLC build
|
||||
-- - RTU_UNIT_TYPE to include all builds of machines of that type
|
||||
---@nodiscard
|
||||
---@param inc_plc boolean? true/nil to include PLC build, false to exclude
|
||||
---@param inc_boilers boolean? true/nil to include boiler builds, false to exclude
|
||||
---@param inc_turbines boolean? true/nil to include turbine builds, false to exclude
|
||||
function public.get_build(inc_plc, inc_boilers, inc_turbines)
|
||||
---@param filter -1|RTU_UNIT_TYPE? filter as described above
|
||||
function public.get_build(filter)
|
||||
local all = filter == nil
|
||||
local build = {}
|
||||
|
||||
if inc_plc ~= false then
|
||||
if all or (filter == -1) then
|
||||
if self.plc_i ~= nil then
|
||||
build.reactor = self.plc_i.get_struct()
|
||||
end
|
||||
end
|
||||
|
||||
if inc_boilers ~= false then
|
||||
if all or (filter == RTU_UNIT_TYPE.BOILER_VALVE) then
|
||||
build.boilers = {}
|
||||
for i = 1, #self.boilers do
|
||||
local boiler = self.boilers[i] ---@type unit_session
|
||||
@ -739,7 +745,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
end
|
||||
end
|
||||
|
||||
if inc_turbines ~= false then
|
||||
if all or (filter == RTU_UNIT_TYPE.TURBINE_VALVE) then
|
||||
build.turbines = {}
|
||||
for i = 1, #self.turbines do
|
||||
local turbine = self.turbines[i] ---@type unit_session
|
||||
@ -747,6 +753,14 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
end
|
||||
end
|
||||
|
||||
if all or (filter == RTU_UNIT_TYPE.DYNAMIC_VALVE) then
|
||||
build.tanks = {}
|
||||
for i = 1, #self.tanks do
|
||||
local tank = self.tanks[i] ---@type unit_session
|
||||
build.tanks[tank.get_device_idx()] = { tank.get_db().formed, tank.get_db().build }
|
||||
end
|
||||
end
|
||||
|
||||
return build
|
||||
end
|
||||
|
||||
@ -777,38 +791,35 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
-- status of boilers (including tanks)
|
||||
status.boilers = {}
|
||||
for i = 1, #self.boilers do
|
||||
local boiler = self.boilers[i] ---@type unit_session
|
||||
status.boilers[boiler.get_device_idx()] = {
|
||||
boiler.is_faulted(),
|
||||
boiler.get_db().formed,
|
||||
boiler.get_db().state,
|
||||
boiler.get_db().tanks
|
||||
}
|
||||
local boiler = self.boilers[i] ---@type unit_session
|
||||
local db = boiler.get_db() ---@type boilerv_session_db
|
||||
status.boilers[boiler.get_device_idx()] = { boiler.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- status of turbines (including tanks)
|
||||
status.turbines = {}
|
||||
for i = 1, #self.turbines do
|
||||
local turbine = self.turbines[i] ---@type unit_session
|
||||
status.turbines[turbine.get_device_idx()] = {
|
||||
turbine.is_faulted(),
|
||||
turbine.get_db().formed,
|
||||
turbine.get_db().state,
|
||||
turbine.get_db().tanks
|
||||
}
|
||||
local db = turbine.get_db() ---@type turbinev_session_db
|
||||
status.turbines[turbine.get_device_idx()] = { turbine.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- basic SNA statistical information, don't send everything, it's not necessary
|
||||
status.sna = { #self.sna, public.get_sna_rate() }
|
||||
-- status of dynamic tanks
|
||||
status.tanks = {}
|
||||
for i = 1, #self.tanks do
|
||||
local tank = self.tanks[i] ---@type unit_session
|
||||
local db = tank.get_db() ---@type dynamicv_session_db
|
||||
status.turbines[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
||||
end
|
||||
|
||||
-- basic SNA statistical information
|
||||
status.sna = { #self.snas, public.get_sna_rate() }
|
||||
|
||||
-- radiation monitors (environment detectors)
|
||||
status.rad_mon = {}
|
||||
for i = 1, #self.envd do
|
||||
local envd = self.envd[i] ---@type unit_session
|
||||
status.rad_mon[envd.get_device_idx()] = {
|
||||
envd.is_faulted(),
|
||||
envd.get_db().radiation
|
||||
}
|
||||
local envd = self.envd[i] ---@type unit_session
|
||||
status.rad_mon[envd.get_device_idx()] = { envd.is_faulted(), envd.get_db().radiation }
|
||||
end
|
||||
|
||||
return status
|
||||
@ -820,8 +831,8 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
||||
function public.get_sna_rate()
|
||||
local total_avail_rate = 0
|
||||
|
||||
for i = 1, #self.sna do
|
||||
local db = self.sna[i].get_db() ---@type sna_session_db
|
||||
for i = 1, #self.snas do
|
||||
local db = self.snas[i].get_db() ---@type sna_session_db
|
||||
total_avail_rate = total_avail_rate + db.state.production_rate
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user