#559 supervisor updates to handle busy errors

This commit is contained in:
Mikayla Fischler 2025-01-08 19:07:53 -05:00
parent 1190fe2dd5
commit cd4caf0163
9 changed files with 239 additions and 173 deletions

View File

@ -105,27 +105,39 @@ function boilerv.new(session_id, unit_id, advert, out_queue)
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- query if the multiblock is formed -- query if the multiblock is formed
local function _request_formed() ---@param time_now integer
local function _request_formed(time_now)
-- read discrete input 1 (start = 1, count = 1) -- read discrete input 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) if self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) ~= false then
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
end end
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 12 (start = 1, count = 12) -- read input registers 1 through 12 (start = 1, count = 12)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 12 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 12 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read input registers 13 through 15 (start = 13, count = 3) -- read input registers 13 through 15 (start = 13, count = 3)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 13, 3 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 13, 3 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 16 through 27 (start = 16, count = 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 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 16, 12 }) ~= false then
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -210,26 +222,12 @@ function boilerv.new(session_id, unit_id, advert, out_queue)
-- update this runner -- update this runner
---@param time_now integer milliseconds ---@param time_now integer milliseconds
function public.update(time_now) function public.update(time_now)
if self.periodics.next_formed_req <= time_now then if self.periodics.next_formed_req <= time_now then _request_formed(time_now) end
_request_formed()
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
if self.db.formed then if self.db.formed then
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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 end
self.session.post_update() self.session.post_update()

View File

@ -42,6 +42,8 @@ local PERIODICS = {
TANKS = 500 TANKS = 500
} }
local WRITE_BUSY_WAIT = 1000
-- create a new dynamicv rtu session runner -- create a new dynamicv rtu session runner
---@nodiscard ---@nodiscard
---@param session_id integer RTU gateway session ID ---@param session_id integer RTU gateway session ID
@ -63,6 +65,8 @@ function dynamicv.new(session_id, unit_id, advert, out_queue)
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),
has_build = false, has_build = false,
mode_cmd = nil, ---@type container_mode|nil
resend_mode = false,
periodics = { periodics = {
next_formed_req = 0, next_formed_req = 0,
next_build_req = 0, next_build_req = 0,
@ -101,45 +105,77 @@ function dynamicv.new(session_id, unit_id, advert, out_queue)
-- increment the container mode -- increment the container mode
local function _inc_cont_mode() local function _inc_cont_mode()
-- set mode command
if self.mode_cmd == "BOTH" then self.mode_cmd = "FILL"
elseif self.mode_cmd == "FILL" then self.mode_cmd = "EMPTY"
elseif self.mode_cmd == "EMPTY" then self.mode_cmd = "BOTH"
end
-- write coil 1 with unused value 0 -- write coil 1 with unused value 0
self.session.send_request(TXN_TYPES.INC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 1, 0 }) if self.session.send_request(TXN_TYPES.INC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 1, 0 }, WRITE_BUSY_WAIT) == false then
self.resend_mode = true
end
end end
-- decrement the container mode -- decrement the container mode
local function _dec_cont_mode() local function _dec_cont_mode()
-- set mode command
if self.mode_cmd == "BOTH" then self.mode_cmd = "EMPTY"
elseif self.mode_cmd == "EMPTY" then self.mode_cmd = "FILL"
elseif self.mode_cmd == "FILL" then self.mode_cmd = "BOTH"
end
-- write coil 2 with unused value 0 -- write coil 2 with unused value 0
self.session.send_request(TXN_TYPES.DEC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 2, 0 }) if self.session.send_request(TXN_TYPES.DEC_CONT, MODBUS_FCODE.WRITE_SINGLE_COIL, { 2, 0 , WRITE_BUSY_WAIT}) == false then
self.resend_mode = false
end
end end
-- set the container mode -- set the container mode
---@param mode container_mode ---@param mode container_mode
local function _set_cont_mode(mode) local function _set_cont_mode(mode)
self.mode_cmd = mode
-- write holding register 1 -- write holding register 1
self.session.send_request(TXN_TYPES.SET_CONT, MODBUS_FCODE.WRITE_SINGLE_HOLD_REG, { 1, mode }) if self.session.send_request(TXN_TYPES.SET_CONT, MODBUS_FCODE.WRITE_SINGLE_HOLD_REG, { 1, mode }, WRITE_BUSY_WAIT) == false then
self.resend_mode = false
end
end end
-- query if the multiblock is formed -- query if the multiblock is formed
local function _request_formed() ---@param time_now integer
local function _request_formed(time_now)
-- read discrete input 1 (start = 1, count = 1) -- read discrete input 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) if self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) ~= false then
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
end end
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 7 (start = 1, count = 7) -- read input registers 1 through 7 (start = 1, count = 7)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 7 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 7 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read holding register 1 (start = 1, count = 1) -- read holding register 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_MUL_HOLD_REGS, { 1, 1 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_MUL_HOLD_REGS, { 1, 1 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 8 through 9 (start = 8, count = 2) -- read input registers 8 through 9 (start = 8, count = 2)
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 8, 2 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 8, 2 }) ~= false then
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -182,6 +218,10 @@ function dynamicv.new(session_id, unit_id, advert, out_queue)
if m_pkt.length == 1 then if m_pkt.length == 1 then
self.db.state.last_update = util.time_ms() self.db.state.last_update = util.time_ms()
self.db.state.container_mode = m_pkt.data[1] self.db.state.container_mode = m_pkt.data[1]
if self.mode_cmd == nil then
self.mode_cmd = self.db.state.container_mode
end
else else
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
end end
@ -247,30 +287,22 @@ function dynamicv.new(session_id, unit_id, advert, out_queue)
end end
end end
-- try to resend mode if needed
if self.resend_mode then
self.resend_mode = false
_set_cont_mode(self.mode_cmd)
end
time_now = util.time() time_now = util.time()
-- handle periodics -- handle periodics
if self.periodics.next_formed_req <= time_now then if self.periodics.next_formed_req <= time_now then _request_formed(time_now) end
_request_formed()
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
if self.db.formed then if self.db.formed then
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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 end
self.session.post_update() self.session.post_update()

