diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 30e9432..f8cd3f4 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -60,6 +60,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) id = id, in_q = in_queue, out_q = out_queue, + modbus_q = mqueue.new(), f_units = facility_units, advert = advertisement, -- connection properties @@ -107,6 +108,8 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) rsio = self.advert[i][4] } + local target_unit = self.f_units[unit_advert.reactor] ---@type reactor_unit + local u_type = unit_advert.type -- validate unit advertisement @@ -133,35 +136,39 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) -- validation fail elseif u_type == RTU_UNIT_TYPES.REDSTONE then -- redstone - unit, rs_in_q = svrs_redstone.new(self.id, i, unit_advert, self.out_q) + unit, rs_in_q = svrs_redstone.new(self.id, i, unit_advert, self.modbus_q) elseif u_type == RTU_UNIT_TYPES.BOILER then -- boiler - unit = svrs_boiler.new(self.id, i, unit_advert, self.out_q) + unit = svrs_boiler.new(self.id, i, unit_advert, self.modbus_q) + target_unit.add_boiler(unit) elseif u_type == RTU_UNIT_TYPES.BOILER_VALVE then -- boiler (Mekanism 10.1+) - unit = svrs_boilerv.new(self.id, 1, unit_advert, self.out_q) + unit = svrs_boilerv.new(self.id, i, unit_advert, self.modbus_q) + target_unit.add_boiler(unit) elseif u_type == RTU_UNIT_TYPES.TURBINE then -- turbine - unit = svrs_turbine.new(self.id, i, unit_advert, self.out_q) + unit = svrs_turbine.new(self.id, i, unit_advert, self.modbus_q) + target_unit.add_turbine(unit) elseif u_type == RTU_UNIT_TYPES.TURBINE_VALVE then -- turbine (Mekanism 10.1+) - unit, tbv_in_q = svrs_turbinev.new(self.id, i, unit_advert, self.out_q) + unit, tbv_in_q = svrs_turbinev.new(self.id, i, unit_advert, self.modbus_q) + target_unit.add_turbine(unit) self.turbine_cmd_capable = true elseif u_type == RTU_UNIT_TYPES.EMACHINE then -- mekanism [energy] machine - unit = svrs_emachine.new(self.id, i, unit_advert, self.out_q) + unit = svrs_emachine.new(self.id, i, unit_advert, self.modbus_q) elseif u_type == RTU_UNIT_TYPES.IMATRIX then -- induction matrix - unit = svrs_imatrix.new(self.id, i, unit_advert, self.out_q) + unit = svrs_imatrix.new(self.id, i, unit_advert, self.modbus_q) elseif u_type == RTU_UNIT_TYPES.SPS then -- super-critical phase shifter - unit = svrs_sps.new(self.id, i, unit_advert, self.out_q) + unit = svrs_sps.new(self.id, i, unit_advert, self.modbus_q) elseif u_type == RTU_UNIT_TYPES.SNA then -- solar neutron activator - unit = svrs_sna.new(self.id, i, unit_advert, self.out_q) + unit = svrs_sna.new(self.id, i, unit_advert, self.modbus_q) elseif u_type == RTU_UNIT_TYPES.ENV_DETECTOR then -- environment detector - unit = svrs_envd.new(self.id, i, unit_advert, self.out_q) + unit = svrs_envd.new(self.id, i, unit_advert, self.modbus_q) else log.error(log_header .. "bad advertisement: encountered unsupported RTU type") end @@ -213,6 +220,17 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) end end + -- send a MODBUS packet + ---@param m_pkt modbus_packet MODBUS packet + local function _send_modbus(m_pkt) + local s_pkt = comms.scada_packet() + + s_pkt.make(self.seq_num, PROTOCOLS.MODBUS_TCP, m_pkt.raw_sendable()) + + self.out_q.push_packet(s_pkt) + self.seq_num = self.seq_num + 1 + end + -- send a SCADA management packet ---@param msg_type SCADA_MGMT_TYPES ---@param msg table @@ -387,11 +405,29 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) end self.periodics.last_update = util.time() + + ---------------------------------------------- + -- pass MODBUS packets on to main out queue -- + ---------------------------------------------- + + for _ = 1, self.modbus_q.length() do + -- get the next message + local msg = self.modbus_q.pop() + + if msg ~= nil then + if msg.qtype == mqueue.TYPE.PACKET then + _send_modbus(msg.message) + end + end + end end return self.connected end + -- handle initial advertisement + _handle_advertisement() + return public end diff --git a/supervisor/session/rtu/boiler.lua b/supervisor/session/rtu/boiler.lua index 5daef5e..7aa25f0 100644 --- a/supervisor/session/rtu/boiler.lua +++ b/supervisor/session/rtu/boiler.lua @@ -108,7 +108,7 @@ function boiler.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.BUILD then diff --git a/supervisor/session/rtu/boilerv.lua b/supervisor/session/rtu/boilerv.lua index 16125f0..bea740a 100644 --- a/supervisor/session/rtu/boilerv.lua +++ b/supervisor/session/rtu/boilerv.lua @@ -1,6 +1,7 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local types = require("scada-common.types") +local util = require("scada-common.util") local unit_session = require("supervisor.session.rtu.unit_session") @@ -60,8 +61,8 @@ function boilerv.new(session_id, unit_id, advert, out_queue) length = 0, width = 0, height = 0, - min_pos = 0, - max_pos = 0, + min_pos = { x = 0, y = 0, z = 0 }, ---@type coordinate + max_pos = { x = 0, y = 0, z = 0 }, ---@type coordinate boil_cap = 0.0, steam_cap = 0, water_cap = 0, @@ -76,16 +77,16 @@ function boilerv.new(session_id, unit_id, advert, out_queue) boil_rate = 0.0 }, tanks = { - steam = {}, ---@type tank_fluid + steam = { type = "mekanism:empty_gas", amount = 0 }, ---@type tank_fluid steam_need = 0, steam_fill = 0.0, - water = {}, ---@type tank_fluid + water = { type = "mekanism:empty_gas", amount = 0 }, ---@type tank_fluid water_need = 0, water_fill = 0.0, - hcool = {}, ---@type tank_fluid + hcool = { type = "mekanism:empty_gas", amount = 0 }, ---@type tank_fluid hcool_need = 0, hcool_fill = 0.0, - ccool = {}, ---@type tank_fluid + ccool = { type = "mekanism:empty_gas", amount = 0 }, ---@type tank_fluid ccool_need = 0, ccool_fill = 0.0 } @@ -105,19 +106,19 @@ function boilerv.new(session_id, unit_id, advert, out_queue) -- query the build of the device local function _request_build() -- read input registers 1 through 13 (start = 1, count = 13) - self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 7 }) + self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 13 }) end -- query the state of the device local function _request_state() - -- read input registers 14 through 16 (start = 14, count = 2) + -- read input registers 14 through 15 (start = 14, count = 2) self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 14, 2 }) end -- query the tanks of the device local function _request_tanks() - -- read input registers 17 through 29 (start = 17, count = 12) - self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 17, 12 }) + -- read input registers 16 through 27 (start = 16, count = 12) + self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 16, 12 }) end -- PUBLIC FUNCTIONS -- @@ -125,7 +126,7 @@ function boilerv.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.FORMED then diff --git a/supervisor/session/rtu/emachine.lua b/supervisor/session/rtu/emachine.lua index db8a17b..e31c2af 100644 --- a/supervisor/session/rtu/emachine.lua +++ b/supervisor/session/rtu/emachine.lua @@ -79,7 +79,7 @@ function emachine.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.BUILD then diff --git a/supervisor/session/rtu/envd.lua b/supervisor/session/rtu/envd.lua index f502e17..93968a9 100644 --- a/supervisor/session/rtu/envd.lua +++ b/supervisor/session/rtu/envd.lua @@ -62,7 +62,7 @@ function envd.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.RAD then diff --git a/supervisor/session/rtu/imatrix.lua b/supervisor/session/rtu/imatrix.lua index 965b57b..e6a9513 100644 --- a/supervisor/session/rtu/imatrix.lua +++ b/supervisor/session/rtu/imatrix.lua @@ -112,7 +112,7 @@ function imatrix.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.FORMED then diff --git a/supervisor/session/rtu/redstone.lua b/supervisor/session/rtu/redstone.lua index ba01400..7ebd28b 100644 --- a/supervisor/session/rtu/redstone.lua +++ b/supervisor/session/rtu/redstone.lua @@ -144,7 +144,7 @@ function redstone.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.DI_READ then diff --git a/supervisor/session/rtu/sna.lua b/supervisor/session/rtu/sna.lua index ccfac69..2d7f885 100644 --- a/supervisor/session/rtu/sna.lua +++ b/supervisor/session/rtu/sna.lua @@ -97,7 +97,7 @@ function sna.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.BUILD then diff --git a/supervisor/session/rtu/sps.lua b/supervisor/session/rtu/sps.lua index c2180d9..18ad727 100644 --- a/supervisor/session/rtu/sps.lua +++ b/supervisor/session/rtu/sps.lua @@ -117,7 +117,7 @@ function sps.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.FORMED then diff --git a/supervisor/session/rtu/turbine.lua b/supervisor/session/rtu/turbine.lua index ab63fed..7fc58f8 100644 --- a/supervisor/session/rtu/turbine.lua +++ b/supervisor/session/rtu/turbine.lua @@ -104,7 +104,7 @@ function turbine.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + local txn_type = self.session.try_resolve(m_pkt) if txn_type == false then -- nothing to do elseif txn_type == TXN_TYPES.BUILD then diff --git a/supervisor/session/rtu/turbinev.lua b/supervisor/session/rtu/turbinev.lua index b31cb7c..56d43ef 100644 --- a/supervisor/session/rtu/turbinev.lua +++ b/supervisor/session/rtu/turbinev.lua @@ -70,6 +70,7 @@ function turbinev.new(session_id, unit_id, advert, out_queue) in_q = mqueue.new(), has_build = false, periodics = { + next_formed_req = 0, next_build_req = 0, next_state_req = 0, next_tanks_req = 0 @@ -81,8 +82,8 @@ function turbinev.new(session_id, unit_id, advert, out_queue) length = 0, width = 0, height = 0, - min_pos = 0, - max_pos = 0, + min_pos = { x = 0, y = 0, z = 0 }, ---@type coordinate + max_pos = { x = 0, y = 0, z = 0 }, ---@type coordinate blades = 0, coils = 0, vents = 0, @@ -101,7 +102,7 @@ function turbinev.new(session_id, unit_id, advert, out_queue) dumping_mode = DUMPING_MODE.IDLE ---@type DUMPING_MODE }, tanks = { - steam = 0, + steam = { type = "mekanism:empty_gas", amount = 0 }, ---@type tank_fluid steam_need = 0, steam_fill = 0.0, energy = 0, @@ -163,9 +164,17 @@ function turbinev.new(session_id, unit_id, advert, out_queue) -- handle a packet ---@param m_pkt modbus_frame function public.handle_packet(m_pkt) - local txn_type = self.session.try_resolve(m_pkt.txn_id) + 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] + 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 == 15 then diff --git a/supervisor/session/rtu/txnctrl.lua b/supervisor/session/rtu/txnctrl.lua index 2ebb526..a1f3b4b 100644 --- a/supervisor/session/rtu/txnctrl.lua +++ b/supervisor/session/rtu/txnctrl.lua @@ -19,6 +19,7 @@ function txnctrl.new() local public = {} local insert = table.insert + local remove = table.remove -- get the length of the transaction list function public.length() @@ -55,8 +56,9 @@ function txnctrl.new() for i = 1, public.length() do if self.list[i].txn_id == txn_id then - txn_type = self.list[i].txn_type - self.list[i] = nil + local entry = remove(self.list, i) + txn_type = entry.txn_type + break end end diff --git a/supervisor/session/rtu/unit_session.lua b/supervisor/session/rtu/unit_session.lua index ed2f027..3a70f26 100644 --- a/supervisor/session/rtu/unit_session.lua +++ b/supervisor/session/rtu/unit_session.lua @@ -1,6 +1,7 @@ local comms = require("scada-common.comms") local log = require("scada-common.log") local types = require("scada-common.types") +local util = require("scada-common.util") local txnctrl = require("supervisor.session.rtu.txnctrl") @@ -57,7 +58,7 @@ function unit_session.new(unit_id, advert, out_queue, log_tag, txn_tags) if m_pkt.scada_frame.protocol() == PROTOCOLS.MODBUS_TCP then if m_pkt.unit_id == self.unit_id then local txn_type = self.transaction_controller.resolve(m_pkt.txn_id) - local txn_tag = " (" .. self.txn_tags[txn_type] .. ")" + local txn_tag = " (" .. util.strval(self.txn_tags[txn_type]) .. ")" if bit.band(m_pkt.func_code, MODBUS_FCODE.ERROR_FLAG) ~= 0 then -- transaction incomplete or failed diff --git a/supervisor/session/unit.lua b/supervisor/session/unit.lua index 6cd3a5a..95fa035 100644 --- a/supervisor/session/unit.lua +++ b/supervisor/session/unit.lua @@ -17,7 +17,8 @@ local DT_KEYS = { BoilerSteam = "BST", BoilerCCool = "BCC", BoilerHCool = "BHC", - TurbineSteam = "TST" + TurbineSteam = "TST", + TurbinePower = "TPR" } -- create a new reactor unit @@ -135,22 +136,21 @@ function unit.new(for_reactor, num_boilers, num_turbines) for i = 1, #self.boilers do local boiler = self.boilers[i] ---@type unit_session - local db = boiler.get_db() ---@type boiler_session_db + local db = boiler.get_db() ---@type boilerv_session_db - ---@todo Mekanism 10.1+ will change water/steam to need .amount - _compute_dt(DT_KEYS.BoilerWater .. boiler.get_device_idx(), db.tanks.water) - _compute_dt(DT_KEYS.BoilerSteam .. boiler.get_device_idx(), db.tanks.steam) + _compute_dt(DT_KEYS.BoilerWater .. boiler.get_device_idx(), db.tanks.water.amount) + _compute_dt(DT_KEYS.BoilerSteam .. boiler.get_device_idx(), db.tanks.steam.amount) _compute_dt(DT_KEYS.BoilerCCool .. boiler.get_device_idx(), db.tanks.ccool.amount) _compute_dt(DT_KEYS.BoilerHCool .. boiler.get_device_idx(), db.tanks.hcool.amount) end for i = 1, #self.turbines do local turbine = self.turbines[i] ---@type unit_session - local db = turbine.get_db() ---@type turbine_session_db + local db = turbine.get_db() ---@type turbinev_session_db - _compute_dt(DT_KEYS.TurbineSteam .. turbine.get_device_idx(), db.tanks.steam) - ---@todo Mekanism 10.1+ needed - -- _compute_dt(DT_KEYS.TurbinePower .. turbine.get_device_idx(), db.?) + _compute_dt(DT_KEYS.TurbineSteam .. turbine.get_device_idx(), db.tanks.steam.amount) + ---@todo unused currently? + _compute_dt(DT_KEYS.TurbinePower .. turbine.get_device_idx(), db.tanks.energy) end end @@ -242,10 +242,12 @@ function unit.new(for_reactor, num_boilers, num_turbines) local idx = boiler.get_device_idx() local db = boiler.get_db() ---@type boiler_session_db + local gaining_hc = _get_dt(DT_KEYS.BoilerHCool .. idx) > 0 or db.tanks.hcool_fill == 1 + -- gaining heated coolant - cfmismatch = cfmismatch or _get_dt(DT_KEYS.BoilerHCool .. idx) > 0 or db.tanks.hcool_fill == 1 + cfmismatch = cfmismatch or gaining_hc -- losing cooled coolant - cfmismatch = cfmismatch or _get_dt(DT_KEYS.BoilerCCool .. idx) < 0 or db.tanks.ccool_fill == 0 + cfmismatch = cfmismatch or _get_dt(DT_KEYS.BoilerCCool .. idx) < 0 or (gaining_hc and db.tanks.ccool_fill == 0) end self.db.annunciator.CoolantFeedMismatch = cfmismatch @@ -264,7 +266,7 @@ function unit.new(for_reactor, num_boilers, num_turbines) -- go through turbines for stats and online for i = 1, #self.turbines do - local session = self.turbine[i] ---@type unit_session + local session = self.turbines[i] ---@type unit_session local turbine = session.get_db() ---@type turbine_session_db total_flow_rate = total_flow_rate + turbine.state.flow_rate @@ -329,6 +331,8 @@ function unit.new(for_reactor, num_boilers, num_turbines) -- PUBLIC FUNCTIONS -- + -- ADD/LINK DEVICES -- + -- link the PLC ---@param plc_session plc_session_struct function public.link_plc_session(plc_session) @@ -388,6 +392,8 @@ function unit.new(for_reactor, num_boilers, num_turbines) table.insert(self.redstone[field], accessor) end + -- UPDATE SESSION -- + -- update (iterate) this unit function public.update() -- unlink PLC if session was closed @@ -403,6 +409,16 @@ function unit.new(for_reactor, num_boilers, num_turbines) _update_annunciator() end + -- COMMAND UNIT -- + + -- SCRAM reactor + function public.scram() + if self.plc_s ~= nil then + end + end + + -- READ STATES/PROPERTIES -- + -- get build properties of all machines function public.get_build() local build = {} diff --git a/supervisor/startup.lua b/supervisor/startup.lua index ea313a4..c7d4072 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -13,7 +13,7 @@ local svsessions = require("supervisor.session.svsessions") local config = require("supervisor.config") local supervisor = require("supervisor.supervisor") -local SUPERVISOR_VERSION = "beta-v0.5.10" +local SUPERVISOR_VERSION = "beta-v0.5.11" local print = util.print local println = util.println