diff --git a/coordinator/iocontrol.lua b/coordinator/iocontrol.lua index 0d953ae..4258bc1 100644 --- a/coordinator/iocontrol.lua +++ b/coordinator/iocontrol.lua @@ -133,12 +133,10 @@ function iocontrol.record_builds(builds) -- boiler builds if type(build.boilers) == "table" then for id, boiler in pairs(build.boilers) do - unit.boiler_data_tbl[id] = { - formed = boiler[2], ---@type boolean|nil - build = boiler[1] ---@type table - } + unit.boiler_data_tbl[id].formed = boiler[1] ---@type boolean + unit.boiler_data_tbl[id].build = boiler[2] ---@type table - unit.boiler_ps_tbl[id].publish("formed", boiler[2]) + unit.boiler_ps_tbl[id].publish("formed", boiler[1]) for key, val in pairs(unit.boiler_data_tbl[id].build) do unit.boiler_ps_tbl[id].publish(key, val) @@ -149,12 +147,10 @@ function iocontrol.record_builds(builds) -- turbine builds if type(build.turbines) == "table" then for id, turbine in pairs(build.turbines) do - unit.turbine_data_tbl[id] = { - formed = turbine[2], ---@type boolean|nil - build = turbine[1] ---@type table - } + unit.turbine_data_tbl[id].formed = turbine[1] ---@type boolean + unit.turbine_data_tbl[id].build = turbine[2] ---@type table - unit.turbine_ps_tbl[id].publish("formed", turbine[2]) + unit.turbine_ps_tbl[id].publish("formed", turbine[1]) for key, val in pairs(unit.turbine_data_tbl[id].build) do unit.turbine_ps_tbl[id].publish(key, val) @@ -290,15 +286,26 @@ function iocontrol.update_statuses(statuses) end for id, boiler in pairs(rtu_statuses.boilers) do - unit.boiler_data_tbl[id].state = boiler[1] ---@type table - unit.boiler_data_tbl[id].tanks = boiler[2] ---@type table + 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 - if data.state.boil_rate > 0 then - unit.boiler_ps_tbl[id].publish("computed_status", 3) -- active + unit.boiler_ps_tbl[id].publish("formed", data.formed) + unit.boiler_ps_tbl[id].publish("faulted", rtu_faulted) + + if data.formed then + if rtu_faulted then + unit.boiler_ps_tbl[id].publish("computed_status", 4) -- faulted + elseif data.state.boil_rate > 0 then + unit.boiler_ps_tbl[id].publish("computed_status", 3) -- active + else + unit.boiler_ps_tbl[id].publish("computed_status", 2) -- idle + end else - unit.boiler_ps_tbl[id].publish("computed_status", 2) -- idle + unit.boiler_ps_tbl[id].publish("computed_status", 5) -- not formed end for key, val in pairs(unit.boiler_data_tbl[id].state) do @@ -322,17 +329,28 @@ function iocontrol.update_statuses(statuses) end for id, turbine in pairs(rtu_statuses.turbines) do - unit.turbine_data_tbl[id].state = turbine[1] ---@type table - unit.turbine_data_tbl[id].tanks = turbine[2] ---@type table + 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 - if data.tanks.steam_fill >= 0.99 then - unit.turbine_ps_tbl[id].publish("computed_status", 4) -- trip - elseif data.state.flow_rate < 100 then - unit.turbine_ps_tbl[id].publish("computed_status", 2) -- idle + unit.turbine_ps_tbl[id].publish("formed", data.formed) + unit.turbine_ps_tbl[id].publish("faulted", rtu_faulted) + + if data.formed then + if data.tanks.energy_fill >= 0.99 then + unit.turbine_ps_tbl[id].publish("computed_status", 4) -- trip + elseif rtu_faulted then + unit.turbine_ps_tbl[id].publish("computed_status", 5) -- faulted + elseif data.state.flow_rate < 100 then + unit.turbine_ps_tbl[id].publish("computed_status", 2) -- idle + else + unit.turbine_ps_tbl[id].publish("computed_status", 3) -- active + end else - unit.turbine_ps_tbl[id].publish("computed_status", 3) -- active + unit.turbine_ps_tbl[id].publish("computed_status", 6) -- not formed end for key, val in pairs(unit.turbine_data_tbl[id].state) do diff --git a/coordinator/startup.lua b/coordinator/startup.lua index 6740bdd..1c6b90b 100644 --- a/coordinator/startup.lua +++ b/coordinator/startup.lua @@ -16,7 +16,7 @@ local config = require("coordinator.config") local coordinator = require("coordinator.coordinator") local renderer = require("coordinator.renderer") -local COORDINATOR_VERSION = "alpha-v0.6.4" +local COORDINATOR_VERSION = "alpha-v0.6.5" local print = util.print local println = util.println diff --git a/coordinator/ui/components/boiler.lua b/coordinator/ui/components/boiler.lua index 08cab0a..c7a1b97 100644 --- a/coordinator/ui/components/boiler.lua +++ b/coordinator/ui/components/boiler.lua @@ -22,7 +22,7 @@ local function new_view(root, x, y, ps) local text_fg_bg = cpair(colors.black, colors.lightGray) local lu_col = cpair(colors.gray, colors.gray) - local status = StateIndicator{parent=boiler,x=10,y=1,states=style.boiler.states,value=1,min_width=10} + local status = StateIndicator{parent=boiler,x=9,y=1,states=style.boiler.states,value=1,min_width=12} local temp = DataIndicator{parent=boiler,x=5,y=3,lu_colors=lu_col,label="Temp:",unit="K",format="%10.2f",value=0,width=22,fg_bg=text_fg_bg} local boil_r = DataIndicator{parent=boiler,x=5,y=4,lu_colors=lu_col,label="Boil:",unit="mB/t",format="%10.0f",value=0,commas=true,width=22,fg_bg=text_fg_bg} diff --git a/coordinator/ui/components/turbine.lua b/coordinator/ui/components/turbine.lua index f4cbcf1..4070eb0 100644 --- a/coordinator/ui/components/turbine.lua +++ b/coordinator/ui/components/turbine.lua @@ -23,7 +23,7 @@ local function new_view(root, x, y, ps) local text_fg_bg = cpair(colors.black, colors.lightGray) local lu_col = cpair(colors.gray, colors.gray) - local status = StateIndicator{parent=turbine,x=8,y=1,states=style.turbine.states,value=1,min_width=10} + local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12} local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,width=16,fg_bg=text_fg_bg} local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg_bg} diff --git a/coordinator/ui/style.lua b/coordinator/ui/style.lua index 395e6f2..ae83155 100644 --- a/coordinator/ui/style.lua +++ b/coordinator/ui/style.lua @@ -76,6 +76,14 @@ style.boiler = { { color = cpair(colors.black, colors.green), text = "ACTIVE" + }, + { + color = cpair(colors.black, colors.orange), + text = "RTU FAULT" + }, + { + color = cpair(colors.black, colors.orange), + text = "NOT FORMED" } } } @@ -98,6 +106,14 @@ style.turbine = { { color = cpair(colors.black, colors.red), text = "TRIP" + }, + { + color = cpair(colors.black, colors.orange), + text = "RTU FAULT" + }, + { + color = cpair(colors.black, colors.orange), + text = "NOT FORMED" } } } diff --git a/rtu/rtu.lua b/rtu/rtu.lua index c427f54..229d58b 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -271,6 +271,12 @@ function rtu.comms(version, modem, local_port, server_port, conn_watchdog) _send(SCADA_MGMT_TYPES.RTU_ADVERT, advertisement) end + -- notify that a peripheral was remounted + ---@param unit_index integer RTU unit ID + function public.send_remounted(unit_index) + _send(SCADA_MGMT_TYPES.RTU_DEV_REMOUNT, { unit_index }) + end + -- parse a MODBUS/SCADA packet ---@param side string ---@param sender integer @@ -400,6 +406,8 @@ function rtu.comms(version, modem, local_port, server_port, conn_watchdog) -- acknowledgement rtu_state.linked = true self.r_seq_num = nil + println_ts("supervisor connection established") + log.info("supervisor connection established") elseif packet.type == SCADA_MGMT_TYPES.RTU_ADVERT then -- request for capabilities again public.send_advertisement(units) diff --git a/rtu/startup.lua b/rtu/startup.lua index 7e6978d..6ce9ffe 100644 --- a/rtu/startup.lua +++ b/rtu/startup.lua @@ -24,7 +24,7 @@ local sna_rtu = require("rtu.dev.sna_rtu") local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") -local RTU_VERSION = "beta-v0.8.2" +local RTU_VERSION = "beta-v0.9.0" local rtu_t = types.rtu_t @@ -116,8 +116,8 @@ local function configure() -- redstone interfaces for entry_idx = 1, #rtu_redstone do local rs_rtu = redstone_rtu.new() - local io_table = rtu_redstone[entry_idx].io - local io_reactor = rtu_redstone[entry_idx].for_reactor + local io_table = rtu_redstone[entry_idx].io ---@type table + local io_reactor = rtu_redstone[entry_idx].for_reactor ---@type integer -- CHECK: reactor ID must be >= to 1 if (not util.is_int(io_reactor)) or (io_reactor <= 0) then @@ -218,6 +218,7 @@ local function configure() index = entry_idx, reactor = io_reactor, device = capabilities, -- use device field for redstone channels + formed = nil, ---@type boolean|nil rtu = rs_rtu, ---@type rtu_device|rtu_rs_device modbus_io = modbus.new(rs_rtu, false), pkt_queue = nil, ---@type mqueue|nil @@ -265,26 +266,55 @@ local function configure() local type = ppm.get_type(name) local rtu_iface = nil ---@type rtu_device local rtu_type = "" + local formed = nil ---@type boolean|nil if type == "boilerValve" then -- boiler multiblock rtu_type = rtu_t.boiler_valve rtu_iface = boilerv_rtu.new(device) + formed = device.isFormed() + + if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then + println_ts(util.c("configure> failed to check if '", name, "' is formed")) + log.fatal(util.c("configure> failed to check if '", name, "' is a formed boiler multiblock")) + return false + end elseif type == "turbineValve" then -- turbine multiblock rtu_type = rtu_t.turbine_valve rtu_iface = turbinev_rtu.new(device) + formed = device.isFormed() + + if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then + println_ts(util.c("configure> failed to check if '", name, "' is formed")) + log.fatal(util.c("configure> failed to check if '", name, "' is a formed turbine multiblock")) + return false + end elseif type == "inductionPort" then -- induction matrix multiblock rtu_type = rtu_t.induction_matrix rtu_iface = imatrix_rtu.new(device) + formed = device.isFormed() + + if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then + println_ts(util.c("configure> failed to check if '", name, "' is formed")) + log.fatal(util.c("configure> failed to check if '", name, "' is a formed induction matrix multiblock")) + return false + end elseif type == "spsPort" then -- SPS multiblock rtu_type = rtu_t.sps rtu_iface = sps_rtu.new(device) + formed = device.isFormed() + + if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then + println_ts(util.c("configure> failed to check if '", name, "' is formed")) + log.fatal(util.c("configure> failed to check if '", name, "' is a formed SPS multiblock")) + return false + end elseif type == "solarNeutronActivator" then -- SNA - rtu_type = rtu_t.sps + rtu_type = rtu_t.sna rtu_iface = sna_rtu.new(device) elseif type == "environmentDetector" then -- advanced peripherals environment detector @@ -305,6 +335,7 @@ local function configure() index = index, reactor = for_reactor, device = device, + formed = formed, rtu = rtu_iface, ---@type rtu_device|rtu_rs_device modbus_io = modbus.new(rtu_iface, true), pkt_queue = mqueue.new(), ---@type mqueue|nil diff --git a/rtu/threads.lua b/rtu/threads.lua index 0cfcd7a..e3d4cd7 100644 --- a/rtu/threads.lua +++ b/rtu/threads.lua @@ -5,7 +5,10 @@ local types = require("scada-common.types") local util = require("scada-common.util") local boilerv_rtu = require("rtu.dev.boilerv_rtu") +local envd_rtu = require("rtu.dev.envd_rtu") local imatrix_rtu = require("rtu.dev.imatrix_rtu") +local sna_rtu = require("rtu.dev.sna_rtu") +local sps_rtu = require("rtu.dev.sps_rtu") local turbinev_rtu = require("rtu.dev.turbinev_rtu") local modbus = require("rtu.modbus") @@ -117,20 +120,35 @@ function threads.thread__main(smem) local unit = units[i] ---@type rtu_unit_registry_entry -- find disconnected device to reconnect + -- note: cannot check isFormed as that would yield this coroutine and consume events if unit.name == param1 then -- found, re-link unit.device = device if unit.type == rtu_t.boiler_valve then unit.rtu = boilerv_rtu.new(device) + unit.formed = true elseif unit.type == rtu_t.turbine_valve then unit.rtu = turbinev_rtu.new(device) + unit.formed = true elseif unit.type == rtu_t.induction_matrix then unit.rtu = imatrix_rtu.new(device) + unit.formed = true + elseif unit.type == rtu_t.sps then + unit.rtu = sps_rtu.new(device) + unit.formed = true + elseif unit.type == rtu_t.sna then + unit.rtu = sna_rtu.new(device) + elseif unit.type == rtu_t.env_detector then + unit.rtu = envd_rtu.new(device) + else + log.error(util.c("unreachable case occured trying to identify reconnected RTU unit type (", unit.name, ")"), true) end unit.modbus_io = modbus.new(unit.rtu, true) + rtu_comms.send_remounted(unit.index) + println_ts("reconnected the " .. unit.type .. " on interface " .. unit.name) end end @@ -256,6 +274,12 @@ function threads.thread__unit_comms(smem, unit) local last_update = util.time() + local check_formed = type(unit.formed) == "boolean" + local last_f_check = 0 + + local detail_name = util.c(unit.type, " (", unit.name, ") [", unit.index, "] for reactor ", unit.reactor) + local short_name = util.c(unit.type, " (", unit.name, ")") + if packet_queue == nil then log.error("rtu unit thread created without a message queue, exiting...", true) return @@ -283,9 +307,64 @@ function threads.thread__unit_comms(smem, unit) util.nop() end + + -- check if multiblocks is still formed + if check_formed and (util.time() - last_f_check > 1000) then + if (not unit.formed) and unit.device.isFormed() then + -- newly re-formed + local iface = ppm.get_iface(unit.device) + if iface then + log.info(util.c("unmounting and remounting reformed RTU unit ", detail_name)) + + ppm.unmount(unit.device) + + local type, device = ppm.mount(iface) + + if device ~= nil then + if type == "boilerValve" and unit.type == rtu_t.boiler_valve then + -- boiler multiblock + unit.device = device + unit.rtu = boilerv_rtu.new(device) + unit.formed = device.isFormed() + unit.modbus_io = modbus.new(unit.rtu, true) + elseif type == "turbineValve" and unit.type == rtu_t.turbine_valve then + -- turbine multiblock + unit.device = device + unit.rtu = turbinev_rtu.new(device) + unit.formed = device.isFormed() + unit.modbus_io = modbus.new(unit.rtu, true) + elseif type == "inductionPort" and unit.type == rtu_t.induction_matrix then + -- induction matrix multiblock + unit.device = device + unit.rtu = imatrix_rtu.new(device) + unit.formed = device.isFormed() + unit.modbus_io = modbus.new(unit.rtu, true) + elseif type == "spsPort" and unit.type == rtu_t.sps then + -- SPS multiblock + unit.device = device + unit.rtu = sps_rtu.new(device) + unit.formed = device.isFormed() + unit.modbus_io = modbus.new(unit.rtu, true) + else + log.error("illegal remount of non-multiblock RTU attempted for " .. short_name, true) + end + + rtu_comms.send_remounted(unit.index) + else + -- fully lost the peripheral now :( + log.error(util.c(unit.name, " lost (failed reconnect)")) + end + + log.info("reconnected the " .. unit.type .. " on interface " .. unit.name) + else + log.error("failed to get interface of previously connected RTU unit " .. detail_name, true) + end + end + end + -- check for termination request if rtu_state.shutdown then - log.info("rtu unit thread exiting -> " .. unit.type .. "(" .. unit.name .. ")") + log.info("rtu unit thread exiting -> " .. short_name) break end @@ -305,7 +384,7 @@ function threads.thread__unit_comms(smem, unit) end if not rtu_state.shutdown then - log.info(util.c("rtu unit thread ", unit.type, "(", unit.name, ") restarting in 5 seconds...")) + log.info(util.c("rtu unit thread ", unit.type, "(", unit.name, " restarting in 5 seconds...")) util.psleep(5) end end diff --git a/scada-common/comms.lua b/scada-common/comms.lua index 352e77f..e4df65c 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -44,8 +44,9 @@ local RPLC_LINKING = { local SCADA_MGMT_TYPES = { KEEP_ALIVE = 0, -- keep alive packet w/ RTT CLOSE = 1, -- close a connection - RTU_ADVERT = 2, -- RTU capability advertisement - REMOTE_LINKED = 3 -- remote device linked + REMOTE_LINKED = 2, -- remote device linked + RTU_ADVERT = 3, -- RTU capability advertisement + RTU_DEV_REMOUNT = 4 -- RTU multiblock possbily changed (formed, unformed) due to PPM remount } ---@alias SCADA_CRDN_TYPES integer @@ -383,7 +384,8 @@ function comms.mgmt_packet() return self.type == SCADA_MGMT_TYPES.KEEP_ALIVE or self.type == SCADA_MGMT_TYPES.CLOSE or self.type == SCADA_MGMT_TYPES.REMOTE_LINKED or - self.type == SCADA_MGMT_TYPES.RTU_ADVERT + self.type == SCADA_MGMT_TYPES.RTU_ADVERT or + self.type == SCADA_MGMT_TYPES.RTU_DEV_REMOUNT end -- make a SCADA management packet diff --git a/supervisor/session/rtu.lua b/supervisor/session/rtu.lua index 1469863..ec48bc0 100644 --- a/supervisor/session/rtu.lua +++ b/supervisor/session/rtu.lua @@ -288,6 +288,16 @@ function rtu.new_session(id, in_queue, out_queue, advertisement, facility_units) -- handle advertisement; this will re-create all unit sub-sessions self.advert = pkt.data _handle_advertisement() + elseif pkt.type == SCADA_MGMT_TYPES.RTU_DEV_REMOUNT then + if pkt.length == 1 then + local unit_id = pkt[1] + if self.units[unit_id] ~= nil then + local unit = self.units[unit_id] ---@type unit_session + unit.invalidate_cache() + end + else + log.debug(log_header .. "SCADA RTU device re-mount packet length mismatch") + end else log.debug(log_header .. "handler received unsupported SCADA_MGMT packet type " .. pkt.type) end diff --git a/supervisor/session/rtu/boilerv.lua b/supervisor/session/rtu/boilerv.lua index bd9aaad..8b5b02e 100644 --- a/supervisor/session/rtu/boilerv.lua +++ b/supervisor/session/rtu/boilerv.lua @@ -159,6 +159,8 @@ function boilerv.new(session_id, unit_id, advert, out_queue) self.db.build.max_boil_rate = m_pkt.data[12] self.db.build.env_loss = m_pkt.data[13] self.has_build = true + + out_queue.push_command(unit_session.RTU_US_CMDS.BUILD_CHANGED) else log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -227,6 +229,13 @@ function boilerv.new(session_id, unit_id, advert, out_queue) 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 function public.get_db() return self.db end diff --git a/supervisor/session/rtu/envd.lua b/supervisor/session/rtu/envd.lua index 3050835..a5546e4 100644 --- a/supervisor/session/rtu/envd.lua +++ b/supervisor/session/rtu/envd.lua @@ -94,6 +94,11 @@ function envd.new(session_id, unit_id, advert, out_queue) self.session.post_update() end + -- invalidate build cache + function public.invalidate_cache() + -- no build cache for this device + end + -- get the unit session database function public.get_db() return self.db end diff --git a/supervisor/session/rtu/imatrix.lua b/supervisor/session/rtu/imatrix.lua index fc64b6d..3ebc7ab 100644 --- a/supervisor/session/rtu/imatrix.lua +++ b/supervisor/session/rtu/imatrix.lua @@ -142,6 +142,8 @@ function imatrix.new(session_id, unit_id, advert, out_queue) self.db.build.cells = m_pkt.data[8] self.db.build.providers = m_pkt.data[9] self.has_build = true + + out_queue.push_command(unit_session.RTU_US_CMDS.BUILD_CHANGED) else log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -201,6 +203,13 @@ function imatrix.new(session_id, unit_id, advert, out_queue) 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 function public.get_db() return self.db end diff --git a/supervisor/session/rtu/redstone.lua b/supervisor/session/rtu/redstone.lua index 7ebd28b..35764ac 100644 --- a/supervisor/session/rtu/redstone.lua +++ b/supervisor/session/rtu/redstone.lua @@ -254,6 +254,11 @@ function redstone.new(session_id, unit_id, advert, out_queue) self.session.post_update() end + -- invalidate build cache + function public.invalidate_cache() + -- no build cache for this device + end + -- get the unit session database function public.get_db() return self.db end diff --git a/supervisor/session/rtu/sna.lua b/supervisor/session/rtu/sna.lua index 68f38ae..297708a 100644 --- a/supervisor/session/rtu/sna.lua +++ b/supervisor/session/rtu/sna.lua @@ -112,6 +112,8 @@ function sna.new(session_id, unit_id, advert, out_queue) self.db.build.input_cap = m_pkt.data[1] self.db.build.output_cap = m_pkt.data[2] self.has_build = true + + out_queue.push_command(unit_session.RTU_US_CMDS.BUILD_CHANGED) else log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -167,6 +169,12 @@ function sna.new(session_id, unit_id, advert, out_queue) self.session.post_update() end + -- invalidate build cache + function public.invalidate_cache() + self.periodics.next_build_req = 0 + self.has_build = false + end + -- get the unit session database function public.get_db() return self.db end diff --git a/supervisor/session/rtu/sps.lua b/supervisor/session/rtu/sps.lua index badad45..8b42bd1 100644 --- a/supervisor/session/rtu/sps.lua +++ b/supervisor/session/rtu/sps.lua @@ -147,6 +147,8 @@ function sps.new(session_id, unit_id, advert, out_queue) self.db.build.output_cap = m_pkt.data[8] self.db.build.max_energy = m_pkt.data[9] self.has_build = true + + out_queue.push_command(unit_session.RTU_US_CMDS.BUILD_CHANGED) else log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -211,6 +213,13 @@ function sps.new(session_id, unit_id, advert, out_queue) 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 function public.get_db() return self.db end diff --git a/supervisor/session/rtu/turbinev.lua b/supervisor/session/rtu/turbinev.lua index d30733c..2927fd2 100644 --- a/supervisor/session/rtu/turbinev.lua +++ b/supervisor/session/rtu/turbinev.lua @@ -198,6 +198,8 @@ function turbinev.new(session_id, unit_id, advert, out_queue) self.db.build.max_production = m_pkt.data[14] self.db.build.max_water_output = m_pkt.data[15] self.has_build = true + + out_queue.push_command(unit_session.RTU_US_CMDS.BUILD_CHANGED) else log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -301,6 +303,13 @@ function turbinev.new(session_id, unit_id, advert, out_queue) 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 function public.get_db() return self.db end diff --git a/supervisor/session/rtu/unit_session.lua b/supervisor/session/rtu/unit_session.lua index 3a70f26..245461e 100644 --- a/supervisor/session/rtu/unit_session.lua +++ b/supervisor/session/rtu/unit_session.lua @@ -11,6 +11,16 @@ local PROTOCOLS = comms.PROTOCOLS local MODBUS_FCODE = types.MODBUS_FCODE local MODBUS_EXCODE = types.MODBUS_EXCODE +local RTU_US_CMDS = { + BUILD_CHANGED = 1 +} + +local RTU_US_DATA = { +} + +unit_session.RTU_US_CMDS = RTU_US_CMDS +unit_session.RTU_US_DATA = RTU_US_DATA + -- create a new unit session runner ---@param unit_id integer MODBUS unit ID ---@param advert rtu_advertisement RTU advertisement for this unit @@ -152,6 +162,11 @@ function unit_session.new(unit_id, advert, out_queue, log_tag, txn_tags) log.debug("template unit_session.update() called", true) end + -- invalidate build cache + function public.invalidate_cache() + log.debug("template unit_session.invalidate_cache() called", true) + end + -- get the unit session database function public.get_db() return {} end diff --git a/supervisor/session/unit.lua b/supervisor/session/unit.lua index b8dcc03..2ad30c4 100644 --- a/supervisor/session/unit.lua +++ b/supervisor/session/unit.lua @@ -429,13 +429,13 @@ function unit.new(for_reactor, num_boilers, num_turbines) build.boilers = {} for i = 1, #self.boilers do local boiler = self.boilers[i] ---@type unit_session - build.boilers[boiler.get_device_idx()] = { boiler.get_db().build, boiler.get_db().formed } + build.boilers[boiler.get_device_idx()] = { boiler.get_db().formed, boiler.get_db().build } end build.turbines = {} for i = 1, #self.turbines do local turbine = self.turbines[i] ---@type unit_session - build.turbines[turbine.get_device_idx()] = { turbine.get_db().build, turbine.get_db().formed } + build.turbines[turbine.get_device_idx()] = { turbine.get_db().formed, turbine.get_db().build } end return build @@ -461,14 +461,24 @@ function unit.new(for_reactor, num_boilers, num_turbines) status.boilers = {} for i = 1, #self.boilers do local boiler = self.boilers[i] ---@type unit_session - status.boilers[boiler.get_device_idx()] = { boiler.get_db().state, boiler.get_db().tanks } + status.boilers[boiler.get_device_idx()] = { + boiler.is_faulted(), + boiler.get_db().formed, + boiler.get_db().state, + boiler.get_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.get_db().state, turbine.get_db().tanks } + status.turbines[turbine.get_device_idx()] = { + turbine.is_faulted(), + turbine.get_db().formed, + turbine.get_db().state, + turbine.get_db().tanks + } end ---@todo other RTU statuses diff --git a/supervisor/startup.lua b/supervisor/startup.lua index afe2745..2459673 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.6.8" +local SUPERVISOR_VERSION = "beta-v0.7.0" local print = util.print local println = util.println