View File

@ -58,9 +58,12 @@ function envd.new(session_id, unit_id, advert, out_queue)
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- query the radiation readings of the device -- query the radiation readings of the device
local function _request_radiation() ---@param time_now integer
local function _request_radiation(time_now)
-- read input registers 1 and 2 (start = 1, count = 2) -- read input registers 1 and 2 (start = 1, count = 2)
self.session.send_request(TXN_TYPES.RAD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 2 }) if self.session.send_request(TXN_TYPES.RAD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 2 }) ~= false then
self.periodics.next_rad_req = time_now + PERIODICS.RAD
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -90,10 +93,7 @@ function envd.new(session_id, unit_id, advert, out_queue)
-- update this runner -- update this runner
---@param time_now integer milliseconds ---@param time_now integer milliseconds
function public.update(time_now) function public.update(time_now)
if self.periodics.next_rad_req <= time_now then if self.periodics.next_rad_req <= time_now then _request_radiation(time_now) end
_request_radiation()
self.periodics.next_rad_req = time_now + PERIODICS.RAD
end
self.session.post_update() self.session.post_update()
end end

View File

@ -89,27 +89,39 @@ function imatrix.new(session_id, unit_id, advert, out_queue)
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- query if the multiblock is formed -- query if the multiblock is formed
local function _request_formed() ---@param time_now integer
local function _request_formed(time_now)
-- read discrete input 1 (start = 1, count = 1) -- read discrete input 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) if self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) ~= false then
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
end end
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 9 (start = 1, count = 9) -- read input registers 1 through 9 (start = 1, count = 9)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 9 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 9 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read input register 10 through 11 (start = 10, count = 2) -- read input register 10 through 11 (start = 10, count = 2)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 10, 2 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 10, 2 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 12 through 15 (start = 12, count = 3) -- read input registers 12 through 15 (start = 12, count = 3)
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 12, 3 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 12, 3 }) ~= false then
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -181,26 +193,12 @@ function imatrix.new(session_id, unit_id, advert, out_queue)
-- update this runner -- update this runner
---@param time_now integer milliseconds ---@param time_now integer milliseconds
function public.update(time_now) function public.update(time_now)
if self.periodics.next_formed_req <= time_now then if self.periodics.next_formed_req <= time_now then _request_formed(time_now) end
_request_formed()
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
if self.db.formed then if self.db.formed then
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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 end
self.session.post_update() self.session.post_update()

