mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#118 RTU/PLC code cleanup
This commit is contained in:
parent
ce0198f389
commit
79494f0587
@ -58,7 +58,6 @@ function plc.rps_init(reactor, is_formed)
|
||||
}
|
||||
|
||||
local self = {
|
||||
reactor = reactor,
|
||||
state = { false, false, false, false, false, false, false, false, false, false, false, false },
|
||||
reactor_enabled = false,
|
||||
enabled_at = 0,
|
||||
@ -68,14 +67,11 @@ function plc.rps_init(reactor, is_formed)
|
||||
trip_cause = "ok" ---@type rps_trip_cause
|
||||
}
|
||||
|
||||
---@class rps
|
||||
local public = {}
|
||||
|
||||
-- PRIVATE FUNCTIONS --
|
||||
|
||||
-- set reactor access fault flag
|
||||
local function _set_fault()
|
||||
if self.reactor.__p_last_fault() ~= "Terminated" then
|
||||
if reactor.__p_last_fault() ~= "Terminated" then
|
||||
self.state[state_keys.fault] = true
|
||||
end
|
||||
end
|
||||
@ -87,7 +83,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check if the reactor is formed
|
||||
local function _is_formed()
|
||||
local formed = self.reactor.isFormed()
|
||||
local formed = reactor.isFormed()
|
||||
if formed == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -102,7 +98,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check if the reactor is force disabled
|
||||
local function _is_force_disabled()
|
||||
local disabled = self.reactor.isForceDisabled()
|
||||
local disabled = reactor.isForceDisabled()
|
||||
if disabled == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -117,7 +113,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check for critical damage
|
||||
local function _damage_critical()
|
||||
local damage_percent = self.reactor.getDamagePercent()
|
||||
local damage_percent = reactor.getDamagePercent()
|
||||
if damage_percent == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -129,7 +125,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
-- check if the reactor is at a critically high temperature
|
||||
local function _high_temp()
|
||||
-- mekanism: MAX_DAMAGE_TEMPERATURE = 1_200
|
||||
local temp = self.reactor.getTemperature()
|
||||
local temp = reactor.getTemperature()
|
||||
if temp == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -140,7 +136,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check if there is no coolant (<2% filled)
|
||||
local function _no_coolant()
|
||||
local coolant_filled = self.reactor.getCoolantFilledPercentage()
|
||||
local coolant_filled = reactor.getCoolantFilledPercentage()
|
||||
if coolant_filled == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -151,7 +147,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check for excess waste (>80% filled)
|
||||
local function _excess_waste()
|
||||
local w_filled = self.reactor.getWasteFilledPercentage()
|
||||
local w_filled = reactor.getWasteFilledPercentage()
|
||||
if w_filled == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -162,7 +158,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check for heated coolant backup (>95% filled)
|
||||
local function _excess_heated_coolant()
|
||||
local hc_filled = self.reactor.getHeatedCoolantFilledPercentage()
|
||||
local hc_filled = reactor.getHeatedCoolantFilledPercentage()
|
||||
if hc_filled == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -173,7 +169,7 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- check if there is no fuel
|
||||
local function _insufficient_fuel()
|
||||
local fuel = self.reactor.getFuel()
|
||||
local fuel = reactor.getFuel()
|
||||
if fuel == ppm.ACCESS_FAULT then
|
||||
-- lost the peripheral or terminated, handled later
|
||||
_set_fault()
|
||||
@ -184,10 +180,13 @@ function plc.rps_init(reactor, is_formed)
|
||||
|
||||
-- PUBLIC FUNCTIONS --
|
||||
|
||||
---@class rps
|
||||
local public = {}
|
||||
|
||||
-- re-link a reactor after a peripheral re-connect
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
function public.reconnect_reactor(reactor)
|
||||
self.reactor = reactor
|
||||
---@param new_reactor table reconnected reactor
|
||||
function public.reconnect_reactor(new_reactor)
|
||||
reactor = new_reactor
|
||||
end
|
||||
|
||||
-- trip for lost peripheral
|
||||
@ -221,8 +220,8 @@ function plc.rps_init(reactor, is_formed)
|
||||
function public.scram()
|
||||
log.info("RPS: reactor SCRAM")
|
||||
|
||||
self.reactor.scram()
|
||||
if self.reactor.__p_is_faulted() and (self.reactor.__p_last_fault() ~= PCALL_SCRAM_MSG) then
|
||||
reactor.scram()
|
||||
if reactor.__p_is_faulted() and (reactor.__p_last_fault() ~= PCALL_SCRAM_MSG) then
|
||||
log.error("RPS: failed reactor SCRAM")
|
||||
return false
|
||||
else
|
||||
@ -238,8 +237,8 @@ function plc.rps_init(reactor, is_formed)
|
||||
if not self.tripped then
|
||||
log.info("RPS: reactor start")
|
||||
|
||||
self.reactor.activate()
|
||||
if self.reactor.__p_is_faulted() and (self.reactor.__p_last_fault() ~= PCALL_START_MSG) then
|
||||
reactor.activate()
|
||||
if reactor.__p_is_faulted() and (reactor.__p_last_fault() ~= PCALL_START_MSG) then
|
||||
log.error("RPS: failed reactor start")
|
||||
else
|
||||
self.reactor_enabled = true
|
||||
@ -423,8 +422,6 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
local self = {
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
modem = modem,
|
||||
reactor = reactor,
|
||||
scrammed = false,
|
||||
linked = false,
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW,
|
||||
@ -440,8 +437,8 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
|
||||
-- configure modem channels
|
||||
local function _conf_channels()
|
||||
self.modem.closeAll()
|
||||
self.modem.open(local_port)
|
||||
modem.closeAll()
|
||||
modem.open(local_port)
|
||||
end
|
||||
|
||||
_conf_channels()
|
||||
@ -456,7 +453,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
r_pkt.make(id, msg_type, msg)
|
||||
s_pkt.make(self.seq_num, PROTOCOL.RPLC, r_pkt.raw_sendable())
|
||||
|
||||
self.modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
self.seq_num = self.seq_num + 1
|
||||
end
|
||||
|
||||
@ -470,7 +467,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
m_pkt.make(msg_type, msg)
|
||||
s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
||||
|
||||
self.modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
self.seq_num = self.seq_num + 1
|
||||
end
|
||||
|
||||
@ -503,21 +500,21 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
}
|
||||
|
||||
local tasks = {
|
||||
function () data_table[1] = self.reactor.getStatus() end,
|
||||
function () data_table[2] = self.reactor.getBurnRate() end,
|
||||
function () data_table[3] = self.reactor.getActualBurnRate() end,
|
||||
function () data_table[4] = self.reactor.getTemperature() end,
|
||||
function () data_table[5] = self.reactor.getDamagePercent() end,
|
||||
function () data_table[6] = self.reactor.getBoilEfficiency() end,
|
||||
function () data_table[7] = self.reactor.getEnvironmentalLoss() end,
|
||||
function () fuel = self.reactor.getFuel() end,
|
||||
function () data_table[9] = self.reactor.getFuelFilledPercentage() end,
|
||||
function () waste = self.reactor.getWaste() end,
|
||||
function () data_table[11] = self.reactor.getWasteFilledPercentage() end,
|
||||
function () coolant = self.reactor.getCoolant() end,
|
||||
function () data_table[14] = self.reactor.getCoolantFilledPercentage() end,
|
||||
function () hcoolant = self.reactor.getHeatedCoolant() end,
|
||||
function () data_table[17] = self.reactor.getHeatedCoolantFilledPercentage() end
|
||||
function () data_table[1] = reactor.getStatus() end,
|
||||
function () data_table[2] = reactor.getBurnRate() end,
|
||||
function () data_table[3] = reactor.getActualBurnRate() end,
|
||||
function () data_table[4] = reactor.getTemperature() end,
|
||||
function () data_table[5] = reactor.getDamagePercent() end,
|
||||
function () data_table[6] = reactor.getBoilEfficiency() end,
|
||||
function () data_table[7] = reactor.getEnvironmentalLoss() end,
|
||||
function () fuel = reactor.getFuel() end,
|
||||
function () data_table[9] = reactor.getFuelFilledPercentage() end,
|
||||
function () waste = reactor.getWaste() end,
|
||||
function () data_table[11] = reactor.getWasteFilledPercentage() end,
|
||||
function () coolant = reactor.getCoolant() end,
|
||||
function () data_table[14] = reactor.getCoolantFilledPercentage() end,
|
||||
function () hcoolant = reactor.getHeatedCoolant() end,
|
||||
function () data_table[17] = reactor.getHeatedCoolantFilledPercentage() end
|
||||
}
|
||||
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
@ -540,7 +537,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
data_table[16] = hcoolant.amount
|
||||
end
|
||||
|
||||
return data_table, self.reactor.__p_is_faulted()
|
||||
return data_table, reactor.__p_is_faulted()
|
||||
end
|
||||
|
||||
-- update the status cache if changed
|
||||
@ -590,24 +587,24 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
local mek_data = { false, 0, 0, 0, min_pos, max_pos, 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||
|
||||
local tasks = {
|
||||
function () mek_data[1] = self.reactor.getLength() end,
|
||||
function () mek_data[2] = self.reactor.getWidth() end,
|
||||
function () mek_data[3] = self.reactor.getHeight() end,
|
||||
function () mek_data[4] = self.reactor.getMinPos() end,
|
||||
function () mek_data[5] = self.reactor.getMaxPos() end,
|
||||
function () mek_data[6] = self.reactor.getHeatCapacity() end,
|
||||
function () mek_data[7] = self.reactor.getFuelAssemblies() end,
|
||||
function () mek_data[8] = self.reactor.getFuelSurfaceArea() end,
|
||||
function () mek_data[9] = self.reactor.getFuelCapacity() end,
|
||||
function () mek_data[10] = self.reactor.getWasteCapacity() end,
|
||||
function () mek_data[11] = self.reactor.getCoolantCapacity() end,
|
||||
function () mek_data[12] = self.reactor.getHeatedCoolantCapacity() end,
|
||||
function () mek_data[13] = self.reactor.getMaxBurnRate() end
|
||||
function () mek_data[1] = reactor.getLength() end,
|
||||
function () mek_data[2] = reactor.getWidth() end,
|
||||
function () mek_data[3] = reactor.getHeight() end,
|
||||
function () mek_data[4] = reactor.getMinPos() end,
|
||||
function () mek_data[5] = reactor.getMaxPos() end,
|
||||
function () mek_data[6] = reactor.getHeatCapacity() end,
|
||||
function () mek_data[7] = reactor.getFuelAssemblies() end,
|
||||
function () mek_data[8] = reactor.getFuelSurfaceArea() end,
|
||||
function () mek_data[9] = reactor.getFuelCapacity() end,
|
||||
function () mek_data[10] = reactor.getWasteCapacity() end,
|
||||
function () mek_data[11] = reactor.getCoolantCapacity() end,
|
||||
function () mek_data[12] = reactor.getHeatedCoolantCapacity() end,
|
||||
function () mek_data[13] = reactor.getMaxBurnRate() end
|
||||
}
|
||||
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
|
||||
if not self.reactor.__p_is_faulted() then
|
||||
if not reactor.__p_is_faulted() then
|
||||
_send(RPLC_TYPE.MEK_STRUCT, mek_data)
|
||||
self.resend_build = false
|
||||
else
|
||||
@ -621,18 +618,16 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
local public = {}
|
||||
|
||||
-- reconnect a newly connected modem
|
||||
---@param modem table
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
function public.reconnect_modem(modem)
|
||||
self.modem = modem
|
||||
---@param new_modem table
|
||||
function public.reconnect_modem(new_modem)
|
||||
modem = new_modem
|
||||
_conf_channels()
|
||||
end
|
||||
|
||||
-- reconnect a newly connected reactor
|
||||
---@param reactor table
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
function public.reconnect_reactor(reactor)
|
||||
self.reactor = reactor
|
||||
---@param new_reactor table
|
||||
function public.reconnect_reactor(new_reactor)
|
||||
reactor = new_reactor
|
||||
self.status_cache = nil
|
||||
self.resend_build = true
|
||||
self.max_burn_rate = nil
|
||||
@ -670,7 +665,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
mek_data = self.status_cache
|
||||
end
|
||||
|
||||
heating_rate = self.reactor.getHeatingRate()
|
||||
heating_rate = reactor.getHeatingRate()
|
||||
end
|
||||
|
||||
local sys_status = {
|
||||
@ -705,6 +700,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
end
|
||||
|
||||
-- parse an RPLC packet
|
||||
---@nodiscard
|
||||
---@param side string
|
||||
---@param sender integer
|
||||
---@param reply_to integer
|
||||
@ -762,6 +758,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
|
||||
-- handle packet
|
||||
if protocol == PROTOCOL.RPLC then
|
||||
---@cast packet rplc_frame
|
||||
if self.linked then
|
||||
if packet.type == RPLC_TYPE.STATUS then
|
||||
-- request of full status, clear cache first
|
||||
@ -781,7 +778,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
|
||||
-- if no known max burn rate, check again
|
||||
if self.max_burn_rate == nil then
|
||||
self.max_burn_rate = self.reactor.getMaxBurnRate()
|
||||
self.max_burn_rate = reactor.getMaxBurnRate()
|
||||
end
|
||||
|
||||
-- if we know our max burn rate, update current burn rate setpoint if in range
|
||||
@ -792,8 +789,8 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
setpoints.burn_rate = burn_rate
|
||||
success = true
|
||||
else
|
||||
self.reactor.setBurnRate(burn_rate)
|
||||
success = not self.reactor.__p_is_faulted()
|
||||
reactor.setBurnRate(burn_rate)
|
||||
success = not reactor.__p_is_faulted()
|
||||
end
|
||||
else
|
||||
log.debug(burn_rate .. " rate outside of 0 < x <= " .. self.max_burn_rate)
|
||||
@ -836,7 +833,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
|
||||
-- if no known max burn rate, check again
|
||||
if self.max_burn_rate == nil then
|
||||
self.max_burn_rate = self.reactor.getMaxBurnRate()
|
||||
self.max_burn_rate = reactor.getMaxBurnRate()
|
||||
end
|
||||
|
||||
-- if we know our max burn rate, update current burn rate setpoint if in range
|
||||
@ -858,8 +855,8 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
-- activate the reactor
|
||||
log.debug("AUTO: activating the reactor")
|
||||
|
||||
self.reactor.setBurnRate(0.01)
|
||||
if self.reactor.__p_is_faulted() then
|
||||
reactor.setBurnRate(0.01)
|
||||
if reactor.__p_is_faulted() then
|
||||
log.warning("AUTO: failed to reset burn rate for auto activation")
|
||||
else
|
||||
if not rps.auto_activate() then
|
||||
@ -877,8 +874,8 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
ack = AUTO_ACK.RAMP_SET_OK
|
||||
else
|
||||
log.debug(util.c("AUTO: setting burn rate directly to ", burn_rate))
|
||||
self.reactor.setBurnRate(burn_rate)
|
||||
ack = util.trinary(self.reactor.__p_is_faulted(), AUTO_ACK.FAIL, AUTO_ACK.DIRECT_SET_OK)
|
||||
reactor.setBurnRate(burn_rate)
|
||||
ack = util.trinary(reactor.__p_is_faulted(), AUTO_ACK.FAIL, AUTO_ACK.DIRECT_SET_OK)
|
||||
end
|
||||
end
|
||||
else
|
||||
@ -897,6 +894,7 @@ function plc.comms(id, version, modem, local_port, server_port, range, reactor,
|
||||
log.debug("discarding RPLC packet before linked")
|
||||
end
|
||||
elseif protocol == PROTOCOL.SCADA_MGMT then
|
||||
---@cast packet mgmt_frame
|
||||
if self.linked then
|
||||
if packet.type == SCADA_MGMT_TYPE.ESTABLISH then
|
||||
-- link request confirmation
|
||||
|
@ -14,7 +14,7 @@ local config = require("reactor-plc.config")
|
||||
local plc = require("reactor-plc.plc")
|
||||
local threads = require("reactor-plc.threads")
|
||||
|
||||
local R_PLC_VERSION = "beta-v0.11.1"
|
||||
local R_PLC_VERSION = "v0.12.0"
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
@ -116,15 +116,15 @@ local function main()
|
||||
|
||||
-- we need a reactor, can at least do some things even if it isn't formed though
|
||||
if smem_dev.reactor == nil then
|
||||
println("boot> fission reactor not found");
|
||||
log.warning("no reactor on startup")
|
||||
println("init> fission reactor not found");
|
||||
log.warning("init> no reactor on startup")
|
||||
|
||||
plc_state.init_ok = false
|
||||
plc_state.degraded = true
|
||||
plc_state.no_reactor = true
|
||||
elseif not smem_dev.reactor.isFormed() then
|
||||
println("boot> fission reactor not formed");
|
||||
log.warning("reactor logic adapter present, but reactor is not formed")
|
||||
println("init> fission reactor not formed");
|
||||
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
||||
|
||||
plc_state.degraded = true
|
||||
plc_state.reactor_formed = false
|
||||
@ -132,8 +132,8 @@ local function main()
|
||||
|
||||
-- modem is required if networked
|
||||
if __shared_memory.networked and smem_dev.modem == nil then
|
||||
println("boot> wireless modem not found")
|
||||
log.warning("no wireless modem on startup")
|
||||
println("init> wireless modem not found")
|
||||
log.warning("init> no wireless modem on startup")
|
||||
|
||||
-- scram reactor if present and enabled
|
||||
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||
@ -168,17 +168,17 @@ local function main()
|
||||
config.TRUSTED_RANGE, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
||||
log.debug("init> comms init")
|
||||
else
|
||||
println("boot> starting in offline mode")
|
||||
println("init> starting in offline mode")
|
||||
log.info("init> running without networking")
|
||||
end
|
||||
|
||||
util.push_event("clock_start")
|
||||
|
||||
println("boot> completed")
|
||||
log.info("init> boot completed")
|
||||
println("init> completed")
|
||||
log.info("init> startup completed")
|
||||
else
|
||||
println("boot> system in degraded state, awaiting devices...")
|
||||
log.warning("init> booted in a degraded state, awaiting peripheral connections...")
|
||||
println("init> system in degraded state, awaiting devices...")
|
||||
log.warning("init> started in a degraded state, awaiting peripheral connections...")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -3,6 +3,7 @@ local rtu = require("rtu.rtu")
|
||||
local boilerv_rtu = {}
|
||||
|
||||
-- create new boiler (mek 10.1+) device
|
||||
---@nodiscard
|
||||
---@param boiler table
|
||||
function boilerv_rtu.new(boiler)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -3,6 +3,7 @@ local rtu = require("rtu.rtu")
|
||||
local envd_rtu = {}
|
||||
|
||||
-- create new environment detector device
|
||||
---@nodiscard
|
||||
---@param envd table
|
||||
function envd_rtu.new(envd)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -3,6 +3,7 @@ local rtu = require("rtu.rtu")
|
||||
local imatrix_rtu = {}
|
||||
|
||||
-- create new induction matrix (mek 10.1+) device
|
||||
---@nodiscard
|
||||
---@param imatrix table
|
||||
function imatrix_rtu.new(imatrix)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -1,7 +1,7 @@
|
||||
local rtu = require("rtu.rtu")
|
||||
|
||||
local rsio = require("scada-common.rsio")
|
||||
|
||||
local rtu = require("rtu.rtu")
|
||||
|
||||
local redstone_rtu = {}
|
||||
|
||||
local IO_LVL = rsio.IO_LVL
|
||||
@ -10,14 +10,15 @@ local digital_read = rsio.digital_read
|
||||
local digital_write = rsio.digital_write
|
||||
|
||||
-- create new redstone device
|
||||
---@nodiscard
|
||||
function redstone_rtu.new()
|
||||
local unit = rtu.init_unit()
|
||||
|
||||
-- get RTU interface
|
||||
local interface = unit.interface()
|
||||
|
||||
-- extends rtu_device; fields added manually to please Lua diagnostics
|
||||
---@class rtu_rs_device
|
||||
--- extends rtu_device; fields added manually to please Lua diagnostics
|
||||
local public = {
|
||||
io_count = interface.io_count,
|
||||
read_coil = interface.read_coil,
|
||||
|
@ -2,7 +2,8 @@ local rtu = require("rtu.rtu")
|
||||
|
||||
local sna_rtu = {}
|
||||
|
||||
-- create new solar neutron activator (sna) device
|
||||
-- create new solar neutron activator (SNA) device
|
||||
---@nodiscard
|
||||
---@param sna table
|
||||
function sna_rtu.new(sna)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -2,7 +2,8 @@ local rtu = require("rtu.rtu")
|
||||
|
||||
local sps_rtu = {}
|
||||
|
||||
-- create new super-critical phase shifter (sps) device
|
||||
-- create new super-critical phase shifter (SPS) device
|
||||
---@nodiscard
|
||||
---@param sps table
|
||||
function sps_rtu.new(sps)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -3,6 +3,7 @@ local rtu = require("rtu.rtu")
|
||||
local turbinev_rtu = {}
|
||||
|
||||
-- create new turbine (mek 10.1+) device
|
||||
---@nodiscard
|
||||
---@param turbine table
|
||||
function turbinev_rtu.new(turbine)
|
||||
local unit = rtu.init_unit()
|
||||
|
@ -7,22 +7,15 @@ local MODBUS_FCODE = types.MODBUS_FCODE
|
||||
local MODBUS_EXCODE = types.MODBUS_EXCODE
|
||||
|
||||
-- new modbus comms handler object
|
||||
---@nodiscard
|
||||
---@param rtu_dev rtu_device|rtu_rs_device RTU device
|
||||
---@param use_parallel_read boolean whether or not to use parallel calls when reading
|
||||
function modbus.new(rtu_dev, use_parallel_read)
|
||||
local self = {
|
||||
rtu = rtu_dev,
|
||||
use_parallel = use_parallel_read
|
||||
}
|
||||
|
||||
---@class modbus
|
||||
local public = {}
|
||||
|
||||
local insert = table.insert
|
||||
|
||||
-- read a span of coils (digital outputs)
|
||||
--
|
||||
-- read a span of coils (digital outputs)<br>
|
||||
-- returns a table of readings or a MODBUS_EXCODE error code
|
||||
---@nodiscard
|
||||
---@param c_addr_start integer
|
||||
---@param count integer
|
||||
---@return boolean ok, table|MODBUS_EXCODE readings
|
||||
@ -30,20 +23,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
local tasks = {}
|
||||
local readings = {} ---@type table|MODBUS_EXCODE
|
||||
local access_fault = false
|
||||
local _, coils, _, _ = self.rtu.io_count()
|
||||
local _, coils, _, _ = rtu_dev.io_count()
|
||||
local return_ok = ((c_addr_start + count) <= (coils + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = c_addr_start + i - 1
|
||||
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
insert(tasks, function ()
|
||||
local reading, fault = self.rtu.read_coil(addr)
|
||||
local reading, fault = rtu_dev.read_coil(addr)
|
||||
if fault then access_fault = true else readings[i] = reading end
|
||||
end)
|
||||
else
|
||||
readings[i], access_fault = self.rtu.read_coil(addr)
|
||||
readings[i], access_fault = rtu_dev.read_coil(addr)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -54,7 +47,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- run parallel tasks if configured
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
end
|
||||
|
||||
@ -69,9 +62,9 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
return return_ok, readings
|
||||
end
|
||||
|
||||
-- read a span of discrete inputs (digital inputs)
|
||||
--
|
||||
-- read a span of discrete inputs (digital inputs)<br>
|
||||
-- returns a table of readings or a MODBUS_EXCODE error code
|
||||
---@nodiscard
|
||||
---@param di_addr_start integer
|
||||
---@param count integer
|
||||
---@return boolean ok, table|MODBUS_EXCODE readings
|
||||
@ -79,20 +72,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
local tasks = {}
|
||||
local readings = {} ---@type table|MODBUS_EXCODE
|
||||
local access_fault = false
|
||||
local discrete_inputs, _, _, _ = self.rtu.io_count()
|
||||
local discrete_inputs, _, _, _ = rtu_dev.io_count()
|
||||
local return_ok = ((di_addr_start + count) <= (discrete_inputs + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = di_addr_start + i - 1
|
||||
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
insert(tasks, function ()
|
||||
local reading, fault = self.rtu.read_di(addr)
|
||||
local reading, fault = rtu_dev.read_di(addr)
|
||||
if fault then access_fault = true else readings[i] = reading end
|
||||
end)
|
||||
else
|
||||
readings[i], access_fault = self.rtu.read_di(addr)
|
||||
readings[i], access_fault = rtu_dev.read_di(addr)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -103,7 +96,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- run parallel tasks if configured
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
end
|
||||
|
||||
@ -118,9 +111,9 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
return return_ok, readings
|
||||
end
|
||||
|
||||
-- read a span of holding registers (analog outputs)
|
||||
--
|
||||
-- read a span of holding registers (analog outputs)<br>
|
||||
-- returns a table of readings or a MODBUS_EXCODE error code
|
||||
---@nodiscard
|
||||
---@param hr_addr_start integer
|
||||
---@param count integer
|
||||
---@return boolean ok, table|MODBUS_EXCODE readings
|
||||
@ -128,20 +121,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
local tasks = {}
|
||||
local readings = {} ---@type table|MODBUS_EXCODE
|
||||
local access_fault = false
|
||||
local _, _, _, hold_regs = self.rtu.io_count()
|
||||
local _, _, _, hold_regs = rtu_dev.io_count()
|
||||
local return_ok = ((hr_addr_start + count) <= (hold_regs + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = hr_addr_start + i - 1
|
||||
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
insert(tasks, function ()
|
||||
local reading, fault = self.rtu.read_holding_reg(addr)
|
||||
local reading, fault = rtu_dev.read_holding_reg(addr)
|
||||
if fault then access_fault = true else readings[i] = reading end
|
||||
end)
|
||||
else
|
||||
readings[i], access_fault = self.rtu.read_holding_reg(addr)
|
||||
readings[i], access_fault = rtu_dev.read_holding_reg(addr)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -152,7 +145,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- run parallel tasks if configured
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
end
|
||||
|
||||
@ -167,9 +160,9 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
return return_ok, readings
|
||||
end
|
||||
|
||||
-- read a span of input registers (analog inputs)
|
||||
--
|
||||
-- read a span of input registers (analog inputs)<br>
|
||||
-- returns a table of readings or a MODBUS_EXCODE error code
|
||||
---@nodiscard
|
||||
---@param ir_addr_start integer
|
||||
---@param count integer
|
||||
---@return boolean ok, table|MODBUS_EXCODE readings
|
||||
@ -177,20 +170,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
local tasks = {}
|
||||
local readings = {} ---@type table|MODBUS_EXCODE
|
||||
local access_fault = false
|
||||
local _, _, input_regs, _ = self.rtu.io_count()
|
||||
local _, _, input_regs, _ = rtu_dev.io_count()
|
||||
local return_ok = ((ir_addr_start + count) <= (input_regs + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = ir_addr_start + i - 1
|
||||
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
insert(tasks, function ()
|
||||
local reading, fault = self.rtu.read_input_reg(addr)
|
||||
local reading, fault = rtu_dev.read_input_reg(addr)
|
||||
if fault then access_fault = true else readings[i] = reading end
|
||||
end)
|
||||
else
|
||||
readings[i], access_fault = self.rtu.read_input_reg(addr)
|
||||
readings[i], access_fault = rtu_dev.read_input_reg(addr)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -201,7 +194,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- run parallel tasks if configured
|
||||
if self.use_parallel then
|
||||
if use_parallel_read then
|
||||
parallel.waitForAll(table.unpack(tasks))
|
||||
end
|
||||
|
||||
@ -217,16 +210,17 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- write a single coil (digital output)
|
||||
---@nodiscard
|
||||
---@param c_addr integer
|
||||
---@param value any
|
||||
---@return boolean ok, MODBUS_EXCODE
|
||||
local function _5_write_single_coil(c_addr, value)
|
||||
local response = nil
|
||||
local _, coils, _, _ = self.rtu.io_count()
|
||||
local _, coils, _, _ = rtu_dev.io_count()
|
||||
local return_ok = c_addr <= coils
|
||||
|
||||
if return_ok then
|
||||
local access_fault = self.rtu.write_coil(c_addr, value)
|
||||
local access_fault = rtu_dev.write_coil(c_addr, value)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -240,16 +234,17 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- write a single holding register (analog output)
|
||||
---@nodiscard
|
||||
---@param hr_addr integer
|
||||
---@param value any
|
||||
---@return boolean ok, MODBUS_EXCODE
|
||||
local function _6_write_single_holding_register(hr_addr, value)
|
||||
local response = nil
|
||||
local _, _, _, hold_regs = self.rtu.io_count()
|
||||
local _, _, _, hold_regs = rtu_dev.io_count()
|
||||
local return_ok = hr_addr <= hold_regs
|
||||
|
||||
if return_ok then
|
||||
local access_fault = self.rtu.write_holding_reg(hr_addr, value)
|
||||
local access_fault = rtu_dev.write_holding_reg(hr_addr, value)
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -263,19 +258,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- write multiple coils (digital outputs)
|
||||
---@nodiscard
|
||||
---@param c_addr_start integer
|
||||
---@param values any
|
||||
---@return boolean ok, MODBUS_EXCODE
|
||||
local function _15_write_multiple_coils(c_addr_start, values)
|
||||
local response = nil
|
||||
local _, coils, _, _ = self.rtu.io_count()
|
||||
local _, coils, _, _ = rtu_dev.io_count()
|
||||
local count = #values
|
||||
local return_ok = ((c_addr_start + count) <= (coils + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = c_addr_start + i - 1
|
||||
local access_fault = self.rtu.write_coil(addr, values[i])
|
||||
local access_fault = rtu_dev.write_coil(addr, values[i])
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -291,19 +287,20 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- write multiple holding registers (analog outputs)
|
||||
---@nodiscard
|
||||
---@param hr_addr_start integer
|
||||
---@param values any
|
||||
---@return boolean ok, MODBUS_EXCODE
|
||||
local function _16_write_multiple_holding_registers(hr_addr_start, values)
|
||||
local response = nil
|
||||
local _, _, _, hold_regs = self.rtu.io_count()
|
||||
local _, _, _, hold_regs = rtu_dev.io_count()
|
||||
local count = #values
|
||||
local return_ok = ((hr_addr_start + count) <= (hold_regs + 1)) and (count > 0)
|
||||
|
||||
if return_ok then
|
||||
for i = 1, count do
|
||||
local addr = hr_addr_start + i - 1
|
||||
local access_fault = self.rtu.write_holding_reg(addr, values[i])
|
||||
local access_fault = rtu_dev.write_holding_reg(addr, values[i])
|
||||
|
||||
if access_fault then
|
||||
return_ok = false
|
||||
@ -318,7 +315,11 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
return return_ok, response
|
||||
end
|
||||
|
||||
---@class modbus
|
||||
local public = {}
|
||||
|
||||
-- validate a request without actually executing it
|
||||
---@nodiscard
|
||||
---@param packet modbus_frame
|
||||
---@return boolean return_code, modbus_packet reply
|
||||
function public.check_request(packet)
|
||||
@ -360,6 +361,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- handle a MODBUS TCP packet and generate a reply
|
||||
---@nodiscard
|
||||
---@param packet modbus_frame
|
||||
---@return boolean return_code, modbus_packet reply
|
||||
function public.handle_packet(packet)
|
||||
@ -420,6 +422,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
||||
end
|
||||
|
||||
-- return a SERVER_DEVICE_BUSY error reply
|
||||
---@nodiscard
|
||||
---@param packet modbus_frame MODBUS packet frame
|
||||
---@return modbus_packet reply
|
||||
function modbus.reply__srv_device_busy(packet)
|
||||
@ -432,6 +435,7 @@ function modbus.reply__srv_device_busy(packet)
|
||||
end
|
||||
|
||||
-- return a NEG_ACKNOWLEDGE error reply
|
||||
---@nodiscard
|
||||
---@param packet modbus_frame MODBUS packet frame
|
||||
---@return modbus_packet reply
|
||||
function modbus.reply__neg_ack(packet)
|
||||
@ -444,6 +448,7 @@ function modbus.reply__neg_ack(packet)
|
||||
end
|
||||
|
||||
-- return a GATEWAY_PATH_UNAVAILABLE error reply
|
||||
---@nodiscard
|
||||
---@param packet modbus_frame MODBUS packet frame
|
||||
---@return modbus_packet reply
|
||||
function modbus.reply__gw_unavailable(packet)
|
||||
|
65
rtu/rtu.lua
65
rtu/rtu.lua
@ -19,7 +19,8 @@ local println = util.println
|
||||
local print_ts = util.print_ts
|
||||
local println_ts = util.println_ts
|
||||
|
||||
-- create a new RTU
|
||||
-- create a new RTU unit
|
||||
---@nodiscard
|
||||
function rtu.init_unit()
|
||||
local self = {
|
||||
discrete_inputs = {},
|
||||
@ -153,14 +154,13 @@ function rtu.init_unit()
|
||||
-- public RTU device access
|
||||
|
||||
-- get the public interface to this RTU
|
||||
function protected.interface()
|
||||
return public
|
||||
end
|
||||
function protected.interface() return public end
|
||||
|
||||
return protected
|
||||
end
|
||||
|
||||
-- RTU Communications
|
||||
---@nodiscard
|
||||
---@param version string RTU version
|
||||
---@param modem table modem device
|
||||
---@param local_port integer local listening port
|
||||
@ -169,20 +169,12 @@ end
|
||||
---@param conn_watchdog watchdog watchdog reference
|
||||
function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog)
|
||||
local self = {
|
||||
version = version,
|
||||
seq_num = 0,
|
||||
r_seq_num = nil,
|
||||
txn_id = 0,
|
||||
modem = modem,
|
||||
s_port = server_port,
|
||||
l_port = local_port,
|
||||
conn_watchdog = conn_watchdog,
|
||||
last_est_ack = ESTABLISH_ACK.ALLOW
|
||||
}
|
||||
|
||||
---@class rtu_comms
|
||||
local public = {}
|
||||
|
||||
local insert = table.insert
|
||||
|
||||
comms.set_trusted_range(range)
|
||||
@ -191,8 +183,8 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
|
||||
-- configure modem channels
|
||||
local function _conf_channels()
|
||||
self.modem.closeAll()
|
||||
self.modem.open(self.l_port)
|
||||
modem.closeAll()
|
||||
modem.open(local_port)
|
||||
end
|
||||
|
||||
_conf_channels()
|
||||
@ -207,7 +199,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
m_pkt.make(msg_type, msg)
|
||||
s_pkt.make(self.seq_num, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
||||
|
||||
self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable())
|
||||
modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
self.seq_num = self.seq_num + 1
|
||||
end
|
||||
|
||||
@ -218,6 +210,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
end
|
||||
|
||||
-- generate device advertisement table
|
||||
---@nodiscard
|
||||
---@param units table
|
||||
---@return table advertisement
|
||||
local function _generate_advertisement(units)
|
||||
@ -227,11 +220,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||
|
||||
if type ~= nil then
|
||||
local advert = {
|
||||
unit.type,
|
||||
unit.index,
|
||||
unit.reactor
|
||||
}
|
||||
local advert = { unit.type, unit.index, unit.reactor }
|
||||
|
||||
if type == RTU_UNIT_TYPE.REDSTONE then
|
||||
insert(advert, unit.device)
|
||||
@ -246,20 +235,22 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
|
||||
-- PUBLIC FUNCTIONS --
|
||||
|
||||
---@class rtu_comms
|
||||
local public = {}
|
||||
|
||||
-- send a MODBUS TCP packet
|
||||
---@param m_pkt modbus_packet
|
||||
function public.send_modbus(m_pkt)
|
||||
local s_pkt = comms.scada_packet()
|
||||
s_pkt.make(self.seq_num, PROTOCOL.MODBUS_TCP, m_pkt.raw_sendable())
|
||||
self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable())
|
||||
modem.transmit(server_port, local_port, s_pkt.raw_sendable())
|
||||
self.seq_num = self.seq_num + 1
|
||||
end
|
||||
|
||||
-- reconnect a newly connected modem
|
||||
---@param modem table
|
||||
---@diagnostic disable-next-line: redefined-local
|
||||
function public.reconnect_modem(modem)
|
||||
self.modem = modem
|
||||
---@param new_modem table
|
||||
function public.reconnect_modem(new_modem)
|
||||
modem = new_modem
|
||||
_conf_channels()
|
||||
end
|
||||
|
||||
@ -273,7 +264,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
-- close the connection to the server
|
||||
---@param rtu_state rtu_state
|
||||
function public.close(rtu_state)
|
||||
self.conn_watchdog.cancel()
|
||||
conn_watchdog.cancel()
|
||||
public.unlink(rtu_state)
|
||||
_send(SCADA_MGMT_TYPE.CLOSE, {})
|
||||
end
|
||||
@ -281,7 +272,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
-- send establish request (includes advertisement)
|
||||
---@param units table
|
||||
function public.send_establish(units)
|
||||
_send(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, self.version, DEVICE_TYPE.RTU, _generate_advertisement(units) })
|
||||
_send(SCADA_MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.RTU, _generate_advertisement(units) })
|
||||
end
|
||||
|
||||
-- send capability advertisement
|
||||
@ -297,6 +288,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
end
|
||||
|
||||
-- parse a MODBUS/SCADA packet
|
||||
---@nodiscard
|
||||
---@param side string
|
||||
---@param sender integer
|
||||
---@param reply_to integer
|
||||
@ -333,10 +325,10 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
|
||||
-- handle a MODBUS/SCADA packet
|
||||
---@param packet modbus_frame|mgmt_frame
|
||||
---@param units table
|
||||
---@param units table RTU units
|
||||
---@param rtu_state rtu_state
|
||||
function public.handle_packet(packet, units, rtu_state)
|
||||
if packet ~= nil and packet.scada_frame.local_port() == self.l_port then
|
||||
if packet.scada_frame.local_port() == local_port then
|
||||
-- check sequence number
|
||||
if self.r_seq_num == nil then
|
||||
self.r_seq_num = packet.scada_frame.seq_num()
|
||||
@ -348,14 +340,14 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
end
|
||||
|
||||
-- feed watchdog on valid sequence number
|
||||
self.conn_watchdog.feed()
|
||||
conn_watchdog.feed()
|
||||
|
||||
local protocol = packet.scada_frame.protocol()
|
||||
|
||||
if protocol == PROTOCOL.MODBUS_TCP then
|
||||
---@cast packet modbus_frame
|
||||
if rtu_state.linked then
|
||||
local return_code = false
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
local reply = modbus.reply__neg_ack(packet)
|
||||
|
||||
-- handle MODBUS instruction
|
||||
@ -365,20 +357,17 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
|
||||
if unit.name == "redstone_io" then
|
||||
-- immediately execute redstone RTU requests
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
return_code, reply = unit.modbus_io.handle_packet(packet)
|
||||
if not return_code then
|
||||
log.warning("requested MODBUS operation failed" .. unit_dbg_tag)
|
||||
end
|
||||
else
|
||||
-- check validity then pass off to unit comms thread
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
return_code, reply = unit.modbus_io.check_request(packet)
|
||||
if return_code then
|
||||
-- check if there are more than 3 active transactions
|
||||
-- still queue the packet, but this may indicate a problem
|
||||
if unit.pkt_queue.length() > 3 then
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
reply = modbus.reply__srv_device_busy(packet)
|
||||
log.debug("queueing new request with " .. unit.pkt_queue.length() ..
|
||||
" transactions already in the queue" .. unit_dbg_tag)
|
||||
@ -392,7 +381,6 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
end
|
||||
else
|
||||
-- unit ID out of range?
|
||||
---@diagnostic disable-next-line: param-type-mismatch
|
||||
reply = modbus.reply__gw_unavailable(packet)
|
||||
log.error("received MODBUS packet for non-existent unit")
|
||||
end
|
||||
@ -402,6 +390,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
log.debug("discarding MODBUS packet before linked")
|
||||
end
|
||||
elseif protocol == PROTOCOL.SCADA_MGMT then
|
||||
---@cast packet mgmt_frame
|
||||
-- SCADA management packet
|
||||
if packet.type == SCADA_MGMT_TYPE.ESTABLISH then
|
||||
if packet.length == 1 then
|
||||
@ -419,10 +408,10 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
if est_ack == ESTABLISH_ACK.BAD_VERSION then
|
||||
-- version mismatch
|
||||
println_ts("supervisor comms version mismatch (try updating), retrying...")
|
||||
log.warning("supervisor connection denied due to comms version mismatch")
|
||||
log.warning("supervisor connection denied due to comms version mismatch, retrying")
|
||||
else
|
||||
println_ts("supervisor connection denied, retrying...")
|
||||
log.warning("supervisor connection denied")
|
||||
log.warning("supervisor connection denied, retrying")
|
||||
end
|
||||
end
|
||||
|
||||
@ -452,7 +441,7 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
||||
end
|
||||
elseif packet.type == SCADA_MGMT_TYPE.CLOSE then
|
||||
-- close connection
|
||||
self.conn_watchdog.cancel()
|
||||
conn_watchdog.cancel()
|
||||
public.unlink(rtu_state)
|
||||
println_ts("server connection closed by remote host")
|
||||
log.warning("server connection closed by remote host")
|
||||
|
@ -25,7 +25,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.11.2"
|
||||
local RTU_VERSION = "v0.12.0"
|
||||
|
||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||
|
||||
@ -132,13 +132,17 @@ local function main()
|
||||
|
||||
-- CHECK: reactor ID must be >= to 1
|
||||
if (not util.is_int(io_reactor)) or (io_reactor < 0) then
|
||||
println(util.c("configure> redstone entry #", entry_idx, " : ", io_reactor, " isn't an integer >= 0"))
|
||||
local message = util.c("configure> redstone entry #", entry_idx, " : ", io_reactor, " isn't an integer >= 0")
|
||||
println(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
end
|
||||
|
||||
-- CHECK: io table exists
|
||||
if type(io_table) ~= "table" then
|
||||
println(util.c("configure> redstone entry #", entry_idx, " no IO table found"))
|
||||
local message = util.c("configure> redstone entry #", entry_idx, " no IO table found")
|
||||
println(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
end
|
||||
|
||||
@ -148,7 +152,7 @@ local function main()
|
||||
|
||||
local continue = true
|
||||
|
||||
-- check for duplicate entries
|
||||
-- CHECK: no duplicate entries
|
||||
for i = 1, #units do
|
||||
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||
if unit.reactor == io_reactor and unit.type == RTU_UNIT_TYPE.REDSTONE then
|
||||
@ -181,7 +185,7 @@ local function main()
|
||||
local message = util.c("configure> invalid redstone definition at index ", i, " in definition block #", entry_idx,
|
||||
" (for reactor ", io_reactor, ")")
|
||||
println(message)
|
||||
log.error(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
else
|
||||
-- link redstone in RTU
|
||||
@ -245,7 +249,7 @@ local function main()
|
||||
for_message = util.c("reactor ", io_reactor)
|
||||
end
|
||||
|
||||
log.debug(util.c("configure> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for ", for_message))
|
||||
log.info(util.c("configure> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for ", for_message))
|
||||
|
||||
unit.uid = #units
|
||||
end
|
||||
@ -259,25 +263,31 @@ local function main()
|
||||
|
||||
-- CHECK: name is a string
|
||||
if type(name) ~= "string" then
|
||||
println(util.c("configure> device entry #", i, ": device ", name, " isn't a string"))
|
||||
local message = util.c("configure> device entry #", i, ": device ", name, " isn't a string")
|
||||
println(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
end
|
||||
|
||||
-- CHECK: index is an integer >= 1
|
||||
if (not util.is_int(index)) or (index <= 0) then
|
||||
println(util.c("configure> device entry #", i, ": index ", index, " isn't an integer >= 1"))
|
||||
local message = util.c("configure> device entry #", i, ": index ", index, " isn't an integer >= 1")
|
||||
println(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
end
|
||||
|
||||
-- CHECK: reactor is an integer >= 0
|
||||
if (not util.is_int(for_reactor)) or (for_reactor < 0) then
|
||||
println(util.c("configure> device entry #", i, ": reactor ", for_reactor, " isn't an integer >= 0"))
|
||||
local message = util.c("configure> device entry #", i, ": reactor ", for_reactor, " isn't an integer >= 0")
|
||||
println(message)
|
||||
log.fatal(message)
|
||||
return false
|
||||
end
|
||||
|
||||
local device = ppm.get_periph(name)
|
||||
|
||||
local type = nil
|
||||
local type = nil ---@type string|nil
|
||||
local rtu_iface = nil ---@type rtu_device
|
||||
local rtu_type = nil ---@type RTU_UNIT_TYPE
|
||||
local is_multiblock = false
|
||||
@ -382,7 +392,7 @@ local function main()
|
||||
table.insert(units, rtu_unit)
|
||||
|
||||
if is_multiblock and not formed then
|
||||
log.debug(util.c("configure> device '", name, "' is not formed"))
|
||||
log.info(util.c("configure> device '", name, "' is not formed"))
|
||||
end
|
||||
|
||||
local for_message = "facility"
|
||||
@ -390,7 +400,7 @@ local function main()
|
||||
for_message = util.c("reactor ", for_reactor)
|
||||
end
|
||||
|
||||
log.debug(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
|
||||
log.info(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
|
||||
|
||||
rtu_unit.uid = #units
|
||||
end
|
||||
@ -408,12 +418,12 @@ local function main()
|
||||
if configure() then
|
||||
-- start connection watchdog
|
||||
smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT)
|
||||
log.debug("boot> conn watchdog started")
|
||||
log.debug("startup> conn watchdog started")
|
||||
|
||||
-- setup comms
|
||||
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT,
|
||||
config.TRUSTED_RANGE, smem_sys.conn_watchdog)
|
||||
log.debug("boot> comms init")
|
||||
log.debug("startup> comms init")
|
||||
|
||||
-- init threads
|
||||
local main_thread = threads.thread__main(__shared_memory)
|
||||
@ -427,6 +437,8 @@ local function main()
|
||||
end
|
||||
end
|
||||
|
||||
log.info("startup> completed")
|
||||
|
||||
-- run threads
|
||||
parallel.waitForAll(table.unpack(_threads))
|
||||
else
|
||||
|
@ -26,6 +26,7 @@ local MAIN_CLOCK = 2 -- (2Hz, 40 ticks)
|
||||
local COMMS_SLEEP = 100 -- (100ms, 2 ticks)
|
||||
|
||||
-- main thread
|
||||
---@nodiscard
|
||||
---@param smem rtu_shared_memory
|
||||
function threads.thread__main(smem)
|
||||
---@class parallel_thread
|
||||
@ -114,9 +115,9 @@ function threads.thread__main(smem)
|
||||
rtu_comms.reconnect_modem(rtu_dev.modem)
|
||||
|
||||
println_ts("wireless modem reconnected.")
|
||||
log.info("comms modem reconnected.")
|
||||
log.info("comms modem reconnected")
|
||||
else
|
||||
log.info("wired modem reconnected.")
|
||||
log.info("wired modem reconnected")
|
||||
end
|
||||
else
|
||||
-- relink lost peripheral to correct unit entry
|
||||
@ -233,6 +234,7 @@ function threads.thread__main(smem)
|
||||
end
|
||||
|
||||
-- communications handler thread
|
||||
---@nodiscard
|
||||
---@param smem rtu_shared_memory
|
||||
function threads.thread__comms(smem)
|
||||
---@class parallel_thread
|
||||
@ -306,6 +308,7 @@ function threads.thread__comms(smem)
|
||||
end
|
||||
|
||||
-- per-unit communications handler thread
|
||||
---@nodiscard
|
||||
---@param smem rtu_shared_memory
|
||||
---@param unit rtu_unit_registry_entry
|
||||
function threads.thread__unit_comms(smem, unit)
|
||||
|
@ -244,7 +244,7 @@ function comms.modbus_packet()
|
||||
txn_id = -1,
|
||||
length = 0,
|
||||
unit_id = -1,
|
||||
func_code = 0,
|
||||
func_code = 0x80,
|
||||
data = {}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user