diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index c45ea39..00dc81b 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -9,6 +9,7 @@ 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_redstone = require("supervisor.session.rtu.redstone") +local svrs_sps = require("supervisor.session.rtu.sps") local svrs_turbine = require("supervisor.session.rtu.turbine") local rtu = {} @@ -103,7 +104,7 @@ function rtu.new_session(id, in_queue, out_queue, advertisement) elseif u_type == RTU_UNIT_TYPES.IMATRIX then -- @todo Mekanism 10.1+ elseif u_type == RTU_UNIT_TYPES.SPS then - -- @todo Mekanism 10.1+ + unit = svrs_sps.new(self.id, i, unit_advert, self.out_q) elseif u_type == RTU_UNIT_TYPES.SNA then -- @todo Mekanism 10.1+ elseif u_type == RTU_UNIT_TYPES.ENV_DETECTOR then diff --git a/supervisor/session/rtu/sps.lua b/supervisor/session/rtu/sps.lua new file mode 100644 index 0000000..47f25ac --- /dev/null +++ b/supervisor/session/rtu/sps.lua @@ -0,0 +1,213 @@ +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 sps = {} + +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 = { + "sps.formed", + "sps.build", + "sps.state", + "sps.tanks", +} + +local PERIODICS = { + FORMED = 2000, + BUILD = 1000, + STATE = 500, + TANKS = 1000 +} + +-- create a new sps rtu session runner +---@param session_id integer +---@param unit_id integer +---@param advert rtu_advertisement +---@param out_queue mqueue +function sps.new(session_id, unit_id, advert, out_queue) + -- type check + if advert.type ~= RTU_UNIT_TYPES.SPS then + log.error("attempt to instantiate sps RTU for type '" .. advert.type .. "'. this is a bug.") + return nil + end + + local log_tag = "session.rtu(" .. session_id .. ").sps(" .. 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 sps_session_db + db = { + formed = false, + build = { + length = 0, + width = 0, + height = 0, + min_pos = 0, + max_pos = 0, + coils = 0, + input_cap = 0, + output_cap = 0, + max_energy = 0 + }, + state = { + process_rate = 0.0 + }, + tanks = { + input = {}, ---@type tank_fluid + input_need = 0, + input_fill = 0.0, + output = {}, ---@type tank_fluid + output_need = 0, + output_fill = 0.0, + 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 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 input registers 8 through 9 (start = 8, count = 2) + self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 8, 2 }) + end + + -- query the tanks of the device + local function _request_tanks() + -- read input registers 10 through 21 (start = 10, count = 12) + self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 10, 12 }) + 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.coils = m_pkt.data[6] + self.db.build.input_cap = m_pkt.data[7] + self.db.build.output_cap = m_pkt.data[8] + self.db.build.max_energy = 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 == 1 then + self.db.state.process_rate = 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 + -- load in data if correct length + if m_pkt.length == 9 then + self.db.tanks.input = m_pkt.data[1] + self.db.tanks.input_need = m_pkt.data[2] + self.db.tanks.input_fill = m_pkt.data[3] + self.db.tanks.output = m_pkt.data[4] + self.db.tanks.output_need = m_pkt.data[5] + self.db.tanks.output_fill = m_pkt.data[6] + self.db.tanks.energy = m_pkt.data[7] + self.db.tanks.energy_need = m_pkt.data[8] + self.db.tanks.energy_fill = m_pkt.data[9] + 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 sps diff --git a/supervisor/startup.lua b/supervisor/startup.lua index bfdd925..456d9d8 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.6" +local SUPERVISOR_VERSION = "beta-v0.4.7" local print = util.print local println = util.println