From cd4caf0163ab8dc20bbdfaefcbeb32633faf3c47 Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 8 Jan 2025 19:07:53 -0500 Subject: [PATCH] #559 supervisor updates to handle busy errors --- supervisor/session/rtu/boilerv.lua | 50 +++++++------- supervisor/session/rtu/dynamicv.lua | 90 +++++++++++++++++-------- supervisor/session/rtu/envd.lua | 12 ++-- supervisor/session/rtu/imatrix.lua | 50 +++++++------- supervisor/session/rtu/sna.lua | 38 +++++------ supervisor/session/rtu/sps.lua | 50 +++++++------- supervisor/session/rtu/turbinev.lua | 90 +++++++++++++++++-------- supervisor/session/rtu/unit_session.lua | 30 ++++++--- supervisor/startup.lua | 2 +- 9 files changed, 239 insertions(+), 173 deletions(-) diff --git a/supervisor/session/rtu/boilerv.lua b/supervisor/session/rtu/boilerv.lua index a1a1a99..11b5e39 100644 --- a/supervisor/session/rtu/boilerv.lua +++ b/supervisor/session/rtu/boilerv.lua @@ -105,27 +105,39 @@ function boilerv.new(session_id, unit_id, advert, out_queue) -- PRIVATE FUNCTIONS -- -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -210,26 +222,12 @@ function boilerv.new(session_id, unit_id, advert, out_queue) -- 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.periodics.next_formed_req <= time_now then _request_formed(time_now) 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end end self.session.post_update() diff --git a/supervisor/session/rtu/dynamicv.lua b/supervisor/session/rtu/dynamicv.lua index 0c06d7b..16ba84b 100644 --- a/supervisor/session/rtu/dynamicv.lua +++ b/supervisor/session/rtu/dynamicv.lua @@ -42,6 +42,8 @@ local PERIODICS = { TANKS = 500 } +local WRITE_BUSY_WAIT = 1000 + -- create a new dynamicv rtu session runner ---@nodiscard ---@param session_id integer RTU gateway session ID @@ -63,6 +65,8 @@ function dynamicv.new(session_id, unit_id, advert, out_queue) local self = { session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), has_build = false, + mode_cmd = nil, ---@type container_mode|nil + resend_mode = false, periodics = { next_formed_req = 0, next_build_req = 0, @@ -101,45 +105,77 @@ function dynamicv.new(session_id, unit_id, advert, out_queue) -- increment the container 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 - 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 -- decrement the container 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 - 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 -- set the container mode ---@param mode container_mode local function _set_cont_mode(mode) + self.mode_cmd = mode + -- 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -182,6 +218,10 @@ function dynamicv.new(session_id, unit_id, advert, out_queue) if m_pkt.length == 1 then self.db.state.last_update = util.time_ms() 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 log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -247,30 +287,22 @@ function dynamicv.new(session_id, unit_id, advert, out_queue) 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() -- handle periodics - if self.periodics.next_formed_req <= time_now then - _request_formed() - self.periodics.next_formed_req = time_now + PERIODICS.FORMED - end + if self.periodics.next_formed_req <= time_now then _request_formed(time_now) 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end end self.session.post_update() diff --git a/supervisor/session/rtu/envd.lua b/supervisor/session/rtu/envd.lua index 269975a..ef36fad 100644 --- a/supervisor/session/rtu/envd.lua +++ b/supervisor/session/rtu/envd.lua @@ -58,9 +58,12 @@ function envd.new(session_id, unit_id, advert, out_queue) -- PRIVATE FUNCTIONS -- -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -90,10 +93,7 @@ function envd.new(session_id, unit_id, advert, out_queue) -- update this runner ---@param time_now integer milliseconds function public.update(time_now) - if self.periodics.next_rad_req <= time_now then - _request_radiation() - self.periodics.next_rad_req = time_now + PERIODICS.RAD - end + if self.periodics.next_rad_req <= time_now then _request_radiation(time_now) end self.session.post_update() end diff --git a/supervisor/session/rtu/imatrix.lua b/supervisor/session/rtu/imatrix.lua index aa7a984..5fea7aa 100644 --- a/supervisor/session/rtu/imatrix.lua +++ b/supervisor/session/rtu/imatrix.lua @@ -89,27 +89,39 @@ function imatrix.new(session_id, unit_id, advert, out_queue) -- PRIVATE FUNCTIONS -- -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -181,26 +193,12 @@ function imatrix.new(session_id, unit_id, advert, out_queue) -- 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.periodics.next_formed_req <= time_now then _request_formed(time_now) 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end end self.session.post_update() diff --git a/supervisor/session/rtu/sna.lua b/supervisor/session/rtu/sna.lua index 7e0fc34..f46fa5c 100644 --- a/supervisor/session/rtu/sna.lua +++ b/supervisor/session/rtu/sna.lua @@ -80,21 +80,30 @@ function sna.new(session_id, unit_id, advert, out_queue) -- PRIVATE FUNCTIONS -- -- 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) - 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 -- 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) - 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 -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -152,20 +161,9 @@ function sna.new(session_id, unit_id, advert, out_queue) -- update this runner ---@param time_now integer milliseconds function public.update(time_now) - 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end self.session.post_update() end diff --git a/supervisor/session/rtu/sps.lua b/supervisor/session/rtu/sps.lua index 1dacd61..2096821 100644 --- a/supervisor/session/rtu/sps.lua +++ b/supervisor/session/rtu/sps.lua @@ -94,27 +94,39 @@ function sps.new(session_id, unit_id, advert, out_queue) -- PRIVATE FUNCTIONS -- -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- PUBLIC FUNCTIONS -- @@ -191,26 +203,12 @@ function sps.new(session_id, unit_id, advert, out_queue) -- 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.periodics.next_formed_req <= time_now then _request_formed(time_now) 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end end self.session.post_update() diff --git a/supervisor/session/rtu/turbinev.lua b/supervisor/session/rtu/turbinev.lua index 3581884..7023021 100644 --- a/supervisor/session/rtu/turbinev.lua +++ b/supervisor/session/rtu/turbinev.lua @@ -42,6 +42,8 @@ local PERIODICS = { TANKS = 1000 } +local WRITE_BUSY_WAIT = 1000 + -- create a new turbinev rtu session runner ---@nodiscard ---@param session_id integer RTU gateway session ID @@ -63,6 +65,8 @@ function turbinev.new(session_id, unit_id, advert, out_queue) local self = { session = unit_session.new(session_id, unit_id, advert, out_queue, log_tag, TXN_TAGS), has_build = false, + mode_cmd = nil, ---@type dumping_mode|nil + resend_mode = false, periodics = { next_formed_req = 0, next_build_req = 0, @@ -116,45 +120,77 @@ function turbinev.new(session_id, unit_id, advert, out_queue) -- increment the dumping 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 - 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 -- decrement the dumping 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 - 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 -- set the dumping mode ---@param mode dumping_mode local function _set_dump_mode(mode) + self.mode_cmd = mode + -- 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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) - 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 -- 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.steam_input_rate = m_pkt.data[3] 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 log.debug(log_tag .. "MODBUS transaction reply length mismatch (" .. TXN_TAGS[txn_type] .. ")") end @@ -277,30 +317,22 @@ function turbinev.new(session_id, unit_id, advert, out_queue) 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() -- handle periodics - if self.periodics.next_formed_req <= time_now then - _request_formed() - self.periodics.next_formed_req = time_now + PERIODICS.FORMED - end + if self.periodics.next_formed_req <= time_now then _request_formed(time_now) 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 + if not self.has_build and self.periodics.next_build_req <= time_now then _request_build(time_now) end + if self.periodics.next_state_req <= time_now then _request_state(time_now) end + if self.periodics.next_tanks_req <= time_now then _request_tanks(time_now) end end self.session.post_update() diff --git a/supervisor/session/rtu/unit_session.lua b/supervisor/session/rtu/unit_session.lua index 632890b..8c51ccb 100644 --- a/supervisor/session/rtu/unit_session.lua +++ b/supervisor/session/rtu/unit_session.lua @@ -22,6 +22,8 @@ local RTU_US_DATA = { unit_session.RTU_US_CMDS = RTU_US_CMDS unit_session.RTU_US_DATA = RTU_US_DATA +local DEFAULT_BUSY_WAIT = 3000 + -- create a new unit session runner ---@nodiscard ---@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, transaction_controller = txnctrl.new(), connected = true, - device_fail = false + device_fail = false, + last_busy = 0 } ---@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 f_code MODBUS_FCODE function code ---@param register_param (number|string)[] register range or register and values - ---@return integer txn_id transaction ID of this transaction - function protected.send_request(txn_type, f_code, register_param) - local m_pkt = comms.modbus_packet() - local txn_id = self.transaction_controller.create(txn_type) + ---@param busy_wait integer|nil milliseconds to wait (>0), or uses the default + ---@return integer|false txn_id transaction ID of this transaction or false if not sent due to being busy + function protected.send_request(txn_type, f_code, register_param, busy_wait) + 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 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 self.transaction_controller.renew(m_pkt.txn_id, txn_type) elseif ex == MODBUS_EXCODE.SERVER_DEVICE_BUSY then - -- will have to wait on reply, renew the transaction - self.transaction_controller.renew(m_pkt.txn_id, txn_type) - log.debug(log_tag .. "MODBUS: device busy" .. txn_tag) + -- will have to try again later + self.last_busy = util.time_ms() + log.warning(log_tag .. "MODBUS: device busy" .. txn_tag) elseif ex == MODBUS_EXCODE.NEG_ACKNOWLEDGE then -- general failure log.error(log_tag .. "MODBUS: negative acknowledge (bad request)" .. txn_tag) diff --git a/supervisor/startup.lua b/supervisor/startup.lua index 870792e..32651da 100644 --- a/supervisor/startup.lua +++ b/supervisor/startup.lua @@ -22,7 +22,7 @@ local supervisor = require("supervisor.supervisor") local svsessions = require("supervisor.session.svsessions") -local SUPERVISOR_VERSION = "v1.6.1" +local SUPERVISOR_VERSION = "v1.6.2" local println = util.println local println_ts = util.println_ts