From 6d97d45227dd6cda1a69b1a266944744dedb8a7c Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Sat, 4 Jun 2022 17:45:52 -0400 Subject: [PATCH] #67 imatrix RTU supervisor session --- rtu/dev/imatrix_rtu.lua | 8 +- supervisor/session/rtu.lua | 13 +- supervisor/session/rtu/imatrix.lua | 203 +++++++++++++++++++++++++++++ supervisor/startup.lua | 2 +- 4 files changed, 220 insertions(+), 6 deletions(-) create mode 100644 supervisor/session/rtu/imatrix.lua diff --git a/rtu/dev/imatrix_rtu.lua b/rtu/dev/imatrix_rtu.lua index 3ac3acd..6e99453 100644 --- a/rtu/dev/imatrix_rtu.lua +++ b/rtu/dev/imatrix_rtu.lua @@ -25,13 +25,13 @@ function imatrix_rtu.new(imatrix) unit.connect_input_reg(imatrix.getTransferCap) unit.connect_input_reg(imatrix.getInstalledCells) unit.connect_input_reg(imatrix.getInstalledProviders) - -- containers - unit.connect_input_reg(imatrix.getEnergy) - unit.connect_input_reg(imatrix.getEnergyNeeded) - unit.connect_input_reg(imatrix.getEnergyFilledPercentage) -- I/O rates unit.connect_input_reg(imatrix.getLastInput) unit.connect_input_reg(imatrix.getLastOutput) + -- tanks + unit.connect_input_reg(imatrix.getEnergy) + unit.connect_input_reg(imatrix.getEnergyNeeded) + unit.connect_input_reg(imatrix.getEnergyFilledPercentage) -- holding registers -- -- none diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index e4827d9..abdfc61 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -8,6 +8,7 @@ local util = require("scada-common.util") local svrs_boiler = require("supervisor.session.rtu.boiler") local svrs_emachine = require("supervisor.session.rtu.emachine") local svrs_envd = require("supervisor.session.rtu.envd") +local svrs_imatrix = require("supervisor.session.rtu.imatrix") local svrs_redstone = require("supervisor.session.rtu.redstone") local svrs_sna = require("supervisor.session.rtu.sna") local svrs_sps = require("supervisor.session.rtu.sps") @@ -91,24 +92,34 @@ function rtu.new_session(id, in_queue, out_queue, advertisement) -- create unit by type if u_type == RTU_UNIT_TYPES.REDSTONE then + -- redstone unit, rs_in_q = svrs_redstone.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.BOILER then + -- boiler unit = svrs_boiler.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.BOILER_VALVE then + -- boiler (Mekanism 10.1+) -- @todo Mekanism 10.1+ elseif u_type == RTU_UNIT_TYPES.TURBINE then + -- turbine unit = svrs_turbine.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.TURBINE_VALVE then + -- turbine (Mekanism 10.1+) -- @todo Mekanism 10.1+ elseif u_type == RTU_UNIT_TYPES.EMACHINE then + -- mekanism [energy] machine unit = svrs_emachine.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.IMATRIX then - -- @todo Mekanism 10.1+ + -- induction matrix + unit = svrs_imatrix.new(self.id, i, unit_advert, self.out_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) elseif u_type == RTU_UNIT_TYPES.SNA then + -- solar neutron activator unit = svrs_sna.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.ENV_DETECTOR then + -- environment detector unit = svrs_envd.new(self.id, i, unit_advert, self.out_q) else log.error(log_header .. "bad advertisement: encountered unsupported RTU type") diff --git a/supervisor/session/rtu/imatrix.lua b/supervisor/session/rtu/imatrix.lua new file mode 100644 index 0000000..92c32f4 --- /dev/null +++ b/supervisor/session/rtu/imatrix.lua @@ -0,0 +1,203 @@ +local comms = require("scada-common.comms") +local log = require("scada-common.log") +local types = require("scada-common.types") + +local unit_session = require("supervisor.session.rtu.unit_session") + +local imatrix = {} + +local RTU_UNIT_TYPES = comms.RTU_UNIT_TYPES +local MODBUS_FCODE = types.MODBUS_FCODE + +local TXN_TYPES = { + FORMED = 1, + BUILD = 2, + STATE = 3, + TANKS = 4 +} + +local TXN_TAGS = { + "imatrix.formed", + "imatrix.build", + "imatrix.state", + "imatrix.tanks", +} + +local PERIODICS = { + FORMED = 2000, + BUILD = 1000, + STATE = 500, + TANKS = 1000 +} + +-- create a new imatrix rtu session runner +---@param session_id integer +---@param unit_id integer +---@param advert rtu_advertisement +---@param out_queue mqueue +function imatrix.new(session_id, unit_id, advert, out_queue) + -- type check + if advert.type ~= RTU_UNIT_TYPES.IMATRIX then + log.error("attempt to instantiate imatrix RTU for type '" .. advert.type .. "'. this is a bug.") + return nil + end + + local log_tag = "session.rtu(" .. session_id .. ").imatrix(" .. advert.index .. "): " + + local self = { + session = unit_session.new(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 imatrix_session_db + db = { + formed = false, + build = { + length = 0, + width = 0, + height = 0, + min_pos = 0, + max_pos = 0, + max_energy = 0, + transfer_cap = 0, + cells = 0, + providers = 0 + }, + state = { + last_input = 0, + last_output = 0 + }, + tanks = { + energy = 0, + energy_need = 0, + energy_fill = 0.0 + } + } + } + + local public = self.session.get() + + -- PRIVATE FUNCTIONS -- + + -- query if the build 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 9 (start = 1, count = 9) + self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 9 }) + end + + -- query the state of the device + local function _request_state() + -- read input register 10 through 11 (start = 10, count = 2) + self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 10, 2 }) + end + + -- query the tanks of the device + local function _request_tanks() + -- read input registers 12 through 15 (start = 12, count = 3) + self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 12, 3 }) + 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.txn_id) + 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 + -- load in data if correct length + if m_pkt.length == 9 then + 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.max_energy = m_pkt.data[6] + self.db.build.transfer_cap = m_pkt.data[7] + self.db.build.cells = m_pkt.data[8] + self.db.build.providers = m_pkt.data[9] + self.has_build = true + else + log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") + end + elseif txn_type == TXN_TYPES.STATE then + -- state response + -- load in data if correct length + if m_pkt.length == 2 then + self.db.state.last_input = m_pkt.data[1] + self.db.state.last_output = m_pkt.data[2] + else + log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") + end + elseif txn_type == TXN_TYPES.TANKS then + -- tanks response + -- load in data if correct length + if m_pkt.length == 3 then + self.db.tanks.energy = m_pkt.data[1] + self.db.tanks.energy_need = m_pkt.data[2] + self.db.tanks.energy_fill = m_pkt.data[3] + else + log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") + end + 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) + 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 + + -- get the unit session database + function public.get_db() return self.db end + + return public +end + +return imatrix diff --git a/supervisor/startup.lua b/supervisor/startup.lua index c71804f..82c38e6 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.4.8" +local SUPERVISOR_VERSION = "beta-v0.4.9" local print = util.print local println = util.println