View File

@ -80,21 +80,30 @@ function sna.new(session_id, unit_id, advert, out_queue)
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 2 (start = 1, count = 2) -- read input registers 1 through 2 (start = 1, count = 2)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 2 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 2 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read input registers 3 through 4 (start = 3, count = 2) -- read input registers 3 through 4 (start = 3, count = 2)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 3, 2 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 3, 2 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 5 through 10 (start = 5, count = 6) -- read input registers 5 through 10 (start = 5, count = 6)
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 5, 6 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 5, 6 }) ~= false then
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -152,20 +161,9 @@ function sna.new(session_id, unit_id, advert, out_queue)
-- update this runner -- update this runner
---@param time_now integer milliseconds ---@param time_now integer milliseconds
function public.update(time_now) function public.update(time_now)
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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
self.session.post_update() self.session.post_update()
end end

View File

@ -94,27 +94,39 @@ function sps.new(session_id, unit_id, advert, out_queue)
-- PRIVATE FUNCTIONS -- -- PRIVATE FUNCTIONS --
-- query if the multiblock is formed -- query if the multiblock is formed
local function _request_formed() ---@param time_now integer
local function _request_formed(time_now)
-- read discrete input 1 (start = 1, count = 1) -- read discrete input 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) if self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) ~= false then
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
end end
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 9 (start = 1, count = 9) -- read input registers 1 through 9 (start = 1, count = 9)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 9 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 9 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read input register 10 (start = 10, count = 1) -- read input register 10 (start = 10, count = 1)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 10, 1 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 10, 1 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 11 through 19 (start = 11, count = 9) -- read input registers 11 through 19 (start = 11, count = 9)
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 11, 9 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 11, 9 }) ~= false then
self.periodics.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -191,26 +203,12 @@ function sps.new(session_id, unit_id, advert, out_queue)
-- update this runner -- update this runner
---@param time_now integer milliseconds ---@param time_now integer milliseconds
function public.update(time_now) function public.update(time_now)
if self.periodics.next_formed_req <= time_now then if self.periodics.next_formed_req <= time_now then _request_formed(time_now) end
_request_formed()
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
if self.db.formed then if self.db.formed then
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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 end
self.session.post_update() self.session.post_update()

View File

