mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#39 RTU unit threads
This commit is contained in:
parent
e833176c65
commit
84e7ad43bc
43
rtu/rtu.lua
43
rtu/rtu.lua
@ -145,13 +145,6 @@ function rtu_comms(modem, local_port, server_port)
|
|||||||
self.seq_num = self.seq_num + 1
|
self.seq_num = self.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local _send_modbus = function (m_pkt)
|
|
||||||
local s_pkt = comms.scada_packet()
|
|
||||||
s_pkt.make(self.seq_num, PROTOCOLS.MODBUS_TCP, m_pkt.raw_sendable())
|
|
||||||
self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable())
|
|
||||||
self.seq_num = self.seq_num + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
-- reconnect a newly connected modem
|
-- reconnect a newly connected modem
|
||||||
@ -164,6 +157,14 @@ function rtu_comms(modem, local_port, server_port)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- send a MODBUS TCP packet
|
||||||
|
local send_modbus = function (m_pkt)
|
||||||
|
local s_pkt = comms.scada_packet()
|
||||||
|
s_pkt.make(self.seq_num, PROTOCOLS.MODBUS_TCP, m_pkt.raw_sendable())
|
||||||
|
self.modem.transmit(self.s_port, self.l_port, s_pkt.raw_sendable())
|
||||||
|
self.seq_num = self.seq_num + 1
|
||||||
|
end
|
||||||
|
|
||||||
-- parse a MODBUS/SCADA packet
|
-- parse a MODBUS/SCADA packet
|
||||||
local parse_packet = function(side, sender, reply_to, message, distance)
|
local parse_packet = function(side, sender, reply_to, message, distance)
|
||||||
local pkt = nil
|
local pkt = nil
|
||||||
@ -216,13 +217,28 @@ function rtu_comms(modem, local_port, server_port)
|
|||||||
if protocol == PROTOCOLS.MODBUS_TCP then
|
if protocol == PROTOCOLS.MODBUS_TCP then
|
||||||
local reply = modbus.reply__neg_ack(packet)
|
local reply = modbus.reply__neg_ack(packet)
|
||||||
|
|
||||||
-- MODBUS instruction
|
-- handle MODBUS instruction
|
||||||
if packet.unit_id <= #units then
|
if packet.unit_id <= #units then
|
||||||
local unit = units[packet.unit_id]
|
local unit = units[packet.unit_id]
|
||||||
local return_code, reply = unit.modbus_io.handle_packet(packet)
|
if unit.name == "redstone_io" then
|
||||||
|
-- immediately execute redstone RTU requests
|
||||||
if not return_code then
|
local return_code, reply = unit.modbus_io.handle_packet(packet)
|
||||||
log._warning("MODBUS operation failed")
|
if not return_code then
|
||||||
|
log._warning("requested MODBUS operation failed")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- check validity then pass off to unit comms thread
|
||||||
|
local return_code, reply = unit.modbus_io.check_request(packet)
|
||||||
|
if return_code then
|
||||||
|
-- check if an operation is already in progress for this unit
|
||||||
|
if unit.modbus_busy then
|
||||||
|
reply = unit.modbus_io.reply__srv_device_busy(packet)
|
||||||
|
else
|
||||||
|
unit.pkt_queue.push(packet)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log._warning("cannot perform requested MODBUS operation")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- unit ID out of range?
|
-- unit ID out of range?
|
||||||
@ -230,7 +246,7 @@ function rtu_comms(modem, local_port, server_port)
|
|||||||
log._error("MODBUS packet requesting non-existent unit")
|
log._error("MODBUS packet requesting non-existent unit")
|
||||||
end
|
end
|
||||||
|
|
||||||
_send_modbus(reply)
|
send_modbus(reply)
|
||||||
elseif protocol == PROTOCOLS.SCADA_MGMT then
|
elseif protocol == PROTOCOLS.SCADA_MGMT then
|
||||||
-- SCADA management packet
|
-- SCADA management packet
|
||||||
if packet.type == SCADA_MGMT_TYPES.REMOTE_LINKED then
|
if packet.type == SCADA_MGMT_TYPES.REMOTE_LINKED then
|
||||||
@ -302,6 +318,7 @@ function rtu_comms(modem, local_port, server_port)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
send_modbus = send_modbus,
|
||||||
reconnect_modem = reconnect_modem,
|
reconnect_modem = reconnect_modem,
|
||||||
parse_packet = parse_packet,
|
parse_packet = parse_packet,
|
||||||
handle_packet = handle_packet,
|
handle_packet = handle_packet,
|
||||||
|
@ -19,7 +19,7 @@ os.loadAPI("dev/boiler_rtu.lua")
|
|||||||
os.loadAPI("dev/imatrix_rtu.lua")
|
os.loadAPI("dev/imatrix_rtu.lua")
|
||||||
os.loadAPI("dev/turbine_rtu.lua")
|
os.loadAPI("dev/turbine_rtu.lua")
|
||||||
|
|
||||||
local RTU_VERSION = "alpha-v0.4.7"
|
local RTU_VERSION = "alpha-v0.4.8"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -142,7 +142,10 @@ for reactor_idx = 1, #rtu_redstone do
|
|||||||
reactor = rtu_redstone[reactor_idx].for_reactor,
|
reactor = rtu_redstone[reactor_idx].for_reactor,
|
||||||
device = capabilities, -- use device field for redstone channels
|
device = capabilities, -- use device field for redstone channels
|
||||||
rtu = rs_rtu,
|
rtu = rs_rtu,
|
||||||
modbus_io = modbus.new(rs_rtu)
|
modbus_io = modbus.new(rs_rtu),
|
||||||
|
modbus_busy = false,
|
||||||
|
pkt_queue = nil,
|
||||||
|
thread = nil
|
||||||
})
|
})
|
||||||
|
|
||||||
log._debug("init> initialized RTU unit #" .. #units .. ": redstone_io (redstone) [1] for reactor " .. rtu_redstone[reactor_idx].for_reactor)
|
log._debug("init> initialized RTU unit #" .. #units .. ": redstone_io (redstone) [1] for reactor " .. rtu_redstone[reactor_idx].for_reactor)
|
||||||
@ -180,15 +183,22 @@ for i = 1, #rtu_devices do
|
|||||||
end
|
end
|
||||||
|
|
||||||
if rtu_iface ~= nil then
|
if rtu_iface ~= nil then
|
||||||
table.insert(units, {
|
local rtu_unit = {
|
||||||
name = rtu_devices[i].name,
|
name = rtu_devices[i].name,
|
||||||
type = rtu_type,
|
type = rtu_type,
|
||||||
index = rtu_devices[i].index,
|
index = rtu_devices[i].index,
|
||||||
reactor = rtu_devices[i].for_reactor,
|
reactor = rtu_devices[i].for_reactor,
|
||||||
device = device,
|
device = device,
|
||||||
rtu = rtu_iface,
|
rtu = rtu_iface,
|
||||||
modbus_io = modbus.new(rtu_iface)
|
modbus_io = modbus.new(rtu_iface),
|
||||||
})
|
modbus_busy = false,
|
||||||
|
pkt_queue = mqueue.new(),
|
||||||
|
thread = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rtu_unit.thread = threads.thread__unit_comms(__shared_memory, rtu_unit)
|
||||||
|
|
||||||
|
table.insert(units, rtu_unit)
|
||||||
|
|
||||||
log._debug("init> initialized RTU unit #" .. #units .. ": " .. rtu_devices[i].name .. " (" .. rtu_type .. ") [" ..
|
log._debug("init> initialized RTU unit #" .. #units .. ": " .. rtu_devices[i].name .. " (" .. rtu_type .. ") [" ..
|
||||||
rtu_devices[i].index .. "] for reactor " .. rtu_devices[i].for_reactor)
|
rtu_devices[i].index .. "] for reactor " .. rtu_devices[i].for_reactor)
|
||||||
@ -208,8 +218,16 @@ local comms_thread = threads.thread__comms(__shared_memory)
|
|||||||
smem_sys.conn_watchdog = util.new_watchdog(5)
|
smem_sys.conn_watchdog = util.new_watchdog(5)
|
||||||
log._debug("init> conn watchdog started")
|
log._debug("init> conn watchdog started")
|
||||||
|
|
||||||
|
-- assemble thread list
|
||||||
|
local _threads = { main_thread.exec, comms_thread.exec }
|
||||||
|
for i = 1, #units do
|
||||||
|
if units[i].thread ~= nil then
|
||||||
|
table.insert(_threads, units[i].thread.exec)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- run threads
|
-- run threads
|
||||||
parallel.waitForAll(main_thread.exec, comms_thread.exec)
|
parallel.waitForAll(table.unpack(_threads))
|
||||||
|
|
||||||
println_ts("exited")
|
println_ts("exited")
|
||||||
log._info("exited")
|
log._info("exited")
|
||||||
|
@ -180,3 +180,52 @@ function thread__comms(smem)
|
|||||||
|
|
||||||
return { exec = exec }
|
return { exec = exec }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- per-unit communications handler thread
|
||||||
|
function thread__unit_comms(smem, unit)
|
||||||
|
-- execute thread
|
||||||
|
local exec = function ()
|
||||||
|
log._debug("rtu unit thread start -> " .. unit.name .. "(" .. unit.type .. ")")
|
||||||
|
|
||||||
|
-- load in from shared memory
|
||||||
|
local rtu_state = smem.rtu_state
|
||||||
|
|
||||||
|
local packet_queue = unit.pkt_queue
|
||||||
|
|
||||||
|
local last_update = util.time()
|
||||||
|
|
||||||
|
-- thread loop
|
||||||
|
while true do
|
||||||
|
-- check for messages in the message queue
|
||||||
|
while packet_queue.ready() and not rtu_state.shutdown do
|
||||||
|
local msg = packet_queue.pop()
|
||||||
|
|
||||||
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
|
-- received a command
|
||||||
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
|
-- received data
|
||||||
|
elseif msg.qtype == mqueue.TYPE.PACKET then
|
||||||
|
-- received a packet
|
||||||
|
unit.modbus_busy = true
|
||||||
|
local return_code, reply = unit.modbus_io.handle_packet(packet)
|
||||||
|
rtu.send_modbus(reply)
|
||||||
|
unit.modbus_busy = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- quick yield
|
||||||
|
util.nop()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request
|
||||||
|
if rtu_state.shutdown then
|
||||||
|
log._warning("rtu unit thread exiting -> " .. unit.name .. "(" .. unit.type .. ")")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delay before next check
|
||||||
|
last_update = util.adaptive_delay(COMMS_SLEEP, last_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return { exec = exec }
|
||||||
|
end
|
||||||
|
@ -203,6 +203,46 @@ function new(rtu_dev)
|
|||||||
return return_ok, response
|
return return_ok, response
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- validate a request without actually executing it
|
||||||
|
local check_request = function (packet)
|
||||||
|
local return_code = true
|
||||||
|
local response = { MODBUS_EXCODE.ACKNOWLEDGE }
|
||||||
|
|
||||||
|
if #packet.data == 2 then
|
||||||
|
-- handle by function code
|
||||||
|
if packet.func_code == MODBUS_FCODE.READ_COILS then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.READ_DISCRETE_INPUTS then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.READ_MUL_HOLD_REGS then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.READ_INPUT_REGISTERS then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.WRITE_SINGLE_COIL then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.WRITE_SINGLE_HOLD_REG then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.WRITE_MUL_COILS then
|
||||||
|
elseif packet.func_code == MODBUS_FCODE.WRITE_MUL_HOLD_REGS then
|
||||||
|
else
|
||||||
|
-- unknown function
|
||||||
|
return_code = false
|
||||||
|
response = { MODBUS_EXCODE.ILLEGAL_FUNCTION }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- invalid length
|
||||||
|
return_code = false
|
||||||
|
response = { MODBUS_EXCODE.NEG_ACKNOWLEDGE }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- default is to echo back
|
||||||
|
local func_code = packet.func_code
|
||||||
|
if not return_code then
|
||||||
|
-- echo back with error flag
|
||||||
|
func_code = bit.bor(packet.func_code, MODBUS_FCODE.ERROR_FLAG)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create reply
|
||||||
|
local reply = comms.modbus_packet()
|
||||||
|
reply.make(packet.txn_id, packet.unit_id, func_code, response)
|
||||||
|
|
||||||
|
return return_code, reply
|
||||||
|
end
|
||||||
|
|
||||||
-- handle a MODBUS TCP packet and generate a reply
|
-- handle a MODBUS TCP packet and generate a reply
|
||||||
local handle_packet = function (packet)
|
local handle_packet = function (packet)
|
||||||
local return_code = true
|
local return_code = true
|
||||||
@ -258,6 +298,16 @@ function new(rtu_dev)
|
|||||||
return return_code, reply
|
return return_code, reply
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- return a SERVER_DEVICE_BUSY error reply
|
||||||
|
local reply__srv_device_busy = function (packet)
|
||||||
|
-- reply back with error flag and exception code
|
||||||
|
local reply = comms.modbus_packet()
|
||||||
|
local fcode = bit.bor(packet.func_code, MODBUS_FCODE.ERROR_FLAG)
|
||||||
|
local data = { MODBUS_EXCODE.SERVER_DEVICE_BUSY }
|
||||||
|
reply.make(packet.txn_id, packet.unit_id, fcode, data)
|
||||||
|
return reply
|
||||||
|
end
|
||||||
|
|
||||||
-- return a NEG_ACKNOWLEDGE error reply
|
-- return a NEG_ACKNOWLEDGE error reply
|
||||||
local reply__neg_ack = function (packet)
|
local reply__neg_ack = function (packet)
|
||||||
-- reply back with error flag and exception code
|
-- reply back with error flag and exception code
|
||||||
@ -279,7 +329,9 @@ function new(rtu_dev)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
check_request = check_request,
|
||||||
handle_packet = handle_packet,
|
handle_packet = handle_packet,
|
||||||
|
reply__srv_device_busy = reply__srv_device_busy,
|
||||||
reply__neg_ack = reply__neg_ack,
|
reply__neg_ack = reply__neg_ack,
|
||||||
reply__gw_unavailable = reply__gw_unavailable
|
reply__gw_unavailable = reply__gw_unavailable
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user