diff --git a/reactor-plc/plc.lua b/reactor-plc/plc.lua index 675bc70..8212875 100644 --- a/reactor-plc/plc.lua +++ b/reactor-plc/plc.lua @@ -172,6 +172,78 @@ function iss_init(reactor) } end +function rplc_packet() + local self = { + frame = nil, + id = nil, + type = nil, + length = nil, + body = nil + } + + local _rplc_type_valid = function () + return self.type == RPLC_TYPES.KEEP_ALIVE or + self.type == RPLC_TYPES.LINK_REQ or + self.type == RPLC_TYPES.STATUS or + self.type == RPLC_TYPES.MEK_STRUCT or + self.type == RPLC_TYPES.MEK_SCRAM or + self.type == RPLC_TYPES.MEK_ENABLE or + self.type == RPLC_TYPES.MEK_BURN_RATE or + self.type == RPLC_TYPES.ISS_ALARM or + self.type == RPLC_TYPES.ISS_GET or + self.type == RPLC_TYPES.ISS_CLEAR + end + + -- make an RPLC packet + local make = function (id, packet_type, length, data) + self.id = id + self.type = packet_type + self.length = length + self.data = data + end + + -- decode an RPLC packet from a SCADA frame + local decode = function (frame) + if frame then + self.frame = frame + + if frame.protocol() == comms.PROTOCOLS.RPLC then + local data = frame.data() + local ok = #data > 2 + + if ok then + make(data[1], data[2], data[3], { table.unpack(data, 4, #data) }) + ok = _rplc_type_valid() + end + + return ok + else + log._debug("attempted RPLC parse of incorrect protocol " .. frame.protocol(), true) + return false + end + else + log._debug("nil frame encountered", true) + return false + end + end + + local get = function () + return { + scada_frame = self.frame, + id = self.id, + type = self.type, + length = self.length, + data = self.data + } + end + + return { + make = make, + decode = decode, + get = get + } +end + -- reactor PLC communications function rplc_comms(id, modem, local_port, server_port, reactor) local self = { @@ -249,17 +321,21 @@ function rplc_comms(id, modem, local_port, server_port, reactor) -- parse packet as generic SCADA packet s_pkt.recieve(side, sender, reply_to, message, distance) - -- get using RPLC protocol format - if s_pkt.is_valid() and s_pkt.protocol() == PROTOCOLS.RPLC then - local body = s_pkt.data() - if #body > 2 then - pkt = { - scada_frame = s_pkt, - id = body[1], - type = body[2], - length = #body - 2, - body = { table.unpack(body, 3, 2 + #body) } - } + if s_pkt.is_valid() then + -- get as RPLC packet + if s_pkt.protocol() == PROTOCOLS.RPLC then + local rplc_pkt = rplc_packet() + if rplc_pkt.decode(s_pkt) then + pkt = rplc_pkt.get() + end + -- get as SCADA management packet + elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then + local mgmt_pkt = mgmt_packet() + if mgmt_pkt.decode(s_pkt) then + pkt = mgmt_packet.get() + end + else + log._error("illegal packet type " .. s_pkt.protocol(), true) end end diff --git a/rtu/rtu.lua b/rtu/rtu.lua index e682787..a80e3f1 100644 --- a/rtu/rtu.lua +++ b/rtu/rtu.lua @@ -121,34 +121,17 @@ function rtu_comms(modem, local_port, server_port) -- get as MODBUS TCP packet if s_pkt.protocol() == PROTOCOLS.MODBUS_TCP then local m_pkt = modbus_packet() - m_pkt.receive(s_pkt.data()) - - pkt = { - scada_frame = s_pkt, - modbus_frame = m_pkt - } + if m_pkt.decode(s_pkt) then + pkt = m_pkt.get() + end -- get as SCADA management packet elseif s_pkt.protocol() == PROTOCOLS.SCADA_MGMT then - local body = s_pkt.data() - if #body > 1 then - pkt = { - scada_frame = s_pkt, - type = body[1], - length = #body - 1, - body = { table.unpack(body, 2, 1 + #body) } - } - elseif #body == 1 then - pkt = { - scada_frame = s_pkt, - type = body[1], - length = #body - 1, - body = nil - } - else - log._error("Malformed SCADA packet has no length field") + local mgmt_pkt = mgmt_packet() + if mgmt_pkt.decode(s_pkt) then + pkt = mgmt_packet.get() end else - log._error("Illegal packet type " .. s_pkt.protocol(), true) + log._error("illegal packet type " .. s_pkt.protocol(), true) end end @@ -161,8 +144,9 @@ function rtu_comms(modem, local_port, server_port) if protocol == PROTOCOLS.MODBUS_TCP then -- MODBUS instruction - if packet.modbus_frame.unit_id <= #units then - local return_code, response = units.modbus_io.handle_packet(packet.modbus_frame) + if packet.unit_id <= #units then + local unit = units[packet.unit_id] + local return_code, response = unit.modbus_io.handle_packet(packet) _send(response, PROTOCOLS.MODBUS_TCP) if not return_code then @@ -186,7 +170,7 @@ function rtu_comms(modem, local_port, server_port) end else -- should be unreachable assuming packet is from parse_packet() - log._error("Illegal packet type " .. protocol, true) + log._error("illegal packet type " .. protocol, true) end end end diff --git a/scada-common/comms.lua b/scada-common/comms.lua index f760352..ac0c3c5 100644 --- a/scada-common/comms.lua +++ b/scada-common/comms.lua @@ -114,3 +114,67 @@ function scada_packet() data = data } end + +function mgmt_packet() + local self = { + frame = nil, + type = nil, + length = nil, + data = nil + } + + local _scada_type_valid = function () + return self.type == SCADA_MGMT_TYPES.PING or + self.type == SCADA_MGMT_TYPES.SV_HEARTBEAT or + self.type == SCADA_MGMT_TYPES.REMOTE_LINKED or + self.type == SCADA_MGMT_TYPES.RTU_ADVERT or + self.type == SCADA_MGMT_TYPES.RTU_HEARTBEAT + end + + -- make a SCADA management packet + local make = function (packet_type, length, data) + self.type = packet_type + self.length = length + self.data = data + end + + -- decode a SCADA management packet from a SCADA frame + local decode = function (frame) + if frame then + self.frame = frame + + if frame.protocol() == comms.PROTOCOLS.SCADA_MGMT then + local data = frame.data() + local ok = #data > 1 + + if ok then + make(data[1], data[2], { table.unpack(data, 3, #data) }) + ok = _scada_type_valid() + end + + return ok + else + log._debug("attempted SCADA_MGMT parse of incorrect protocol " .. frame.protocol(), true) + return false + end + else + log._debug("nil frame encountered", true) + return false + end + end + + local get = function () + return { + scada_frame = self.frame, + type = self.type, + length = self.length, + data = self.data + } + end + + return { + make = make, + decode = decode, + get = get + } +end diff --git a/scada-common/modbus.lua b/scada-common/modbus.lua index de21a2e..9d2899d 100644 --- a/scada-common/modbus.lua +++ b/scada-common/modbus.lua @@ -176,9 +176,8 @@ function modbus_init(rtu_dev) end function modbus_packet() - local MODBUS_TCP = 0 - local self = { + frame = nil, txn_id = txn_id, protocol = protocol, length = length, @@ -187,6 +186,7 @@ function modbus_packet() data = data } + -- make a MODBUS packet local make = function (txn_id, protocol, length, unit_id, func_code, data) self.txn_id = txn_id self.protocol = protocol @@ -196,18 +196,29 @@ function modbus_packet() self.data = data end - local receive = function (raw) - local size_ok = #raw ~= 6 + -- decode a MODBUS packet from a SCADA frame + local decode = function (frame) + if frame then + self.frame = frame + + local data = frame.data() + local size_ok = #data ~= 6 - if size_ok then - make(raw[1], raw[2], raw[3], raw[4], raw[5], raw[6]) + if size_ok then + make(data[1], data[2], data[3], data[4], data[5], data[6]) + end + + return size_ok and self.protocol == comms.PROTOCOLS.MODBUS_TCP + else + log._debug("nil frame encountered", true) + return false end - - return size_ok and self.protocol == MODBUS_TCP end + -- get this packet local get = function () return { + scada_frame = self.frame, txn_id = self.txn_id, protocol = self.protocol, length = self.length, @@ -219,7 +230,7 @@ function modbus_packet() return { make = make, - receive = receive, + decode = decode, get = get } end