@ -42,6 +42,8 @@ local PERIODICS = {
TANKS = 1000 TANKS = 1000
} }
local WRITE_BUSY_WAIT = 1000
-- create a new turbinev rtu session runner -- create a new turbinev rtu session runner
---@nodiscard ---@nodiscard
---@param session_id integer RTU gateway session ID ---@param session_id integer RTU gateway session ID
@ -63,6 +65,8 @@ function turbinev.new(session_id, unit_id, advert, out_queue)
local self = { local self = {
session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS),
has_build = false, has_build = false,
mode_cmd = nil, ---@type dumping_mode|nil
resend_mode = false,
periodics = { periodics = {
next_formed_req = 0, next_formed_req = 0,
next_build_req = 0, next_build_req = 0,
@ -116,45 +120,77 @@ function turbinev.new(session_id, unit_id, advert, out_queue)
-- increment the dumping mode -- increment the dumping mode
local function _inc_dump_mode() local function _inc_dump_mode()
-- set mode command
if self.mode_cmd == "IDLE" then self.mode_cmd = "DUMPING_EXCESS"
elseif self.mode_cmd == "DUMPING_EXCESS" then self.mode_cmd = "DUMPING"
elseif self.mode_cmd == "DUMPING" then self.mode_cmd = "IDLE"
end
-- write coil 1 with unused value 0 -- write coil 1 with unused value 0
self.session.send_request(TXN_TYPES.INC_DUMP, MODBUS_FCODE.WRITE_SINGLE_COIL, { 1, 0 }) if self.session.send_request(TXN_TYPES.INC_DUMP, MODBUS_FCODE.WRITE_SINGLE_COIL, { 1, 0 }, WRITE_BUSY_WAIT) == false then
self.resend_mode = true
end
end end
-- decrement the dumping mode -- decrement the dumping mode
local function _dec_dump_mode() local function _dec_dump_mode()
-- set mode command
if self.mode_cmd == "IDLE" then self.mode_cmd = "DUMPING"
elseif self.mode_cmd == "DUMPING_EXCESS" then self.mode_cmd = "IDLE"
elseif self.mode_cmd == "DUMPING" then self.mode_cmd = "DUMPING_EXCESS"
end
-- write coil 2 with unused value 0 -- write coil 2 with unused value 0
self.session.send_request(TXN_TYPES.DEC_DUMP, MODBUS_FCODE.WRITE_SINGLE_COIL, { 2, 0 }) if self.session.send_request(TXN_TYPES.DEC_DUMP, MODBUS_FCODE.WRITE_SINGLE_COIL, { 2, 0 }, WRITE_BUSY_WAIT) == false then
self.resend_mode = true
end
end end
-- set the dumping mode -- set the dumping mode
---@param mode dumping_mode ---@param mode dumping_mode
local function _set_dump_mode(mode) local function _set_dump_mode(mode)
self.mode_cmd = mode
-- write holding register 1 -- write holding register 1
self.session.send_request(TXN_TYPES.SET_DUMP, MODBUS_FCODE.WRITE_SINGLE_HOLD_REG, { 1, mode }) if self.session.send_request(TXN_TYPES.SET_DUMP, MODBUS_FCODE.WRITE_SINGLE_HOLD_REG, { 1, mode }, WRITE_BUSY_WAIT) == false then
self.resend_mode = true
end
end end
-- query if the multiblock is formed -- query if the multiblock is formed
local function _request_formed() ---@param time_now integer
local function _request_formed(time_now)
-- read discrete input 1 (start = 1, count = 1) -- read discrete input 1 (start = 1, count = 1)
self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) if self.session.send_request(TXN_TYPES.FORMED, MODBUS_FCODE.READ_DISCRETE_INPUTS, { 1, 1 }) ~= false then
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
end end
-- query the build of the device -- query the build of the device
local function _request_build() ---@param time_now integer
local function _request_build(time_now)
-- read input registers 1 through 15 (start = 1, count = 15) -- read input registers 1 through 15 (start = 1, count = 15)
self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 15 }) if self.session.send_request(TXN_TYPES.BUILD, MODBUS_FCODE.READ_INPUT_REGS, { 1, 15 }) ~= false then
self.periodics.next_build_req = time_now + PERIODICS.BUILD
end
end end
-- query the state of the device -- query the state of the device
local function _request_state() ---@param time_now integer
local function _request_state(time_now)
-- read input registers 16 through 19 (start = 16, count = 4) -- read input registers 16 through 19 (start = 16, count = 4)
self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 16, 4 }) if self.session.send_request(TXN_TYPES.STATE, MODBUS_FCODE.READ_INPUT_REGS, { 16, 4 }) ~= false then
self.periodics.next_state_req = time_now + PERIODICS.STATE
end
end end
-- query the tanks of the device -- query the tanks of the device
local function _request_tanks() ---@param time_now integer
local function _request_tanks(time_now)
-- read input registers 20 through 25 (start = 20, count = 6) -- read input registers 20 through 25 (start = 20, count = 6)
self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 20, 6 }) if self.session.send_request(TXN_TYPES.TANKS, MODBUS_FCODE.READ_INPUT_REGS, { 20, 6 }) ~= false then
self.periodic.next_tanks_req = time_now + PERIODICS.TANKS
end
end end
-- PUBLIC FUNCTIONS -- -- PUBLIC FUNCTIONS --
@ -208,6 +244,10 @@ function turbinev.new(session_id, unit_id, advert, out_queue)
self.db.state.prod_rate = m_pkt.data[2] self.db.state.prod_rate = m_pkt.data[2]
self.db.state.steam_input_rate = m_pkt.data[3] self.db.state.steam_input_rate = m_pkt.data[3]
self.db.state.dumping_mode = m_pkt.data[4] self.db.state.dumping_mode = m_pkt.data[4]
if self.mode_cmd == nil then
self.mode_cmd = self.db.state.dumping_mode
end
else else
log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")")
end end
@ -277,30 +317,22 @@ function turbinev.new(session_id, unit_id, advert, out_queue)
end end
end end
-- try to resend mode if needed
if self.resend_mode then
self.resend_mode = false
_set_dump_mode(self.mode_cmd)
end
time_now = util.time() time_now = util.time()
-- handle periodics -- handle periodics
if self.periodics.next_formed_req <= time_now then if self.periodics.next_formed_req <= time_now then _request_formed(time_now) end
_request_formed()
self.periodics.next_formed_req = time_now + PERIODICS.FORMED
end
if self.db.formed then if self.db.formed then
if not self.has_build and self.periodics.next_build_req <= time_now then if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end
_request_build() if self.periodics.next_state_req <= time_now then _request_state(time_now) end
self.periodics.next_build_req = time_now + PERIODICS.BUILD if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end
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 end
self.session.post_update() self.session.post_update()

