#7 work on PLC session comms, bugfixes with comms, general supervisor bugfixes
This commit is contained in:
parent
19a4b3c0ef
commit
f7f723829c
@ -6,6 +6,11 @@ local PROTOCOLS = comms.PROTOCOLS
|
|||||||
local RPLC_TYPES = comms.RPLC_TYPES
|
local RPLC_TYPES = comms.RPLC_TYPES
|
||||||
local RPLC_LINKING = comms.RPLC_LINKING
|
local RPLC_LINKING = comms.RPLC_LINKING
|
||||||
|
|
||||||
|
local print = util.print
|
||||||
|
local println = util.println
|
||||||
|
local print_ts = util.print_ts
|
||||||
|
local println_ts = util.println_ts
|
||||||
|
|
||||||
-- Internal Safety System
|
-- Internal Safety System
|
||||||
-- identifies dangerous states and SCRAMs reactor if warranted
|
-- identifies dangerous states and SCRAMs reactor if warranted
|
||||||
-- autonomous from main SCADA supervisor/coordinator control
|
-- autonomous from main SCADA supervisor/coordinator control
|
||||||
@ -167,6 +172,10 @@ function iss_init(reactor)
|
|||||||
return self.tripped, status, first_trip
|
return self.tripped, status, first_trip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- get the ISS status
|
||||||
|
local status = function () return self.cache end
|
||||||
|
local is_tripped = function () return self.tripped end
|
||||||
|
|
||||||
-- reset the ISS
|
-- reset the ISS
|
||||||
local reset = function ()
|
local reset = function ()
|
||||||
self.timed_out = false
|
self.timed_out = false
|
||||||
@ -174,17 +183,13 @@ function iss_init(reactor)
|
|||||||
self.trip_cause = ""
|
self.trip_cause = ""
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the ISS status
|
|
||||||
local status = function ()
|
|
||||||
return self.cache
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
reconnect_reactor = reconnect_reactor,
|
reconnect_reactor = reconnect_reactor,
|
||||||
trip_timeout = trip_timeout,
|
trip_timeout = trip_timeout,
|
||||||
check = check,
|
check = check,
|
||||||
reset = reset,
|
status = status,
|
||||||
status = status
|
is_tripped = is_tripped,
|
||||||
|
reset = reset
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -225,7 +230,6 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
|
|
||||||
-- variable reactor status information, excluding heating rate
|
-- variable reactor status information, excluding heating rate
|
||||||
local _reactor_status = function ()
|
local _reactor_status = function ()
|
||||||
ppm.clear_fault()
|
|
||||||
return {
|
return {
|
||||||
self.reactor.getStatus(),
|
self.reactor.getStatus(),
|
||||||
self.reactor.getBurnRate(),
|
self.reactor.getBurnRate(),
|
||||||
@ -249,13 +253,14 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
self.reactor.getHeatedCoolant()['amount'],
|
self.reactor.getHeatedCoolant()['amount'],
|
||||||
self.reactor.getHeatedCoolantNeeded(),
|
self.reactor.getHeatedCoolantNeeded(),
|
||||||
self.reactor.getHeatedCoolantFilledPercentage()
|
self.reactor.getHeatedCoolantFilledPercentage()
|
||||||
}, ppm.faulted()
|
}, self.reactor.__p_is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
local _update_status_cache = function ()
|
local _update_status_cache = function ()
|
||||||
local status, faulted = _reactor_status()
|
local status, faulted = _reactor_status()
|
||||||
local changed = false
|
local changed = false
|
||||||
|
|
||||||
|
if self.status_cache ~= nil then
|
||||||
if not faulted then
|
if not faulted then
|
||||||
for i = 1, #status do
|
for i = 1, #status do
|
||||||
if status[i] ~= self.status_cache[i] then
|
if status[i] ~= self.status_cache[i] then
|
||||||
@ -264,6 +269,9 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
changed = true
|
||||||
|
end
|
||||||
|
|
||||||
if changed then
|
if changed then
|
||||||
self.status_cache = status
|
self.status_cache = status
|
||||||
@ -285,8 +293,6 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
-- send structure properties (these should not change)
|
-- send structure properties (these should not change)
|
||||||
-- (server will cache these)
|
-- (server will cache these)
|
||||||
local _send_struct = function ()
|
local _send_struct = function ()
|
||||||
ppm.clear_fault()
|
|
||||||
|
|
||||||
local mek_data = {
|
local mek_data = {
|
||||||
self.reactor.getHeatCapacity(),
|
self.reactor.getHeatCapacity(),
|
||||||
self.reactor.getFuelAssemblies(),
|
self.reactor.getFuelAssemblies(),
|
||||||
@ -298,7 +304,7 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
self.reactor.getMaxBurnRate()
|
self.reactor.getMaxBurnRate()
|
||||||
}
|
}
|
||||||
|
|
||||||
if not ppm.is_faulted() then
|
if not self.reactor.__p_is_faulted() then
|
||||||
_send(RPLC_TYPES.MEK_STRUCT, mek_data)
|
_send(RPLC_TYPES.MEK_STRUCT, mek_data)
|
||||||
else
|
else
|
||||||
log._error("failed to send structure: PPM fault")
|
log._error("failed to send structure: PPM fault")
|
||||||
@ -323,10 +329,48 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
_update_status_cache()
|
_update_status_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- attempt to establish link with supervisor
|
||||||
|
local send_link_req = function ()
|
||||||
|
_send(RPLC_TYPES.LINK_REQ, { self.id })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- send live status information
|
||||||
|
local send_status = function (degraded)
|
||||||
|
local mek_data = nil
|
||||||
|
|
||||||
|
if _update_status_cache() then
|
||||||
|
mek_data = self.status_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
local sys_status = {
|
||||||
|
util.time(),
|
||||||
|
(not self.scrammed),
|
||||||
|
iss.is_tripped(),
|
||||||
|
degraded,
|
||||||
|
self.reactor.getHeatingRate(),
|
||||||
|
mek_data
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(RPLC_TYPES.STATUS, sys_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
local send_iss_status = function ()
|
||||||
|
_send(RPLC_TYPES.ISS_STATUS, iss.status())
|
||||||
|
end
|
||||||
|
|
||||||
|
local send_iss_alarm = function (cause)
|
||||||
|
local iss_alarm = {
|
||||||
|
cause,
|
||||||
|
iss.status()
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(RPLC_TYPES.ISS_ALARM, iss_alarm)
|
||||||
|
end
|
||||||
|
|
||||||
-- parse an RPLC packet
|
-- parse an RPLC 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
|
||||||
local s_pkt = scada_packet()
|
local s_pkt = comms.scada_packet()
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
-- parse packet as generic SCADA packet
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
s_pkt.receive(side, sender, reply_to, message, distance)
|
||||||
@ -358,7 +402,7 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
self.r_seq_num = packet.scada_frame.seq_num()
|
self.r_seq_num = packet.scada_frame.seq_num()
|
||||||
elseif self.r_seq_num >= packet.scada_frame.seq_num() then
|
elseif self.linked and self.r_seq_num >= packet.scada_frame.seq_num() then
|
||||||
log._warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
log._warning("sequence out-of-order: last = " .. self.r_seq_num .. ", new = " .. packet.scada_frame.seq_num())
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
@ -388,19 +432,19 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
|
|
||||||
if link_ack == RPLC_LINKING.ALLOW then
|
if link_ack == RPLC_LINKING.ALLOW then
|
||||||
_send_struct()
|
_send_struct()
|
||||||
send_status()
|
send_status(plc_state.degraded)
|
||||||
log._debug("re-sent initial status data")
|
log._debug("re-sent initial status data")
|
||||||
elseif link_ack == RPLC_LINKING.DENY then
|
elseif link_ack == RPLC_LINKING.DENY then
|
||||||
-- @todo: make sure this doesn't become a MITM security risk
|
-- @todo: make sure this doesn't become a MITM security risk
|
||||||
print_ts("received unsolicited link denial, unlinking\n")
|
println_ts("received unsolicited link denial, unlinking")
|
||||||
log._debug("unsolicited rplc link request denied")
|
log._debug("unsolicited RPLC link request denied")
|
||||||
elseif link_ack == RPLC_LINKING.COLLISION then
|
elseif link_ack == RPLC_LINKING.COLLISION then
|
||||||
-- @todo: make sure this doesn't become a MITM security risk
|
-- @todo: make sure this doesn't become a MITM security risk
|
||||||
print_ts("received unsolicited link collision, unlinking\n")
|
println_ts("received unsolicited link collision, unlinking")
|
||||||
log._warning("unsolicited rplc link request collision")
|
log._warning("unsolicited RPLC link request collision")
|
||||||
else
|
else
|
||||||
print_ts("invalid unsolicited link response\n")
|
println_ts("invalid unsolicited link response")
|
||||||
log._error("unsolicited unknown rplc link request response")
|
log._error("unsolicited unknown RPLC link request response")
|
||||||
end
|
end
|
||||||
|
|
||||||
self.linked = link_ack == RPLC_LINKING.ALLOW
|
self.linked = link_ack == RPLC_LINKING.ALLOW
|
||||||
@ -452,22 +496,25 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
local link_ack = packet.data[1]
|
local link_ack = packet.data[1]
|
||||||
|
|
||||||
if link_ack == RPLC_LINKING.ALLOW then
|
if link_ack == RPLC_LINKING.ALLOW then
|
||||||
print_ts("...linked!\n")
|
println_ts("linked!")
|
||||||
log._debug("rplc link request approved")
|
log._debug("RPLC link request approved")
|
||||||
|
|
||||||
|
-- reset remote sequence number
|
||||||
|
self.r_seq_num = nil
|
||||||
|
|
||||||
_send_struct()
|
_send_struct()
|
||||||
send_status()
|
send_status(plc_state.degraded)
|
||||||
|
|
||||||
log._debug("sent initial status data")
|
log._debug("sent initial status data")
|
||||||
elseif link_ack == RPLC_LINKING.DENY then
|
elseif link_ack == RPLC_LINKING.DENY then
|
||||||
print_ts("...denied, retrying...\n")
|
println_ts("link request denied, retrying...")
|
||||||
log._debug("rplc link request denied")
|
log._debug("RPLC link request denied")
|
||||||
elseif link_ack == RPLC_LINKING.COLLISION then
|
elseif link_ack == RPLC_LINKING.COLLISION then
|
||||||
print_ts("reactor PLC ID collision (check config), retrying...\n")
|
println_ts("reactor PLC ID collision (check config), retrying...")
|
||||||
log._warning("rplc link request collision")
|
log._warning("RPLC link request collision")
|
||||||
else
|
else
|
||||||
print_ts("invalid link response, bad channel? retrying...\n")
|
println_ts("invalid link response, bad channel? retrying...")
|
||||||
log._error("unknown rplc link request response")
|
log._error("unknown RPLC link request response")
|
||||||
end
|
end
|
||||||
|
|
||||||
self.linked = link_ack == RPLC_LINKING.ALLOW
|
self.linked = link_ack == RPLC_LINKING.ALLOW
|
||||||
@ -480,46 +527,6 @@ function comms_init(id, modem, local_port, server_port, reactor, iss)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- attempt to establish link with supervisor
|
|
||||||
local send_link_req = function ()
|
|
||||||
_send(RPLC_TYPES.LINK_REQ, {})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- send live status information
|
|
||||||
-- overridden : if ISS force disabled reactor
|
|
||||||
-- degraded : if PLC status is degraded
|
|
||||||
local send_status = function (overridden, degraded)
|
|
||||||
local mek_data = nil
|
|
||||||
|
|
||||||
if _update_status_cache() then
|
|
||||||
mek_data = self.status_cache
|
|
||||||
end
|
|
||||||
|
|
||||||
local sys_status = {
|
|
||||||
util.time(),
|
|
||||||
(not self.scrammed),
|
|
||||||
overridden,
|
|
||||||
degraded,
|
|
||||||
self.reactor.getHeatingRate(),
|
|
||||||
mek_data
|
|
||||||
}
|
|
||||||
|
|
||||||
_send(RPLC_TYPES.STATUS, sys_status)
|
|
||||||
end
|
|
||||||
|
|
||||||
local send_iss_status = function ()
|
|
||||||
_send(RPLC_TYPES.ISS_STATUS, iss.status())
|
|
||||||
end
|
|
||||||
|
|
||||||
local send_iss_alarm = function (cause)
|
|
||||||
local iss_alarm = {
|
|
||||||
cause,
|
|
||||||
iss.status()
|
|
||||||
}
|
|
||||||
|
|
||||||
_send(RPLC_TYPES.ISS_ALARM, iss_alarm)
|
|
||||||
end
|
|
||||||
|
|
||||||
local is_scrammed = function () return self.scrammed end
|
local is_scrammed = function () return self.scrammed end
|
||||||
local is_linked = function () return self.linked end
|
local is_linked = function () return self.linked end
|
||||||
local unlink = function () self.linked = false end
|
local unlink = function () self.linked = false end
|
||||||
|
|||||||
@ -11,7 +11,7 @@ os.loadAPI("config.lua")
|
|||||||
os.loadAPI("plc.lua")
|
os.loadAPI("plc.lua")
|
||||||
os.loadAPI("threads.lua")
|
os.loadAPI("threads.lua")
|
||||||
|
|
||||||
local R_PLC_VERSION = "alpha-v0.3.1"
|
local R_PLC_VERSION = "alpha-v0.3.3"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
|
|||||||
@ -11,7 +11,7 @@ local println_ts = util.println_ts
|
|||||||
local async_wait = util.async_wait
|
local async_wait = util.async_wait
|
||||||
|
|
||||||
local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks)
|
local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks)
|
||||||
local ISS_CLOCK = 0.5 -- (2Hz, 10 ticks)
|
local ISS_CLOCK = 0.25 -- (4Hz, 5 ticks) however this is AFTER all the ISS checks, so it is a pause between calls, not start-to-start
|
||||||
|
|
||||||
local ISS_EVENT = {
|
local ISS_EVENT = {
|
||||||
SCRAM = 1,
|
SCRAM = 1,
|
||||||
@ -28,7 +28,7 @@ function thread__main(shared_memory, init)
|
|||||||
local LINK_TICKS = 4
|
local LINK_TICKS = 4
|
||||||
|
|
||||||
local loop_clock = nil
|
local loop_clock = nil
|
||||||
local ticks_to_update = LINK_TICKS -- start by linking
|
local ticks_to_update = 0
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local networked = shared_memory.networked
|
local networked = shared_memory.networked
|
||||||
@ -40,7 +40,7 @@ function thread__main(shared_memory, init)
|
|||||||
local conn_watchdog = shared_memory.system.conn_watchdog
|
local conn_watchdog = shared_memory.system.conn_watchdog
|
||||||
|
|
||||||
-- debug
|
-- debug
|
||||||
-- local last_update = util.time()
|
local last_update = util.time()
|
||||||
|
|
||||||
-- event loop
|
-- event loop
|
||||||
while true do
|
while true do
|
||||||
@ -55,26 +55,25 @@ function thread__main(shared_memory, init)
|
|||||||
|
|
||||||
-- send updated data
|
-- send updated data
|
||||||
if not plc_state.no_modem then
|
if not plc_state.no_modem then
|
||||||
|
|
||||||
if plc_comms.is_linked() then
|
if plc_comms.is_linked() then
|
||||||
async_wait(function ()
|
async_wait(function ()
|
||||||
plc_comms.send_status(iss_tripped, plc_state.degraded)
|
plc_comms.send_status(iss_tripped, plc_state.degraded)
|
||||||
plc_comms.send_iss_status()
|
plc_comms.send_iss_status()
|
||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
ticks_to_update = ticks_to_update - 1
|
if ticks_to_update == 0 then
|
||||||
|
|
||||||
if ticks_to_update <= 0 then
|
|
||||||
plc_comms.send_link_req()
|
plc_comms.send_link_req()
|
||||||
ticks_to_update = LINK_TICKS
|
ticks_to_update = LINK_TICKS
|
||||||
|
else
|
||||||
|
ticks_to_update = ticks_to_update - 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- debug
|
-- debug
|
||||||
-- print(util.time() - last_update)
|
print(util.time() - last_update)
|
||||||
-- println("ms")
|
println("ms")
|
||||||
-- last_update = util.time()
|
last_update = util.time()
|
||||||
end
|
end
|
||||||
elseif event == "modem_message" and networked and not plc_state.no_modem then
|
elseif event == "modem_message" and networked and not plc_state.no_modem then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
@ -82,8 +81,10 @@ function thread__main(shared_memory, init)
|
|||||||
conn_watchdog.feed()
|
conn_watchdog.feed()
|
||||||
|
|
||||||
-- handle the packet (plc_state passed to allow clearing SCRAM flag)
|
-- handle the packet (plc_state passed to allow clearing SCRAM flag)
|
||||||
local packet = plc_comms.parse_packet(p1, p2, p3, p4, p5)
|
async_wait(function ()
|
||||||
async_wait(function () plc_comms.handle_packet(packet, plc_state) end)
|
local packet = plc_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
plc_comms.handle_packet(packet, plc_state)
|
||||||
|
end)
|
||||||
elseif event == "timer" and networked and param1 == conn_watchdog.get_timer() then
|
elseif event == "timer" and networked and param1 == conn_watchdog.get_timer() then
|
||||||
-- haven't heard from server recently? shutdown reactor
|
-- haven't heard from server recently? shutdown reactor
|
||||||
plc_comms.unlink()
|
plc_comms.unlink()
|
||||||
@ -169,7 +170,7 @@ function thread__main(shared_memory, init)
|
|||||||
elseif event == "clock_start" then
|
elseif event == "clock_start" then
|
||||||
-- start loop clock
|
-- start loop clock
|
||||||
loop_clock = os.startTimer(MAIN_CLOCK)
|
loop_clock = os.startTimer(MAIN_CLOCK)
|
||||||
log._debug("loop clock started")
|
log._debug("main thread started")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
@ -208,9 +209,6 @@ function thread__iss(shared_memory)
|
|||||||
local reactor = shared_memory.plc_devices.reactor
|
local reactor = shared_memory.plc_devices.reactor
|
||||||
|
|
||||||
if event == "timer" and param1 == loop_clock then
|
if event == "timer" and param1 == loop_clock then
|
||||||
-- start next clock timer
|
|
||||||
loop_clock = os.startTimer(ISS_CLOCK)
|
|
||||||
|
|
||||||
-- ISS checks
|
-- ISS checks
|
||||||
if plc_state.init_ok then
|
if plc_state.init_ok then
|
||||||
-- if we tried to SCRAM but failed, keep trying
|
-- if we tried to SCRAM but failed, keep trying
|
||||||
@ -244,6 +242,10 @@ function thread__iss(shared_memory)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- start next clock timer after all the long operations
|
||||||
|
-- otherwise we will never get around to other events
|
||||||
|
loop_clock = os.startTimer(ISS_CLOCK)
|
||||||
|
|
||||||
-- debug
|
-- debug
|
||||||
-- print(util.time() - last_update)
|
-- print(util.time() - last_update)
|
||||||
-- println("ms")
|
-- println("ms")
|
||||||
@ -270,13 +272,13 @@ function thread__iss(shared_memory)
|
|||||||
-- watchdog tripped
|
-- watchdog tripped
|
||||||
plc_state.scram = true
|
plc_state.scram = true
|
||||||
iss.trip_timeout()
|
iss.trip_timeout()
|
||||||
println_ts("server timeout, reactor disabled")
|
println_ts("server timeout")
|
||||||
log._warning("server timeout, reactor disabled")
|
log._warning("server timeout")
|
||||||
end
|
end
|
||||||
elseif event == "clock_start" then
|
elseif event == "clock_start" then
|
||||||
-- start loop clock
|
-- start loop clock
|
||||||
loop_clock = os.startTimer(ISS_CLOCK)
|
loop_clock = os.startTimer(ISS_CLOCK)
|
||||||
log._debug("loop clock started")
|
log._debug("iss thread started")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
|
|||||||
@ -17,7 +17,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.3.1"
|
local RTU_VERSION = "alpha-v0.3.2"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -233,7 +233,7 @@ while true do
|
|||||||
elseif event == "modem_message" then
|
elseif event == "modem_message" then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
local link_ref = { linked = linked }
|
local link_ref = { linked = linked }
|
||||||
local packet = rtu_comms.parse_packet(p1, p2, p3, p4, p5)
|
local packet = rtu_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
|
||||||
async_wait(function () rtu_comms.handle_packet(packet, units, link_ref) end)
|
async_wait(function () rtu_comms.handle_packet(packet, units, link_ref) end)
|
||||||
|
|
||||||
|
|||||||
@ -74,6 +74,7 @@ function scada_packet()
|
|||||||
|
|
||||||
self.raw = self.modem_msg_in.msg
|
self.raw = self.modem_msg_in.msg
|
||||||
|
|
||||||
|
if type(self.raw) == "table" then
|
||||||
if #self.raw >= 3 then
|
if #self.raw >= 3 then
|
||||||
self.valid = true
|
self.valid = true
|
||||||
self.seq_num = self.raw[1]
|
self.seq_num = self.raw[1]
|
||||||
@ -81,6 +82,7 @@ function scada_packet()
|
|||||||
self.length = #self.raw[3]
|
self.length = #self.raw[3]
|
||||||
self.payload = self.raw[3]
|
self.payload = self.raw[3]
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return self.valid
|
return self.valid
|
||||||
end
|
end
|
||||||
@ -90,8 +92,8 @@ function scada_packet()
|
|||||||
local modem_event = function () return self.modem_msg_in end
|
local modem_event = function () return self.modem_msg_in end
|
||||||
local raw_sendable = function () return self.raw end
|
local raw_sendable = function () return self.raw end
|
||||||
|
|
||||||
local sender = function () return self.s_port end
|
local local_port = function () return self.modem_msg_in.s_port end
|
||||||
local receiver = function () return self.r_port end
|
local remote_port = function () return self.modem_msg_in.r_port end
|
||||||
|
|
||||||
local is_valid = function () return self.valid end
|
local is_valid = function () return self.valid end
|
||||||
|
|
||||||
@ -107,9 +109,9 @@ function scada_packet()
|
|||||||
-- raw access
|
-- raw access
|
||||||
modem_event = modem_event,
|
modem_event = modem_event,
|
||||||
raw_sendable = raw_sendable,
|
raw_sendable = raw_sendable,
|
||||||
-- sender/receiver
|
-- ports
|
||||||
sender = sender,
|
local_port = local_port,
|
||||||
receiver = receiver,
|
remote_port = remote_port,
|
||||||
-- well-formed
|
-- well-formed
|
||||||
is_valid = is_valid,
|
is_valid = is_valid,
|
||||||
-- packet properties
|
-- packet properties
|
||||||
|
|||||||
@ -23,11 +23,11 @@ function new()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local push_packet = function (message)
|
local push_packet = function (message)
|
||||||
push(TYPE.PACKET, message)
|
_push(TYPE.PACKET, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
local push_command = function (message)
|
local push_command = function (message)
|
||||||
push(TYPE.COMMAND, message)
|
_push(TYPE.COMMAND, message)
|
||||||
end
|
end
|
||||||
|
|
||||||
local pop = function ()
|
local pop = function ()
|
||||||
|
|||||||
@ -70,6 +70,7 @@ function get_reactor_session(reactor)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function establish_plc_session(local_port, remote_port, for_reactor)
|
function establish_plc_session(local_port, remote_port, for_reactor)
|
||||||
|
util.println(remote_port)
|
||||||
if get_reactor_session(for_reactor) == nil then
|
if get_reactor_session(for_reactor) == nil then
|
||||||
local plc_s = {
|
local plc_s = {
|
||||||
open = true,
|
open = true,
|
||||||
@ -81,9 +82,12 @@ function establish_plc_session(local_port, remote_port, for_reactor)
|
|||||||
instance = nil
|
instance = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
plc_s.instance = plc.new_session(next_plc_id, plc_s.in_queue, plc_s.out_queue)
|
plc_s.instance = plc.new_session(self.next_plc_id, for_reactor, plc_s.in_queue, plc_s.out_queue)
|
||||||
table.insert(self.plc_sessions, plc_s)
|
table.insert(self.plc_sessions, plc_s)
|
||||||
next_plc_id = next_plc_id + 1
|
|
||||||
|
log._debug("established new PLC session to " .. remote_port .. " with ID " .. self.next_plc_id)
|
||||||
|
|
||||||
|
self.next_plc_id = self.next_plc_id + 1
|
||||||
|
|
||||||
-- success
|
-- success
|
||||||
return plc_s.instance.get_id()
|
return plc_s.instance.get_id()
|
||||||
@ -129,7 +133,7 @@ local function _iterate(sessions)
|
|||||||
while not session.out_queue.empty() do
|
while not session.out_queue.empty() do
|
||||||
local msg = session.out_queue.pop()
|
local msg = session.out_queue.pop()
|
||||||
if msg.qtype == mqueue.TYPE.PACKET then
|
if msg.qtype == mqueue.TYPE.PACKET then
|
||||||
self.modem.transmit(self.r_port, self.l_port, msg.message.raw_sendable())
|
self.modem.transmit(session.r_port, session.l_port, msg.message.raw_sendable())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -163,7 +167,7 @@ local function _free_closed(sessions)
|
|||||||
end
|
end
|
||||||
move_to = move_to + 1
|
move_to = move_to + 1
|
||||||
else
|
else
|
||||||
log._debug("free'ing closing session " .. session.instance.get_id() .. " on remote port " .. session.r_port)
|
log._debug("free'ing closed session " .. session.instance.get_id() .. " on remote port " .. session.r_port)
|
||||||
sessions[i] = nil
|
sessions[i] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -18,7 +18,7 @@ os.loadAPI("session/svsessions.lua")
|
|||||||
|
|
||||||
os.loadAPI("supervisor.lua")
|
os.loadAPI("supervisor.lua")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "alpha-v0.1.4"
|
local SUPERVISOR_VERSION = "alpha-v0.1.5"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -93,7 +93,7 @@ while true do
|
|||||||
svsessions.check_all_watchdogs(param1)
|
svsessions.check_all_watchdogs(param1)
|
||||||
elseif event == "modem_message" then
|
elseif event == "modem_message" then
|
||||||
-- got a packet
|
-- got a packet
|
||||||
local packet = superv_comms.parse_packet(p1, p2, p3, p4, p5)
|
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
superv_comms.handle_packet(packet)
|
superv_comms.handle_packet(packet)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
local PROTOCOLS = comms.PROTOCOLS
|
local PROTOCOLS = comms.PROTOCOLS
|
||||||
local RPLC_TYPES = comms.RPLC_TYPES
|
local RPLC_TYPES = comms.RPLC_TYPES
|
||||||
|
local RPLC_LINKING = comms.RPLC_LINKING
|
||||||
local SCADA_MGMT_TYPES = comms.SCADA_MGMT_TYPES
|
local SCADA_MGMT_TYPES = comms.SCADA_MGMT_TYPES
|
||||||
local RTU_ADVERT_TYPES = comms.RTU_ADVERT_TYPES
|
local RTU_ADVERT_TYPES = comms.RTU_ADVERT_TYPES
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ function superv_comms(num_reactors, modem, dev_listen, coord_listen)
|
|||||||
-- parse a packet
|
-- parse a 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
|
||||||
local s_pkt = scada_packet()
|
local s_pkt = comms.scada_packet()
|
||||||
|
|
||||||
-- parse packet as generic SCADA packet
|
-- parse packet as generic SCADA packet
|
||||||
s_pkt.receive(side, sender, reply_to, message, distance)
|
s_pkt.receive(side, sender, reply_to, message, distance)
|
||||||
@ -104,21 +105,22 @@ function superv_comms(num_reactors, modem, dev_listen, coord_listen)
|
|||||||
|
|
||||||
local handle_packet = function(packet)
|
local handle_packet = function(packet)
|
||||||
if packet ~= nil then
|
if packet ~= nil then
|
||||||
local sender = packet.scada_frame.sender()
|
local l_port = packet.scada_frame.local_port()
|
||||||
local receiver = packet.scada_frame.receiver()
|
local r_port = packet.scada_frame.remote_port()
|
||||||
local protocol = packet.scada_frame.protocol()
|
local protocol = packet.scada_frame.protocol()
|
||||||
|
|
||||||
-- device (RTU/PLC) listening channel
|
-- device (RTU/PLC) listening channel
|
||||||
if receiver == self.dev_listen then
|
if l_port == self.dev_listen then
|
||||||
if protocol == PROTOCOLS.MODBUS_TCP then
|
if protocol == PROTOCOLS.MODBUS_TCP then
|
||||||
-- MODBUS response
|
-- MODBUS response
|
||||||
elseif protocol == PROTOCOLS.RPLC then
|
elseif protocol == PROTOCOLS.RPLC then
|
||||||
-- reactor PLC packet
|
-- reactor PLC packet
|
||||||
local session = svsessions.find_session(SESSION_TYPE.PLC_SESSION, sender)
|
local session = svsessions.find_session(SESSION_TYPE.PLC_SESSION, r_port)
|
||||||
if session then
|
if session then
|
||||||
if packet.type == RPLC_TYPES.LINK_REQ then
|
if packet.type == RPLC_TYPES.LINK_REQ then
|
||||||
-- new device on this port? that's a collision
|
-- new device on this port? that's a collision
|
||||||
_send_plc_linking(sender, { RPLC_LINKING.COLLISION })
|
log._debug("PLC_LNK: request from existing connection received on " .. r_port .. ", responding with collision")
|
||||||
|
_send_plc_linking(r_port, { RPLC_LINKING.COLLISION })
|
||||||
else
|
else
|
||||||
-- pass the packet onto the session handler
|
-- pass the packet onto the session handler
|
||||||
session.in_queue.push_packet(packet)
|
session.in_queue.push_packet(packet)
|
||||||
@ -126,18 +128,25 @@ function superv_comms(num_reactors, modem, dev_listen, coord_listen)
|
|||||||
else
|
else
|
||||||
-- unknown session, is this a linking request?
|
-- unknown session, is this a linking request?
|
||||||
if packet.type == RPLC_TYPES.LINK_REQ then
|
if packet.type == RPLC_TYPES.LINK_REQ then
|
||||||
|
if packet.length == 1 then
|
||||||
-- this is a linking request
|
-- this is a linking request
|
||||||
local plc_id = svsessions.establish_plc_session(sender)
|
local plc_id = svsessions.establish_plc_session(l_port, r_port, packet.data[1])
|
||||||
if plc_id == false then
|
if plc_id == false then
|
||||||
-- reactor already has a PLC assigned
|
-- reactor already has a PLC assigned
|
||||||
_send_plc_linking(sender, { RPLC_LINKING.COLLISION })
|
log._debug("PLC_LNK: assignment collision with reactor " .. packet.data[1])
|
||||||
|
_send_plc_linking(r_port, { RPLC_LINKING.COLLISION })
|
||||||
else
|
else
|
||||||
-- got an ID; assigned to a reactor successfully
|
-- got an ID; assigned to a reactor successfully
|
||||||
_send_plc_linking(sender, { RPLC_LINKING.ALLOW })
|
log._debug("PLC_LNK: allowed for device at " .. r_port)
|
||||||
|
_send_plc_linking(r_port, { RPLC_LINKING.ALLOW })
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log._debug("PLC_LNK: new linking packet length mismatch")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- force a re-link
|
-- force a re-link
|
||||||
_send_plc_linking(sender, { RPLC_LINKING.DENY })
|
log._debug("PLC_LNK: no session but not a link, force relink")
|
||||||
|
_send_plc_linking(r_port, { RPLC_LINKING.DENY })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif protocol == PROTOCOLS.SCADA_MGMT then
|
elseif protocol == PROTOCOLS.SCADA_MGMT then
|
||||||
@ -146,7 +155,7 @@ function superv_comms(num_reactors, modem, dev_listen, coord_listen)
|
|||||||
log._debug("illegal packet type " .. protocol .. " on device listening channel")
|
log._debug("illegal packet type " .. protocol .. " on device listening channel")
|
||||||
end
|
end
|
||||||
-- coordinator listening channel
|
-- coordinator listening channel
|
||||||
elseif receiver == self.coord_listen then
|
elseif l_port == self.coord_listen then
|
||||||
if protocol == PROTOCOLS.SCADA_MGMT then
|
if protocol == PROTOCOLS.SCADA_MGMT then
|
||||||
-- SCADA management packet
|
-- SCADA management packet
|
||||||
elseif protocol == PROTOCOLS.COORD_DATA then
|
elseif protocol == PROTOCOLS.COORD_DATA then
|
||||||
@ -155,7 +164,7 @@ function superv_comms(num_reactors, modem, dev_listen, coord_listen)
|
|||||||
log._debug("illegal packet type " .. protocol .. " on coordinator listening channel")
|
log._debug("illegal packet type " .. protocol .. " on coordinator listening channel")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log._error("received packet on unused channel " .. receiver, true)
|
log._error("received packet on unused channel " .. l_port, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user