#30 comms rework
This commit is contained in:
parent
6daf6df2d0
commit
78a1073e2a
@ -2,7 +2,8 @@ PROTOCOLS = {
|
|||||||
MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol
|
MODBUS_TCP = 0, -- our "MODBUS TCP"-esque protocol
|
||||||
RPLC = 1, -- reactor PLC protocol
|
RPLC = 1, -- reactor PLC protocol
|
||||||
SCADA_MGMT = 2, -- SCADA supervisor management, device advertisements, etc
|
SCADA_MGMT = 2, -- SCADA supervisor management, device advertisements, etc
|
||||||
COORD_DATA = 3 -- data packets for coordinators to/from supervisory controller
|
COORD_DATA = 3, -- data/control packets for coordinators to/from supervisory controllers
|
||||||
|
COORD_API = 4 -- data/control packets for pocket computers to/from coordinators
|
||||||
}
|
}
|
||||||
|
|
||||||
RPLC_TYPES = {
|
RPLC_TYPES = {
|
||||||
@ -44,20 +45,24 @@ function scada_packet()
|
|||||||
local self = {
|
local self = {
|
||||||
modem_msg_in = nil,
|
modem_msg_in = nil,
|
||||||
valid = false,
|
valid = false,
|
||||||
|
raw = nil,
|
||||||
seq_num = nil,
|
seq_num = nil,
|
||||||
protocol = nil,
|
protocol = nil,
|
||||||
length = nil,
|
length = nil,
|
||||||
raw = nil
|
payload = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- make a SCADA packet
|
||||||
local make = function (seq_num, protocol, payload)
|
local make = function (seq_num, protocol, payload)
|
||||||
self.valid = true
|
self.valid = true
|
||||||
self.seq_num = seq_num
|
self.seq_num = seq_num
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.length = #payload
|
self.length = #payload
|
||||||
self.raw = { self.seq_num, self.protocol, self.length, payload }
|
self.payload = payload
|
||||||
|
self.raw = { self.seq_num, self.protocol, self.payload }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- parse in a modem message as a SCADA packet
|
||||||
local receive = function (side, sender, reply_to, message, distance)
|
local receive = function (side, sender, reply_to, message, distance)
|
||||||
self.modem_msg_in = {
|
self.modem_msg_in = {
|
||||||
iface = side,
|
iface = side,
|
||||||
@ -69,17 +74,19 @@ function scada_packet()
|
|||||||
|
|
||||||
self.raw = self.modem_msg_in.msg
|
self.raw = self.modem_msg_in.msg
|
||||||
|
|
||||||
if #self.raw < 3 then
|
if #self.raw >= 3 then
|
||||||
-- malformed
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
self.valid = true
|
self.valid = true
|
||||||
self.seq_num = self.raw[1]
|
self.seq_num = self.raw[1]
|
||||||
self.protocol = self.raw[2]
|
self.protocol = self.raw[2]
|
||||||
self.length = self.raw[3]
|
self.length = #self.raw[3]
|
||||||
|
self.payload = self.raw[3]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self.valid
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- public accessors --
|
||||||
|
|
||||||
local modem_event = function () return self.modem_msg_in end
|
local modem_event = function () return self.modem_msg_in end
|
||||||
local raw = function () return self.raw end
|
local raw = function () return self.raw end
|
||||||
|
|
||||||
@ -91,23 +98,21 @@ function scada_packet()
|
|||||||
local seq_num = function () return self.seq_num end
|
local seq_num = function () return self.seq_num end
|
||||||
local protocol = function () return self.protocol end
|
local protocol = function () return self.protocol end
|
||||||
local length = function () return self.length end
|
local length = function () return self.length end
|
||||||
|
local data = function () return self.payload end
|
||||||
local data = function ()
|
|
||||||
local subset = nil
|
|
||||||
if self.valid then
|
|
||||||
subset = { table.unpack(self.raw, 4, 3 + self.length) }
|
|
||||||
end
|
|
||||||
return subset
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- construct
|
||||||
make = make,
|
make = make,
|
||||||
receive = receive,
|
receive = receive,
|
||||||
|
-- raw access
|
||||||
modem_event = modem_event,
|
modem_event = modem_event,
|
||||||
raw = raw,
|
raw = raw,
|
||||||
|
-- sender/receiver
|
||||||
sender = sender,
|
sender = sender,
|
||||||
receiver = receiver,
|
receiver = receiver,
|
||||||
|
-- well-formed
|
||||||
is_valid = is_valid,
|
is_valid = is_valid,
|
||||||
|
-- packet properties
|
||||||
seq_num = seq_num,
|
seq_num = seq_num,
|
||||||
protocol = protocol,
|
protocol = protocol,
|
||||||
length = length,
|
length = length,
|
||||||
@ -116,9 +121,11 @@ function scada_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- MODBUS packet
|
-- MODBUS packet
|
||||||
|
-- modeled after MODBUS TCP packet, but length is not transmitted since these are tables (#data = length, easy)
|
||||||
function modbus_packet()
|
function modbus_packet()
|
||||||
local self = {
|
local self = {
|
||||||
frame = nil,
|
frame = nil,
|
||||||
|
raw = nil,
|
||||||
txn_id = txn_id,
|
txn_id = txn_id,
|
||||||
protocol = protocol,
|
protocol = protocol,
|
||||||
length = length,
|
length = length,
|
||||||
@ -128,13 +135,19 @@ function modbus_packet()
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- make a MODBUS packet
|
-- make a MODBUS packet
|
||||||
local make = function (txn_id, protocol, length, unit_id, func_code, data)
|
local make = function (txn_id, protocol, unit_id, func_code, data)
|
||||||
self.txn_id = txn_id
|
self.txn_id = txn_id
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.length = length
|
self.length = #data
|
||||||
self.unit_id = unit_id
|
self.unit_id = unit_id
|
||||||
self.func_code = func_code
|
self.func_code = func_code
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
-- populate raw array
|
||||||
|
self.raw = { self.txn_id, self.protocol, self.unit_id, self.func_code }
|
||||||
|
for i = 1, self.length do
|
||||||
|
table.insert(self.raw, data[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- decode a MODBUS packet from a SCADA frame
|
-- decode a MODBUS packet from a SCADA frame
|
||||||
@ -142,20 +155,28 @@ function modbus_packet()
|
|||||||
if frame then
|
if frame then
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
|
|
||||||
local data = frame.data()
|
if frame.protocol() == PROTOCOLS.MODBUS_TCP then
|
||||||
local size_ok = #data ~= 6
|
local size_ok = frame.length() >= 4
|
||||||
|
|
||||||
if size_ok then
|
if size_ok then
|
||||||
make(data[1], data[2], data[3], data[4], data[5], data[6])
|
local data = frame.data()
|
||||||
|
make(data[1], data[2], data[3], data[4], { table.unpack(data, 5, #data) })
|
||||||
end
|
end
|
||||||
|
|
||||||
return size_ok and self.protocol == comms.PROTOCOLS.MODBUS_TCP
|
return size_ok
|
||||||
|
else
|
||||||
|
log._debug("attempted MODBUS_TCP parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
|
return false
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log._debug("nil frame encountered", true)
|
log._debug("nil frame encountered", true)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get raw to send
|
||||||
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
-- get this packet
|
-- get this packet
|
||||||
local get = function ()
|
local get = function ()
|
||||||
return {
|
return {
|
||||||
@ -170,8 +191,12 @@ function modbus_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- construct
|
||||||
make = make,
|
make = make,
|
||||||
decode = decode,
|
decode = decode,
|
||||||
|
-- raw access
|
||||||
|
raw_sendable = raw_sendable,
|
||||||
|
-- formatted access
|
||||||
get = get
|
get = get
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -180,12 +205,14 @@ end
|
|||||||
function rplc_packet()
|
function rplc_packet()
|
||||||
local self = {
|
local self = {
|
||||||
frame = nil,
|
frame = nil,
|
||||||
|
raw = nil,
|
||||||
id = nil,
|
id = nil,
|
||||||
type = nil,
|
type = nil,
|
||||||
length = nil,
|
length = nil,
|
||||||
body = nil
|
body = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- check that type is known
|
||||||
local _rplc_type_valid = function ()
|
local _rplc_type_valid = function ()
|
||||||
return self.type == RPLC_TYPES.KEEP_ALIVE or
|
return self.type == RPLC_TYPES.KEEP_ALIVE or
|
||||||
self.type == RPLC_TYPES.LINK_REQ or
|
self.type == RPLC_TYPES.LINK_REQ or
|
||||||
@ -200,11 +227,18 @@ function rplc_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- make an RPLC packet
|
-- make an RPLC packet
|
||||||
local make = function (id, packet_type, length, data)
|
local make = function (id, packet_type, data)
|
||||||
|
-- packet accessor properties
|
||||||
self.id = id
|
self.id = id
|
||||||
self.type = packet_type
|
self.type = packet_type
|
||||||
self.length = length
|
self.length = #data
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
-- populate raw array
|
||||||
|
self.raw = { self.id, self.type }
|
||||||
|
for i = 1, #data do
|
||||||
|
table.insert(self.raw, data[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- decode an RPLC packet from a SCADA frame
|
-- decode an RPLC packet from a SCADA frame
|
||||||
@ -212,12 +246,12 @@ function rplc_packet()
|
|||||||
if frame then
|
if frame then
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
|
|
||||||
if frame.protocol() == comms.PROTOCOLS.RPLC then
|
if frame.protocol() == PROTOCOLS.RPLC then
|
||||||
local data = frame.data()
|
local ok = frame.length() >= 2
|
||||||
local ok = #data > 2
|
|
||||||
|
|
||||||
if ok then
|
if ok then
|
||||||
make(data[1], data[2], data[3], { table.unpack(data, 4, #data) })
|
local data = frame.data()
|
||||||
|
make(data[1], data[2], { table.unpack(data, 3, #data) })
|
||||||
ok = _rplc_type_valid()
|
ok = _rplc_type_valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -232,6 +266,10 @@ function rplc_packet()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get raw to send
|
||||||
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
|
-- get this packet
|
||||||
local get = function ()
|
local get = function ()
|
||||||
return {
|
return {
|
||||||
scada_frame = self.frame,
|
scada_frame = self.frame,
|
||||||
@ -243,8 +281,12 @@ function rplc_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- construct
|
||||||
make = make,
|
make = make,
|
||||||
decode = decode,
|
decode = decode,
|
||||||
|
-- raw access
|
||||||
|
raw_sendable = raw_sendable,
|
||||||
|
-- formatted access
|
||||||
get = get
|
get = get
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -253,11 +295,13 @@ end
|
|||||||
function mgmt_packet()
|
function mgmt_packet()
|
||||||
local self = {
|
local self = {
|
||||||
frame = nil,
|
frame = nil,
|
||||||
|
raw = nil,
|
||||||
type = nil,
|
type = nil,
|
||||||
length = nil,
|
length = nil,
|
||||||
data = nil
|
data = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- check that type is known
|
||||||
local _scada_type_valid = function ()
|
local _scada_type_valid = function ()
|
||||||
return self.type == SCADA_MGMT_TYPES.PING or
|
return self.type == SCADA_MGMT_TYPES.PING or
|
||||||
self.type == SCADA_MGMT_TYPES.SV_HEARTBEAT or
|
self.type == SCADA_MGMT_TYPES.SV_HEARTBEAT or
|
||||||
@ -267,10 +311,17 @@ function mgmt_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- make a SCADA management packet
|
-- make a SCADA management packet
|
||||||
local make = function (packet_type, length, data)
|
local make = function (packet_type, data)
|
||||||
|
-- packet accessor properties
|
||||||
self.type = packet_type
|
self.type = packet_type
|
||||||
self.length = length
|
self.length = #data
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
-- populate raw array
|
||||||
|
self.raw = { self.type }
|
||||||
|
for i = 1, #data do
|
||||||
|
table.insert(self.raw, data[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- decode a SCADA management packet from a SCADA frame
|
-- decode a SCADA management packet from a SCADA frame
|
||||||
@ -278,12 +329,12 @@ function mgmt_packet()
|
|||||||
if frame then
|
if frame then
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
|
|
||||||
if frame.protocol() == comms.PROTOCOLS.SCADA_MGMT then
|
if frame.protocol() == PROTOCOLS.SCADA_MGMT then
|
||||||
local data = frame.data()
|
local ok = #data >= 1
|
||||||
local ok = #data > 1
|
|
||||||
|
|
||||||
if ok then
|
if ok then
|
||||||
make(data[1], data[2], { table.unpack(data, 3, #data) })
|
local data = frame.data()
|
||||||
|
make(data[1], { table.unpack(data, 2, #data) })
|
||||||
ok = _scada_type_valid()
|
ok = _scada_type_valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -298,6 +349,10 @@ function mgmt_packet()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get raw to send
|
||||||
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
|
-- get this packet
|
||||||
local get = function ()
|
local get = function ()
|
||||||
return {
|
return {
|
||||||
scada_frame = self.frame,
|
scada_frame = self.frame,
|
||||||
@ -308,8 +363,12 @@ function mgmt_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- construct
|
||||||
make = make,
|
make = make,
|
||||||
decode = decode,
|
decode = decode,
|
||||||
|
-- raw access
|
||||||
|
raw_sendable = raw_sendable,
|
||||||
|
-- formatted access
|
||||||
get = get
|
get = get
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
@ -319,6 +378,7 @@ end
|
|||||||
function coord_packet()
|
function coord_packet()
|
||||||
local self = {
|
local self = {
|
||||||
frame = nil,
|
frame = nil,
|
||||||
|
raw = nil,
|
||||||
type = nil,
|
type = nil,
|
||||||
length = nil,
|
length = nil,
|
||||||
data = nil
|
data = nil
|
||||||
@ -330,10 +390,17 @@ function coord_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- make a coordinator packet
|
-- make a coordinator packet
|
||||||
local make = function (packet_type, length, data)
|
local make = function (packet_type, data)
|
||||||
|
-- packet accessor properties
|
||||||
self.type = packet_type
|
self.type = packet_type
|
||||||
self.length = length
|
self.length = #data
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
-- populate raw array
|
||||||
|
self.raw = { self.type }
|
||||||
|
for i = 1, #data do
|
||||||
|
table.insert(self.raw, data[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- decode a coordinator packet from a SCADA frame
|
-- decode a coordinator packet from a SCADA frame
|
||||||
@ -341,12 +408,12 @@ function coord_packet()
|
|||||||
if frame then
|
if frame then
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
|
|
||||||
if frame.protocol() == comms.PROTOCOLS.COORD_DATA then
|
if frame.protocol() == PROTOCOLS.COORD_DATA then
|
||||||
local data = frame.data()
|
local ok = #data >= 1
|
||||||
local ok = #data > 1
|
|
||||||
|
|
||||||
if ok then
|
if ok then
|
||||||
make(data[1], data[2], { table.unpack(data, 3, #data) })
|
local data = frame.data()
|
||||||
|
make(data[1], { table.unpack(data, 2, #data) })
|
||||||
ok = _coord_type_valid()
|
ok = _coord_type_valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -361,6 +428,10 @@ function coord_packet()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get raw to send
|
||||||
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
|
-- get this packet
|
||||||
local get = function ()
|
local get = function ()
|
||||||
return {
|
return {
|
||||||
scada_frame = self.frame,
|
scada_frame = self.frame,
|
||||||
@ -371,8 +442,91 @@ function coord_packet()
|
|||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
-- construct
|
||||||
make = make,
|
make = make,
|
||||||
decode = decode,
|
decode = decode,
|
||||||
|
-- raw access
|
||||||
|
raw_sendable = raw_sendable,
|
||||||
|
-- formatted access
|
||||||
|
get = get
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- coordinator API (CAPI) packet
|
||||||
|
-- @todo
|
||||||
|
function capi_packet()
|
||||||
|
local self = {
|
||||||
|
frame = nil,
|
||||||
|
raw = nil,
|
||||||
|
type = nil,
|
||||||
|
length = nil,
|
||||||
|
data = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
local _coord_type_valid = function ()
|
||||||
|
-- @todo
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- make a coordinator packet
|
||||||
|
local make = function (packet_type, data)
|
||||||
|
-- packet accessor properties
|
||||||
|
self.type = packet_type
|
||||||
|
self.length = #data
|
||||||
|
self.data = data
|
||||||
|
|
||||||
|
-- populate raw array
|
||||||
|
self.raw = { self.type }
|
||||||
|
for i = 1, #data do
|
||||||
|
table.insert(self.raw, data[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- decode a coordinator packet from a SCADA frame
|
||||||
|
local decode = function (frame)
|
||||||
|
if frame then
|
||||||
|
self.frame = frame
|
||||||
|
|
||||||
|
if frame.protocol() == PROTOCOLS.COORD_API then
|
||||||
|
local ok = #data >= 1
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
local data = frame.data()
|
||||||
|
make(data[1], { table.unpack(data, 2, #data) })
|
||||||
|
ok = _coord_type_valid()
|
||||||
|
end
|
||||||
|
|
||||||
|
return ok
|
||||||
|
else
|
||||||
|
log._debug("attempted COORD_API parse of incorrect protocol " .. frame.protocol(), true)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log._debug("nil frame encountered", true)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get raw to send
|
||||||
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
|
-- get this packet
|
||||||
|
local get = function ()
|
||||||
|
return {
|
||||||
|
scada_frame = self.frame,
|
||||||
|
type = self.type,
|
||||||
|
length = self.length,
|
||||||
|
data = self.data
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
-- construct
|
||||||
|
make = make,
|
||||||
|
decode = decode,
|
||||||
|
-- raw access
|
||||||
|
raw_sendable = raw_sendable,
|
||||||
|
-- formatted access
|
||||||
get = get
|
get = get
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user