View File

@ -22,6 +22,8 @@ local RTU_US_DATA = {
unit_session.RTU_US_CMDS = RTU_US_CMDS unit_session.RTU_US_CMDS = RTU_US_CMDS
unit_session.RTU_US_DATA = RTU_US_DATA unit_session.RTU_US_DATA = RTU_US_DATA
local DEFAULT_BUSY_WAIT = 3000
-- create a new unit session runner -- create a new unit session runner
---@nodiscard ---@nodiscard
---@param session_id integer RTU gateway session ID ---@param session_id integer RTU gateway session ID
@ -36,7 +38,8 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
reactor = advert.reactor, reactor = advert.reactor,
transaction_controller = txnctrl.new(), transaction_controller = txnctrl.new(),
connected = true, connected = true,
device_fail = false device_fail = false,
last_busy = 0
} }
---@class _unit_session ---@class _unit_session
@ -53,14 +56,21 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
---@param txn_type integer transaction type ---@param txn_type integer transaction type
---@param f_code MODBUS_FCODE function code ---@param f_code MODBUS_FCODE function code
---@param register_param (number|string)[] register range or register and values ---@param register_param (number|string)[] register range or register and values
---@return integer txn_id transaction ID of this transaction ---@param busy_wait integer|nil milliseconds to wait (>0), or uses the default
function protected.send_request(txn_type, f_code, register_param) ---@return integer|false txn_id transaction ID of this transaction or false if not sent due to being busy
local m_pkt = comms.modbus_packet() function protected.send_request(txn_type, f_code, register_param, busy_wait)
local txn_id = self.transaction_controller.create(txn_type) local txn_id = false ---@type integer|false
m_pkt.make(txn_id, unit_id, f_code, register_param) busy_wait = busy_wait or DEFAULT_BUSY_WAIT
out_queue.push_packet(m_pkt) if (util.time_ms() - self.last_busy) >= busy_wait then
local m_pkt = comms.modbus_packet()
txn_id = self.transaction_controller.create(txn_type)
m_pkt.make(txn_id, unit_id, f_code, register_param)
out_queue.push_packet(m_pkt)
end
return txn_id return txn_id
end end
@ -99,9 +109,9 @@ function unit_session.new(session_id, unit_id, advert, out_queue, log_tag, txn_t
-- will have to wait on reply, renew the transaction -- will have to wait on reply, renew the transaction
self.transaction_controller.renew(m_pkt.txn_id, txn_type) self.transaction_controller.renew(m_pkt.txn_id, txn_type)
elseif ex == MODBUS_EXCODE.SERVER_DEVICE_BUSY then elseif ex == MODBUS_EXCODE.SERVER_DEVICE_BUSY then
-- will have to wait on reply, renew the transaction -- will have to try again later
self.transaction_controller.renew(m_pkt.txn_id, txn_type) self.last_busy = util.time_ms()
log.debug(log_tag .. "MODBUS: device busy" .. txn_tag) log.warning(log_tag .. "MODBUS: device busy" .. txn_tag)
elseif ex == MODBUS_EXCODE.NEG_ACKNOWLEDGE then elseif ex == MODBUS_EXCODE.NEG_ACKNOWLEDGE then
-- general failure -- general failure
log.error(log_tag .. "MODBUS: negative acknowledge (bad request)" .. txn_tag) log.error(log_tag .. "MODBUS: negative acknowledge (bad request)" .. txn_tag)

View File

@ -22,7 +22,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.6.1" local SUPERVISOR_VERSION = "v1.6.2"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts