diff --git a/coordinator/coordinator.lua b/coordinator/coordinator.lua index 25ffa02..13430e0 100644 --- a/coordinator/coordinator.lua +++ b/coordinator/coordinator.lua @@ -2,6 +2,7 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local ppm = require("scada-common.ppm") local util = require("scada-common.util") +local process = require("coordinator.process") local apisessions = require("coordinator.apisessions") local iocontrol = require("coordinator.iocontrol") @@ -442,6 +443,10 @@ function coordinator.comms(version, modem, sv_port, sv_listen, api_listen, sv_wa unit.set_waste_ack(ack) elseif cmd == UNIT_COMMANDS.ACK_ALL_ALARMS then unit.ack_alarms_ack(ack) + elseif cmd == UNIT_COMMANDS.SET_GROUP then + process.sv_assign(unit_id, ack) + elseif cmd == UNIT_COMMANDS.SET_LIMIT then + process.sv_limit(unit_id, ack) else log.debug(util.c("received command ack with unknown command ", cmd)) end diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 775cfb9..c66d8d5 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -4,6 +4,7 @@ local psil = require("scada-common.psil") local types = require("scada-common.types") local util = require("scada-common.util") +local process = require("coordinator.process") local sounder = require("coordinator.sounder") local UNIT_COMMANDS = comms.UNIT_COMMANDS @@ -20,13 +21,21 @@ local io = {} ---@param comms coord_comms comms reference ---@diagnostic disable-next-line: redefined-local function iocontrol.init(conf, comms) + -- pass IO control here since it can't be require'd due to a require loop + process.init(iocontrol, comms) + io.facility = { + auto_active = false, scram = false, + num_units = conf.num_units, ---@type integer ps = psil.create(), induction_ps_tbl = {}, - induction_data_tbl = {} + induction_data_tbl = {}, + + env_d_ps = psil.create(), + env_d_data = {} } -- create induction tables (max 1 per unit, preferably 1 total) @@ -38,20 +47,12 @@ function iocontrol.init(conf, comms) io.units = {} for i = 1, conf.num_units do - local function ack(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(UNIT_COMMANDS.RESET_ALARM, i, alarm) - log.debug(util.c("UNIT[", i, "]: RESET ALARM ", alarm)) - end + local function ack(alarm) process.ack_alarm(i, alarm) end + local function reset(alarm) process.reset_alarm(i, alarm) end ---@class ioctl_entry local entry = { - unit_id = i, ---@type integer - initialized = false, + unit_id = i, ---@type integer num_boilers = 0, num_turbines = 0, @@ -60,53 +61,58 @@ function iocontrol.init(conf, comms) burn_rate_cmd = 0.0, waste_control = 0, - start = function () end, - scram = function () end, - reset_rps = function () end, - ack_alarms = function () end, - set_burn = function (rate) end, ---@param rate number - set_waste = function (mode) end, ---@param mode integer + a_group = 0, -- auto control group + a_limit = 0.0, -- auto control limit - start_ack = function (success) end, ---@param success boolean - scram_ack = function (success) end, ---@param success boolean - reset_rps_ack = function (success) end, ---@param success boolean - ack_alarms_ack = function (success) end, ---@param success boolean - set_burn_ack = function (success) end, ---@param success boolean - set_waste_ack = function (success) end, ---@param success boolean + start = function () process.start(i) end, + scram = function () process.scram(i) end, + reset_rps = function () process.reset_rps(i) end, + ack_alarms = function () process.ack_all_alarms(i) end, + set_burn = function (rate) process.set_rate(i, rate) end, ---@param rate number burn rate + set_waste = function (mode) process.set_waste(i, mode) end, ---@param mode integer waste processing mode + + set_group = function (g) process.set_group(i, g) end, ---@param g integer|0 group ID or 0 + + start_ack = function (success) end, ---@param success boolean + scram_ack = function (success) end, ---@param success boolean + reset_rps_ack = function (success) end, ---@param success boolean + ack_alarms_ack = function (success) end, ---@param success boolean + set_burn_ack = function (success) end, ---@param success boolean + set_waste_ack = function (success) end, ---@param success boolean alarm_callbacks = { - c_breach = { ack = function () ack(1) end, reset = function () reset(1) end }, - radiation = { ack = function () ack(2) end, reset = function () reset(2) end }, - r_lost = { ack = function () ack(3) end, reset = function () reset(3) end }, - dmg_crit = { ack = function () ack(4) end, reset = function () reset(4) end }, - damage = { ack = function () ack(5) end, reset = function () reset(5) end }, - over_temp = { ack = function () ack(6) end, reset = function () reset(6) end }, - high_temp = { ack = function () ack(7) end, reset = function () reset(7) end }, - waste_leak = { ack = function () ack(8) end, reset = function () reset(8) end }, - waste_high = { ack = function () ack(9) end, reset = function () reset(9) end }, - rps_trans = { ack = function () ack(10) end, reset = function () reset(10) end }, - rcs_trans = { ack = function () ack(11) end, reset = function () reset(11) end }, - t_trip = { ack = function () ack(12) end, reset = function () reset(12) end } + c_breach = { ack = function () ack(1) end, reset = function () reset(1) end }, + radiation = { ack = function () ack(2) end, reset = function () reset(2) end }, + r_lost = { ack = function () ack(3) end, reset = function () reset(3) end }, + dmg_crit = { ack = function () ack(4) end, reset = function () reset(4) end }, + damage = { ack = function () ack(5) end, reset = function () reset(5) end }, + over_temp = { ack = function () ack(6) end, reset = function () reset(6) end }, + high_temp = { ack = function () ack(7) end, reset = function () reset(7) end }, + waste_leak = { ack = function () ack(8) end, reset = function () reset(8) end }, + waste_high = { ack = function () ack(9) end, reset = function () reset(9) end }, + rps_trans = { ack = function () ack(10) end, reset = function () reset(10) end }, + rcs_trans = { ack = function () ack(11) end, reset = function () reset(11) end }, + t_trip = { ack = function () ack(12) end, reset = function () reset(12) end } }, ---@type alarms alarms = { - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE, - ALARM_STATE.INACTIVE + ALARM_STATE.INACTIVE, -- containment breach + ALARM_STATE.INACTIVE, -- containment radiation + ALARM_STATE.INACTIVE, -- reactor lost + ALARM_STATE.INACTIVE, -- damage critical + ALARM_STATE.INACTIVE, -- reactor taking damage + ALARM_STATE.INACTIVE, -- reactor over temperature + ALARM_STATE.INACTIVE, -- reactor high temperature + ALARM_STATE.INACTIVE, -- waste leak + ALARM_STATE.INACTIVE, -- waste level high + ALARM_STATE.INACTIVE, -- RPS transient + ALARM_STATE.INACTIVE, -- RCS transient + ALARM_STATE.INACTIVE -- turbine trip }, reactor_ps = psil.create(), - reactor_data = {}, ---@type reactor_db + reactor_data = {}, ---@type reactor_db boiler_ps_tbl = {}, boiler_data_tbl = {}, @@ -115,38 +121,6 @@ function iocontrol.init(conf, comms) turbine_data_tbl = {} } - function entry.start() - entry.control_state = true - 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(UNIT_COMMANDS.SCRAM, i) - log.debug(util.c("UNIT[", i, "]: SCRAM")) - end - - function entry.reset_rps() - comms.send_command(UNIT_COMMANDS.RESET_RPS, i) - log.debug(util.c("UNIT[", i, "]: RESET_RPS")) - end - - function entry.ack_alarms() - 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(UNIT_COMMANDS.SET_BURN, i, rate) - log.debug(util.c("UNIT[", i, "]: SET_BURN = ", rate)) - end - - function entry.set_waste(mode) - comms.send_command(UNIT_COMMANDS.SET_WASTE, i, mode) - log.debug(util.c("UNIT[", i, "]: SET_WASTE = ", mode)) - end - -- create boiler tables for _ = 1, conf.defs[(i * 2) - 1] do local data = {} ---@type boilerv_session_db diff --git a/coordinator/process.lua b/coordinator/process.lua new file mode 100644 index 0000000..b594306 --- /dev/null +++ b/coordinator/process.lua @@ -0,0 +1,117 @@ + +local comms = require("scada-common.comms") +local log = require("scada-common.log") +local util = require("scada-common.util") + +local UNIT_COMMANDS = comms.UNIT_COMMANDS + +---@class process_controller +local process = {} + +local self = { + io = nil, ---@type ioctl + comms = nil ---@type coord_comms +} + +-------------------------- +-- UNIT COMMAND CONTROL -- +-------------------------- + +-- initialize the process controller +---@param iocontrol ioctl +---@diagnostic disable-next-line: redefined-local +function process.init(iocontrol, comms) + self.io = iocontrol + self.comms = comms +end + +-- start reactor +---@param id integer unit ID +function process.start(id) + self.io.units[id].control_state = true + self.comms.send_command(UNIT_COMMANDS.START, id) + log.debug(util.c("UNIT[", id, "]: START")) +end + +-- SCRAM reactor +---@param id integer unit ID +function process.scram(id) + self.io.units[id].control_state = false + self.comms.send_command(UNIT_COMMANDS.SCRAM, id) + log.debug(util.c("UNIT[", id, "]: SCRAM")) +end + +-- reset reactor protection system +---@param id integer unit ID +function process.reset_rps(id) + self.comms.send_command(UNIT_COMMANDS.RESET_RPS, id) + log.debug(util.c("UNIT[", id, "]: RESET RPS")) +end + +-- set burn rate or burn limit if part of a group +---@param id integer unit ID +---@param rate number burn rate +function process.set_rate(id, rate) + if self.io.units[id].group == 0 then + self.comms.send_command(UNIT_COMMANDS.SET_BURN, id, rate) + log.debug(util.c("UNIT[", id, "]: SET BURN = ", rate)) + else + self.comms.send_command(UNIT_COMMANDS.SET_LIMIT, id, rate) + log.debug(util.c("UNIT[", id, "]: SET LIMIT = ", rate)) + end +end + +-- set waste mode +---@param id integer unit ID +---@param mode integer waste mode +function process.set_waste(id, mode) + self.comms.send_command(UNIT_COMMANDS.SET_WASTE, id, mode) + log.debug(util.c("UNIT[", id, "]: SET WASTE = ", mode)) +end + +-- acknowledge all alarms +---@param id integer unit ID +function process.ack_all_alarms(id) + self.comms.send_command(UNIT_COMMANDS.ACK_ALL_ALARMS, id) + log.debug(util.c("UNIT[", id, "]: ACK ALL ALARMS")) +end + +-- acknowledge an alarm +---@param id integer unit ID +---@param alarm integer alarm ID +function process.ack_alarm(id, alarm) + self.comms.send_command(UNIT_COMMANDS.ACK_ALARM, id, alarm) + log.debug(util.c("UNIT[", id, "]: ACK ALARM ", alarm)) +end + +-- reset an alarm +---@param id integer unit ID +---@param alarm integer alarm ID +function process.reset_alarm(id, alarm) + self.comms.send_command(UNIT_COMMANDS.RESET_ALARM, id, alarm) + log.debug(util.c("UNIT[", id, "]: RESET ALARM ", alarm)) +end + +-- assign a unit to a group +---@param unit_id integer unit ID +---@param group_id integer|0 group ID or 0 for independent +function process.set_group(unit_id, group_id) + self.comms.send_command(UNIT_COMMANDS.SET_GROUP, unit_id, group_id) + log.debug(util.c("UNIT[", unit_id, "]: SET GROUP ", group_id)) +end + +-------------------------- +-- SUPERVISOR RESPONSES -- +-------------------------- + +-- acknowledgement from the supervisor to assign a unit to a group +function process.sv_assign(unit_id, group_id) + self.io.units[unit_id].group = group_id +end + +-- acknowledgement from the supervisor to assign a unit a burn rate limit +function process.sv_limit(unit_id, limit) + self.io.units[unit_id].limit = limit +end + +return process diff --git a/coordinator/startup.lua b/coordinator/startup.lua index e39d535..5faab51 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -19,7 +19,7 @@ local iocontrol = require("coordinator.iocontrol") local renderer = require("coordinator.renderer") local sounder = require("coordinator.sounder") -local COORDINATOR_VERSION = "beta-v0.8.4" +local COORDINATOR_VERSION = "beta-v0.8.5" local print = util.print local println = util.println diff --git a/coordinator/ui/components/unit_overview.lua b/coordinator/ui/components/unit_overview.lua index 7e24396..71a2d87 100644 --- a/coordinator/ui/components/unit_overview.lua +++ b/coordinator/ui/components/unit_overview.lua @@ -2,17 +2,17 @@ -- Basic Unit Overview -- -local core = require("graphics.core") +local core = require("graphics.core") -local style = require("coordinator.ui.style") +local style = require("coordinator.ui.style") local reactor_view = require("coordinator.ui.components.reactor") local boiler_view = require("coordinator.ui.components.boiler") local turbine_view = require("coordinator.ui.components.turbine") -local Div = require("graphics.elements.div") -local PipeNetwork = require("graphics.elements.pipenet") -local TextBox = require("graphics.elements.textbox") +local Div = require("graphics.elements.div") +local PipeNetwork = require("graphics.elements.pipenet") +local TextBox = require("graphics.elements.textbox") local TEXT_ALIGN = core.graphics.TEXT_ALIGN diff --git a/scada-common/comms.lua b/scada-common/comms.lua index bb876dd..2abc533 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -12,7 +12,7 @@ local rtu_t = types.rtu_t local insert = table.insert -comms.version = "1.1.0" +comms.version = "1.1.1" ---@alias PROTOCOLS integer local PROTOCOLS = { @@ -79,7 +79,9 @@ local UNIT_COMMANDS = { SET_WASTE = 4, -- set the waste processing mode ACK_ALL_ALARMS = 5, -- ack all active alarms ACK_ALARM = 6, -- ack a particular alarm - RESET_ALARM = 7 -- reset a particular alarm + RESET_ALARM = 7, -- reset a particular alarm + SET_GROUP = 8, -- assign this unit to a group + SET_LIMIT = 9 -- set this unit maximum auto burn rate } ---@alias CAPI_TYPES integer diff --git a/supervisor/session/coordinator.lua b/supervisor/session/coordinator.lua index 1a5e93f..9a19ce2 100644 --- a/supervisor/session/coordinator.lua +++ b/supervisor/session/coordinator.lua @@ -45,13 +45,13 @@ local PERIODICS = { ---@param in_queue mqueue ---@param out_queue mqueue ---@param facility facility ----@param units table -function coordinator.new_session(id, in_queue, out_queue, facility, units) +function coordinator.new_session(id, in_queue, out_queue, facility) local log_header = "crdn_session(" .. id .. "): " local self = { in_q = in_queue, out_q = out_queue, + units = facility.get_units(), -- connection properties seq_num = 0, r_seq_num = nil, @@ -122,8 +122,8 @@ function coordinator.new_session(id, in_queue, out_queue, facility, units) local builds = {} - for i = 1, #units do - local unit = units[i] ---@type reactor_unit + for i = 1, #self.units do + local unit = self.units[i] ---@type reactor_unit builds[unit.get_id()] = unit.get_build() end @@ -143,8 +143,8 @@ function coordinator.new_session(id, in_queue, out_queue, facility, units) local function _send_unit_statuses() local status = {} - for i = 1, #units do - local unit = units[i] ---@type reactor_unit + for i = 1, #self.units do + local unit = self.units[i] ---@type reactor_unit status[unit.get_id()] = { unit.get_reactor_status(), unit.get_rtu_statuses(), @@ -215,8 +215,8 @@ 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 <= #units then - local unit = units[uid] ---@type reactor_unit + if util.is_int(uid) and uid > 0 and uid <= #self.units then + local unit = self.units[uid] ---@type reactor_unit if cmd == UNIT_COMMANDS.START then self.out_q.push_data(SV_Q_DATA.START, data) @@ -243,13 +243,27 @@ function coordinator.new_session(id, in_queue, out_queue, facility, units) if pkt.length == 3 then unit.ack_alarm(pkt.data[3]) else - log.debug(log_header .. "CRDN command unit ack alarm missing id") + log.debug(log_header .. "CRDN command unit ack alarm missing alarm id") end elseif cmd == UNIT_COMMANDS.RESET_ALARM then if pkt.length == 3 then unit.reset_alarm(pkt.data[3]) else - log.debug(log_header .. "CRDN command unit reset alarm missing id") + log.debug(log_header .. "CRDN command unit reset alarm missing alarm id") + end + elseif cmd == UNIT_COMMANDS.SET_GROUP then + if pkt.length == 3 then + unit.set_group(pkt.data[3]) + _send(SCADA_CRDN_TYPES.UNIT_CMD, { cmd, uid, pkt.data[3] }) + else + log.debug(log_header .. "CRDN command unit set group missing group id") + end + elseif cmd == UNIT_COMMANDS.SET_LIMIT then + if pkt.length == 3 then + unit.set_burn_limit(pkt.data[3]) + _send(SCADA_CRDN_TYPES.UNIT_CMD, { cmd, uid, pkt.data[3] }) + else + log.debug(log_header .. "CRDN command unit set limit missing group id") end else log.debug(log_header .. "CRDN command unknown") diff --git a/supervisor/session/facility.lua b/supervisor/session/facility.lua index 454606c..acb101b 100644 --- a/supervisor/session/facility.lua +++ b/supervisor/session/facility.lua @@ -3,17 +3,102 @@ local rsio = require("scada-common.rsio") local util = require("scada-common.util") local rsctl = require("supervisor.session.rsctl") +local unit = require("supervisor.session.unit") + +local HEATING_WATER = 20000 +local HEATING_SODIUM = 200000 + +-- 7.14 kJ per blade for 1 mB of fissile fuel +local POWER_PER_BLADE = util.joules_to_fe(7140) + +local function m_avg(length, default) + local data = {} + local index = 1 + local last_t = 0 ---@type number|nil + + ---@class moving_average + local public = {} + + -- reset all to a given value + ---@param x number value + function public.reset(x) + data = {} + for _ = 1, length do table.insert(data, x) end + end + + -- record a new value + ---@param x number new value + ---@param t number? optional last update time to prevent duplicated entries + function public.record(x, t) + if type(t) == "number" and last_t == t then + return + end + + data[index] = x + last_t = t + + index = index + 1 + if index > length then index = 1 end + end + + -- compute the moving average + ---@return number average + function public.compute() + local sum = 0 + for i = 1, length do sum = sum + data[i] end + return sum + end + + public.reset(default) + + return public +end + +---@alias PROCESS integer +local PROCESS = { + INACTIVE = 1, + SIMPLE = 2, + CHARGE = 3, + GEN_RATE = 4, + BURN_RATE = 5 +} ---@class facility_management local facility = {} +facility.PROCESS_MODES = PROCESS + -- create a new facility management object -function facility.new() +---@param num_reactors integer number of reactor units +---@param cooling_conf table cooling configurations of reactor units +function facility.new(num_reactors, cooling_conf) local self = { + -- components + units = {}, induction = {}, - redstone = {} + redstone = {}, + -- process control + mode = PROCESS.INACTIVE, + charge_target = 0, -- FE + charge_rate = 0, -- FE/t + charge_limit = 0.99, -- percentage + burn_rate_set = 0, + unit_limits = {}, + -- statistics + im_stat_init = false, + avg_charge = m_avg(10, 0.0), + avg_inflow = m_avg(10, 0.0), + avg_outflow = m_avg(10, 0.0) } + -- create units + for i = 1, num_reactors do + table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES)) + + local u_lim = { burn_rate = -1.0, temp = 1100 } ---@class unit_limit + table.insert(self.unit_limits, u_lim) + end + -- init redstone RTU I/O controller local rs_rtu_io_ctl = rsctl.new(self.redstone) @@ -58,6 +143,13 @@ function facility.new() _unlink_disconnected_units(self.redstone) end + function public.update_units() + for i = 1, #self.units do + local u = self.units[i] ---@type reactor_unit + u.update() + end + end + -- READ STATES/PROPERTIES -- -- get build properties of all machines @@ -94,6 +186,10 @@ function facility.new() return status end + function public.get_units() + return self.units + end + return public end diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 0d5bc1d..b5a72b2 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -37,8 +37,7 @@ local PERIODICS = { ---@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, facility_units) +function rtu.new_session(id, in_queue, out_queue, advertisement, facility) local log_header = "rtu_session(" .. id .. "): " local self = { @@ -46,6 +45,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility, facil out_q = out_queue, modbus_q = mqueue.new(), advert = advertisement, + fac_units = facility.get_units(), -- connection properties seq_num = 0, r_seq_num = nil, @@ -71,8 +71,8 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility, facil local function _handle_advertisement() _reset_config() - for i = 1, #facility_units do - local unit = facility_units[i] ---@type reactor_unit + for i = 1, #self.fac_units do + local unit = self.fac_units[i] ---@type reactor_unit unit.purge_rtu_devices(id) facility.purge_rtu_devices(id) end @@ -105,7 +105,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility, facil if advert_validator.valid() then advert_validator.assert_min(unit_advert.index, 1) advert_validator.assert_min(unit_advert.reactor, 0) - advert_validator.assert_max(unit_advert.reactor, #facility_units) + advert_validator.assert_max(unit_advert.reactor, #self.fac_units) if not advert_validator.valid() then u_type = false end else u_type = false @@ -121,7 +121,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility, facil log.debug(log_header .. "advertisement unit validation failure") else if unit_advert.reactor > 0 then - local target_unit = facility_units[unit_advert.reactor] ---@type reactor_unit + local target_unit = self.fac_units[unit_advert.reactor] ---@type reactor_unit if u_type == RTU_UNIT_TYPES.REDSTONE then -- redstone diff --git a/supervisor/session/svsessions.lua b/supervisor/session/svsessions.lua index 91b4873..10b5b96 100644 --- a/supervisor/session/svsessions.lua +++ b/supervisor/session/svsessions.lua @@ -4,7 +4,6 @@ local util = require("scada-common.util") local facility = require("supervisor.session.facility") local svqtypes = require("supervisor.session.svqtypes") -local unit = require("supervisor.session.unit") local coordinator = require("supervisor.session.coordinator") local plc = require("supervisor.session.plc") @@ -33,8 +32,7 @@ svsessions.SESSION_TYPE = SESSION_TYPE local self = { modem = nil, num_reactors = 0, - facility = facility.new(), - units = {}, + facility = nil, ---@type facility rtu_sessions = {}, plc_sessions = {}, coord_sessions = {}, @@ -199,11 +197,7 @@ end function svsessions.init(modem, num_reactors, cooling_conf) self.modem = modem self.num_reactors = num_reactors - self.units = {} - - for i = 1, self.num_reactors do - table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES)) - end + self.facility = facility.new(num_reactors, cooling_conf) end -- re-link the modem @@ -299,7 +293,8 @@ 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.units[for_reactor].link_plc_session(plc_s) + local units = self.facility.get_units() + units[for_reactor].link_plc_session(plc_s) log.debug("established new PLC session to " .. remote_port .. " with ID " .. self.next_plc_id) @@ -332,7 +327,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, self.units) + rtu_s.instance = rtu.new_session(self.next_rtu_id, rtu_s.in_queue, rtu_s.out_queue, advertisement, self.facility) table.insert(self.rtu_sessions, rtu_s) log.debug("established new RTU session to " .. remote_port .. " with ID " .. self.next_rtu_id) @@ -362,7 +357,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, self.units) + coord_s.instance = coordinator.new_session(self.next_coord_id, coord_s.in_queue, coord_s.out_queue, self.facility) table.insert(self.coord_sessions, coord_s) log.debug("established new coordinator session to " .. remote_port .. " with ID " .. self.next_coord_id) @@ -405,10 +400,7 @@ function svsessions.iterate_all() self.facility.update() -- iterate units - for i = 1, #self.units do - local u = self.units[i] ---@type reactor_unit - u.update() - end + self.facility.update_units() end -- delete all closed sessions diff --git a/supervisor/session/unit.lua b/supervisor/session/unit.lua index 3c378e6..c61a661 100644 --- a/supervisor/session/unit.lua +++ b/supervisor/session/unit.lua @@ -88,7 +88,10 @@ function unit.new(for_reactor, num_boilers, num_turbines) damage_last = 0, damage_est_last = 0, waste_mode = WASTE_MODE.AUTO, - status_text = { "Unknown", "Awaiting Connection..." }, + status_text = { "UNKNOWN", "awaiting connection..." }, + -- auto control + group = 0, + limit = 0.0, -- logic for alarms had_reactor = false, start_ms = 0, @@ -947,6 +950,28 @@ function unit.new(for_reactor, num_boilers, num_turbines) end end + -- set the automatic control group of this unit + ---@param group integer group ID or 0 for independent + function public.set_group(group) + if group >= 0 and group <= 4 then + self.group = group + end + end + + -- set the automatic control max burn rate for this unit + ---@param limit number burn rate limit for auto control + function public.set_burn_limit(limit) + if limit >= 0 then + self.limit = limit + + if self.plc_i ~= nil then + if limit > self.plc_i.get_struct().max_burn then + self.limit = self.plc_i.get_struct().max_burn + end + end + end + end + -- READ STATES/PROPERTIES -- -- get build properties of all machines diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 79fa007..2c340dc 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -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.9.1" +local SUPERVISOR_VERSION = "beta-v0.9.2" local print = util.print local println = util.println