mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#124 debug stack trace on error
This commit is contained in:
parent
e679b5a25a
commit
9761228b8e
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -13,6 +13,7 @@
|
|||||||
"window",
|
"window",
|
||||||
"read",
|
"read",
|
||||||
"periphemu",
|
"periphemu",
|
||||||
"mekanismEnergyHelper"
|
"mekanismEnergyHelper",
|
||||||
|
"_HOST"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcallbackdsp = require("scada-common.tcallbackdsp")
|
local tcallbackdsp = require("scada-common.tcallbackdsp")
|
||||||
@ -16,7 +17,7 @@ local config = require("coordinator.config")
|
|||||||
local coordinator = require("coordinator.coordinator")
|
local coordinator = require("coordinator.coordinator")
|
||||||
local renderer = require("coordinator.renderer")
|
local renderer = require("coordinator.renderer")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "alpha-v0.6.11"
|
local COORDINATOR_VERSION = "alpha-v0.6.12"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -57,262 +58,272 @@ log.info("BOOTING coordinator.startup " .. COORDINATOR_VERSION)
|
|||||||
log.info("========================================")
|
log.info("========================================")
|
||||||
println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<")
|
println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<")
|
||||||
|
|
||||||
----------------------------------------
|
crash.set_env("coordinator", COORDINATOR_VERSION)
|
||||||
-- system startup
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
-- mount connected devices
|
|
||||||
ppm.mount_all()
|
|
||||||
|
|
||||||
-- setup monitors
|
|
||||||
local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS)
|
|
||||||
if not configured or monitors == nil then
|
|
||||||
println("boot> monitor setup failed")
|
|
||||||
log.fatal("monitor configuration failed")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
log.info("monitors ready, dmesg output incoming...")
|
|
||||||
|
|
||||||
-- init renderer
|
|
||||||
renderer.set_displays(monitors)
|
|
||||||
renderer.reset(config.RECOLOR)
|
|
||||||
renderer.init_dmesg()
|
|
||||||
|
|
||||||
log_graphics("displays connected and reset")
|
|
||||||
log_sys("system start on " .. os.date("%c"))
|
|
||||||
log_boot("starting " .. COORDINATOR_VERSION)
|
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- setup communications
|
-- main application
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
-- get the communications modem
|
local function main()
|
||||||
local modem = ppm.get_wireless_modem()
|
----------------------------------------
|
||||||
if modem == nil then
|
-- system startup
|
||||||
log_comms("wireless modem not found")
|
----------------------------------------
|
||||||
println("boot> wireless modem not found")
|
|
||||||
log.fatal("no wireless modem on startup")
|
|
||||||
return
|
|
||||||
else
|
|
||||||
log_comms("wireless modem connected")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- create connection watchdog
|
-- mount connected devices
|
||||||
local conn_watchdog = util.new_watchdog(5)
|
ppm.mount_all()
|
||||||
conn_watchdog.cancel()
|
|
||||||
log.debug("boot> conn watchdog created")
|
|
||||||
|
|
||||||
-- start comms, open all channels
|
-- setup monitors
|
||||||
local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.SCADA_SV_PORT, config.SCADA_SV_LISTEN, config.SCADA_API_LISTEN, conn_watchdog)
|
local configured, monitors = coordinator.configure_monitors(config.NUM_UNITS)
|
||||||
log.debug("boot> comms init")
|
if not configured or monitors == nil then
|
||||||
log_comms("comms initialized")
|
println("boot> monitor setup failed")
|
||||||
|
log.fatal("monitor configuration failed")
|
||||||
-- base loop clock (2Hz, 10 ticks)
|
return
|
||||||
local MAIN_CLOCK = 0.5
|
|
||||||
local loop_clock = util.new_clock(MAIN_CLOCK)
|
|
||||||
|
|
||||||
----------------------------------------
|
|
||||||
-- connect to the supervisor
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
-- attempt to connect to the supervisor or exit
|
|
||||||
local function init_connect_sv()
|
|
||||||
local tick_waiting, task_done = log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SCADA_SV_PORT)
|
|
||||||
|
|
||||||
-- attempt to establish a connection with the supervisory computer
|
|
||||||
if not coord_comms.sv_connect(60, tick_waiting, task_done) then
|
|
||||||
log_comms("supervisor connection failed")
|
|
||||||
log.fatal("failed to connect to supervisor")
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
log.info("monitors ready, dmesg output incoming...")
|
||||||
end
|
|
||||||
|
|
||||||
if not init_connect_sv() then
|
-- init renderer
|
||||||
println("boot> failed to connect to supervisor")
|
renderer.set_displays(monitors)
|
||||||
log_sys("system shutdown")
|
renderer.reset(config.RECOLOR)
|
||||||
return
|
renderer.init_dmesg()
|
||||||
else
|
|
||||||
log_sys("supervisor connected, proceeding to UI start")
|
|
||||||
end
|
|
||||||
|
|
||||||
----------------------------------------
|
log_graphics("displays connected and reset")
|
||||||
-- start the UI
|
log_sys("system start on " .. os.date("%c"))
|
||||||
----------------------------------------
|
log_boot("starting " .. COORDINATOR_VERSION)
|
||||||
|
|
||||||
-- start up the UI
|
----------------------------------------
|
||||||
---@return boolean ui_ok started ok
|
-- setup communications
|
||||||
local function init_start_ui()
|
----------------------------------------
|
||||||
log_graphics("starting UI...")
|
|
||||||
|
|
||||||
local draw_start = util.time_ms()
|
-- get the communications modem
|
||||||
|
local modem = ppm.get_wireless_modem()
|
||||||
local ui_ok, message = pcall(renderer.start_ui)
|
if modem == nil then
|
||||||
if not ui_ok then
|
log_comms("wireless modem not found")
|
||||||
renderer.close_ui()
|
println("boot> wireless modem not found")
|
||||||
log_graphics(util.c("UI crashed: ", message))
|
log.fatal("no wireless modem on startup")
|
||||||
println_ts("UI crashed")
|
return
|
||||||
log.fatal(util.c("ui crashed with error ", message))
|
|
||||||
else
|
else
|
||||||
log_graphics("first UI draw took " .. (util.time_ms() - draw_start) .. "ms")
|
log_comms("wireless modem connected")
|
||||||
|
|
||||||
-- start clock
|
|
||||||
loop_clock.start()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return ui_ok
|
-- create connection watchdog
|
||||||
end
|
local conn_watchdog = util.new_watchdog(5)
|
||||||
|
conn_watchdog.cancel()
|
||||||
|
log.debug("boot> conn watchdog created")
|
||||||
|
|
||||||
local ui_ok = init_start_ui()
|
-- start comms, open all channels
|
||||||
|
local coord_comms = coordinator.comms(COORDINATOR_VERSION, modem, config.SCADA_SV_PORT, config.SCADA_SV_LISTEN, config.SCADA_API_LISTEN, conn_watchdog)
|
||||||
|
log.debug("boot> comms init")
|
||||||
|
log_comms("comms initialized")
|
||||||
|
|
||||||
----------------------------------------
|
-- base loop clock (2Hz, 10 ticks)
|
||||||
-- main event loop
|
local MAIN_CLOCK = 0.5
|
||||||
----------------------------------------
|
local loop_clock = util.new_clock(MAIN_CLOCK)
|
||||||
|
|
||||||
local no_modem = false
|
----------------------------------------
|
||||||
|
-- connect to the supervisor
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
if ui_ok then
|
-- attempt to connect to the supervisor or exit
|
||||||
-- start connection watchdog
|
local function init_connect_sv()
|
||||||
conn_watchdog.feed()
|
local tick_waiting, task_done = log_comms_connecting("attempting to connect to configured supervisor on channel " .. config.SCADA_SV_PORT)
|
||||||
log.debug("boot> conn watchdog started")
|
|
||||||
|
|
||||||
log_sys("system started successfully")
|
-- attempt to establish a connection with the supervisory computer
|
||||||
end
|
if not coord_comms.sv_connect(60, tick_waiting, task_done) then
|
||||||
|
log_comms("supervisor connection failed")
|
||||||
|
log.fatal("failed to connect to supervisor")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- main event loop
|
return true
|
||||||
while ui_ok do
|
end
|
||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
|
||||||
|
|
||||||
-- handle event
|
if not init_connect_sv() then
|
||||||
if event == "peripheral_detach" then
|
println("boot> failed to connect to supervisor")
|
||||||
local type, device = ppm.handle_unmount(param1)
|
log_sys("system shutdown")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
log_sys("supervisor connected, proceeding to UI start")
|
||||||
|
end
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
----------------------------------------
|
||||||
if type == "modem" then
|
-- start the UI
|
||||||
-- we only really care if this is our wireless modem
|
----------------------------------------
|
||||||
if device == modem then
|
|
||||||
no_modem = true
|
|
||||||
log_sys("comms modem disconnected")
|
|
||||||
println_ts("wireless modem disconnected!")
|
|
||||||
log.error("comms modem disconnected!")
|
|
||||||
|
|
||||||
-- close out UI
|
-- start up the UI
|
||||||
renderer.close_ui()
|
---@return boolean ui_ok started ok
|
||||||
|
local function init_start_ui()
|
||||||
|
log_graphics("starting UI...")
|
||||||
|
|
||||||
-- alert user to status
|
local draw_start = util.time_ms()
|
||||||
log_sys("awaiting comms modem reconnect...")
|
|
||||||
else
|
local ui_ok, message = pcall(renderer.start_ui)
|
||||||
log_sys("non-comms modem disconnected")
|
if not ui_ok then
|
||||||
log.warning("non-comms modem disconnected")
|
renderer.close_ui()
|
||||||
end
|
log_graphics(util.c("UI crashed: ", message))
|
||||||
elseif type == "monitor" then
|
println_ts("UI crashed")
|
||||||
if renderer.is_monitor_used(device) then
|
log.fatal(util.c("ui crashed with error ", message))
|
||||||
-- "halt and catch fire" style handling
|
else
|
||||||
log_sys("lost a configured monitor, system will now exit")
|
log_graphics("first UI draw took " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
break
|
|
||||||
else
|
-- start clock
|
||||||
log_sys("lost unused monitor, ignoring")
|
loop_clock.start()
|
||||||
|
end
|
||||||
|
|
||||||
|
return ui_ok
|
||||||
|
end
|
||||||
|
|
||||||
|
local ui_ok = init_start_ui()
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- main event loop
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
local no_modem = false
|
||||||
|
|
||||||
|
if ui_ok then
|
||||||
|
-- start connection watchdog
|
||||||
|
conn_watchdog.feed()
|
||||||
|
log.debug("boot> conn watchdog started")
|
||||||
|
|
||||||
|
log_sys("system started successfully")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- main event loop
|
||||||
|
while ui_ok do
|
||||||
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
|
-- handle event
|
||||||
|
if event == "peripheral_detach" then
|
||||||
|
local type, device = ppm.handle_unmount(param1)
|
||||||
|
|
||||||
|
if type ~= nil and device ~= nil then
|
||||||
|
if type == "modem" then
|
||||||
|
-- we only really care if this is our wireless modem
|
||||||
|
if device == modem then
|
||||||
|
no_modem = true
|
||||||
|
log_sys("comms modem disconnected")
|
||||||
|
println_ts("wireless modem disconnected!")
|
||||||
|
log.error("comms modem disconnected!")
|
||||||
|
|
||||||
|
-- close out UI
|
||||||
|
renderer.close_ui()
|
||||||
|
|
||||||
|
-- alert user to status
|
||||||
|
log_sys("awaiting comms modem reconnect...")
|
||||||
|
else
|
||||||
|
log_sys("non-comms modem disconnected")
|
||||||
|
log.warning("non-comms modem disconnected")
|
||||||
|
end
|
||||||
|
elseif type == "monitor" then
|
||||||
|
if renderer.is_monitor_used(device) then
|
||||||
|
-- "halt and catch fire" style handling
|
||||||
|
log_sys("lost a configured monitor, system will now exit")
|
||||||
|
break
|
||||||
|
else
|
||||||
|
log_sys("lost unused monitor, ignoring")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
elseif event == "peripheral" then
|
||||||
elseif event == "peripheral" then
|
local type, device = ppm.mount(param1)
|
||||||
local type, device = ppm.mount(param1)
|
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
if type ~= nil and device ~= nil then
|
||||||
if type == "modem" then
|
if type == "modem" then
|
||||||
if device.isWireless() then
|
if device.isWireless() then
|
||||||
-- reconnected modem
|
-- reconnected modem
|
||||||
no_modem = false
|
no_modem = false
|
||||||
modem = device
|
modem = device
|
||||||
coord_comms.reconnect_modem(modem)
|
coord_comms.reconnect_modem(modem)
|
||||||
|
|
||||||
log_sys("comms modem reconnected")
|
log_sys("comms modem reconnected")
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
|
|
||||||
-- re-init system
|
-- re-init system
|
||||||
|
if not init_connect_sv() then break end
|
||||||
|
ui_ok = init_start_ui()
|
||||||
|
else
|
||||||
|
log_sys("wired modem reconnected")
|
||||||
|
end
|
||||||
|
elseif type == "monitor" then
|
||||||
|
-- not supported, system will exit on loss of in-use monitors
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "timer" then
|
||||||
|
if loop_clock.is_clock(param1) then
|
||||||
|
-- main loop tick
|
||||||
|
|
||||||
|
-- free any closed sessions
|
||||||
|
--apisessions.free_all_closed()
|
||||||
|
|
||||||
|
loop_clock.start()
|
||||||
|
elseif conn_watchdog.is_timer(param1) then
|
||||||
|
-- supervisor watchdog timeout
|
||||||
|
local msg = "supervisor server timeout"
|
||||||
|
log_comms(msg)
|
||||||
|
println_ts(msg)
|
||||||
|
log.warning(msg)
|
||||||
|
|
||||||
|
-- close connection and UI
|
||||||
|
coord_comms.close()
|
||||||
|
renderer.close_ui()
|
||||||
|
|
||||||
|
if not no_modem then
|
||||||
|
-- try to re-connect to the supervisor
|
||||||
if not init_connect_sv() then break end
|
if not init_connect_sv() then break end
|
||||||
ui_ok = init_start_ui()
|
ui_ok = init_start_ui()
|
||||||
else
|
|
||||||
log_sys("wired modem reconnected")
|
|
||||||
end
|
end
|
||||||
elseif type == "monitor" then
|
else
|
||||||
-- not supported, system will exit on loss of in-use monitors
|
-- a non-clock/main watchdog timer event
|
||||||
|
|
||||||
|
--check API watchdogs
|
||||||
|
--apisessions.check_all_watchdogs(param1)
|
||||||
|
|
||||||
|
-- notify timer callback dispatcher
|
||||||
|
tcallbackdsp.handle(param1)
|
||||||
end
|
end
|
||||||
|
elseif event == "modem_message" then
|
||||||
|
-- got a packet
|
||||||
|
local packet = coord_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
coord_comms.handle_packet(packet)
|
||||||
|
|
||||||
|
-- check if it was a disconnect
|
||||||
|
if not coord_comms.is_linked() then
|
||||||
|
log_comms("supervisor closed connection")
|
||||||
|
|
||||||
|
-- close connection and UI
|
||||||
|
coord_comms.close()
|
||||||
|
renderer.close_ui()
|
||||||
|
|
||||||
|
if not no_modem then
|
||||||
|
-- try to re-connect to the supervisor
|
||||||
|
if not init_connect_sv() then break end
|
||||||
|
ui_ok = init_start_ui()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "monitor_touch" then
|
||||||
|
-- handle a monitor touch event
|
||||||
|
renderer.handle_touch(core.events.touch(param1, param2, param3))
|
||||||
end
|
end
|
||||||
elseif event == "timer" then
|
|
||||||
if loop_clock.is_clock(param1) then
|
|
||||||
-- main loop tick
|
|
||||||
|
|
||||||
-- free any closed sessions
|
-- check for termination request
|
||||||
--apisessions.free_all_closed()
|
if event == "terminate" or ppm.should_terminate() then
|
||||||
|
println_ts("terminate requested, closing connections...")
|
||||||
loop_clock.start()
|
log_comms("terminate requested, closing supervisor connection...")
|
||||||
elseif conn_watchdog.is_timer(param1) then
|
|
||||||
-- supervisor watchdog timeout
|
|
||||||
local msg = "supervisor server timeout"
|
|
||||||
log_comms(msg)
|
|
||||||
println_ts(msg)
|
|
||||||
log.warning(msg)
|
|
||||||
|
|
||||||
-- close connection and UI
|
|
||||||
coord_comms.close()
|
coord_comms.close()
|
||||||
renderer.close_ui()
|
log_comms("supervisor connection closed")
|
||||||
|
log_comms("closing api sessions...")
|
||||||
if not no_modem then
|
apisessions.close_all()
|
||||||
-- try to re-connect to the supervisor
|
log_comms("api sessions closed")
|
||||||
if not init_connect_sv() then break end
|
break
|
||||||
ui_ok = init_start_ui()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- a non-clock/main watchdog timer event
|
|
||||||
|
|
||||||
--check API watchdogs
|
|
||||||
--apisessions.check_all_watchdogs(param1)
|
|
||||||
|
|
||||||
-- notify timer callback dispatcher
|
|
||||||
tcallbackdsp.handle(param1)
|
|
||||||
end
|
end
|
||||||
elseif event == "modem_message" then
|
|
||||||
-- got a packet
|
|
||||||
local packet = coord_comms.parse_packet(param1, param2, param3, param4, param5)
|
|
||||||
coord_comms.handle_packet(packet)
|
|
||||||
|
|
||||||
-- check if it was a disconnect
|
|
||||||
if not coord_comms.is_linked() then
|
|
||||||
log_comms("supervisor closed connection")
|
|
||||||
|
|
||||||
-- close connection and UI
|
|
||||||
coord_comms.close()
|
|
||||||
renderer.close_ui()
|
|
||||||
|
|
||||||
if not no_modem then
|
|
||||||
-- try to re-connect to the supervisor
|
|
||||||
if not init_connect_sv() then break end
|
|
||||||
ui_ok = init_start_ui()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == "monitor_touch" then
|
|
||||||
-- handle a monitor touch event
|
|
||||||
renderer.handle_touch(core.events.touch(param1, param2, param3))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
renderer.close_ui()
|
||||||
if event == "terminate" or ppm.should_terminate() then
|
log_sys("system shutdown")
|
||||||
println_ts("terminate requested, closing connections...")
|
|
||||||
log_comms("terminate requested, closing supervisor connection...")
|
println_ts("exited")
|
||||||
coord_comms.close()
|
log.info("exited")
|
||||||
log_comms("supervisor connection closed")
|
|
||||||
log_comms("closing api sessions...")
|
|
||||||
apisessions.close_all()
|
|
||||||
log_comms("api sessions closed")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
renderer.close_ui()
|
if not xpcall(main, crash.handler) then crash.exit() end
|
||||||
log_sys("system shutdown")
|
|
||||||
|
|
||||||
println_ts("exited")
|
|
||||||
log.info("exited")
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
@ -13,7 +14,7 @@ local config = require("reactor-plc.config")
|
|||||||
local plc = require("reactor-plc.plc")
|
local plc = require("reactor-plc.plc")
|
||||||
local threads = require("reactor-plc.threads")
|
local threads = require("reactor-plc.threads")
|
||||||
|
|
||||||
local R_PLC_VERSION = "beta-v0.9.6"
|
local R_PLC_VERSION = "beta-v0.9.7"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -45,166 +46,176 @@ log.info("BOOTING reactor-plc.startup " .. R_PLC_VERSION)
|
|||||||
log.info("========================================")
|
log.info("========================================")
|
||||||
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
|
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
|
||||||
|
|
||||||
|
crash.set_env("plc", R_PLC_VERSION)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- startup
|
-- main application
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
-- mount connected devices
|
local function main()
|
||||||
ppm.mount_all()
|
----------------------------------------
|
||||||
|
-- startup
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
-- shared memory across threads
|
-- mount connected devices
|
||||||
---@class plc_shared_memory
|
ppm.mount_all()
|
||||||
local __shared_memory = {
|
|
||||||
-- networked setting
|
|
||||||
networked = config.NETWORKED, ---@type boolean
|
|
||||||
|
|
||||||
-- PLC system state flags
|
-- shared memory across threads
|
||||||
---@class plc_state
|
---@class plc_shared_memory
|
||||||
plc_state = {
|
local __shared_memory = {
|
||||||
init_ok = true,
|
-- networked setting
|
||||||
shutdown = false,
|
networked = config.NETWORKED, ---@type boolean
|
||||||
degraded = false,
|
|
||||||
reactor_formed = true,
|
|
||||||
no_reactor = false,
|
|
||||||
no_modem = false
|
|
||||||
},
|
|
||||||
|
|
||||||
-- control setpoints
|
-- PLC system state flags
|
||||||
---@class setpoints
|
---@class plc_state
|
||||||
setpoints = {
|
plc_state = {
|
||||||
burn_rate_en = false,
|
init_ok = true,
|
||||||
burn_rate = 0.0
|
shutdown = false,
|
||||||
},
|
degraded = false,
|
||||||
|
reactor_formed = true,
|
||||||
|
no_reactor = false,
|
||||||
|
no_modem = false
|
||||||
|
},
|
||||||
|
|
||||||
-- core PLC devices
|
-- control setpoints
|
||||||
plc_dev = {
|
---@class setpoints
|
||||||
reactor = ppm.get_fission_reactor(),
|
setpoints = {
|
||||||
modem = ppm.get_wireless_modem()
|
burn_rate_en = false,
|
||||||
},
|
burn_rate = 0.0
|
||||||
|
},
|
||||||
|
|
||||||
-- system objects
|
-- core PLC devices
|
||||||
plc_sys = {
|
plc_dev = {
|
||||||
rps = nil, ---@type rps
|
reactor = ppm.get_fission_reactor(),
|
||||||
plc_comms = nil, ---@type plc_comms
|
modem = ppm.get_wireless_modem()
|
||||||
conn_watchdog = nil ---@type watchdog
|
},
|
||||||
},
|
|
||||||
|
|
||||||
-- message queues
|
-- system objects
|
||||||
q = {
|
plc_sys = {
|
||||||
mq_rps = mqueue.new(),
|
rps = nil, ---@type rps
|
||||||
mq_comms_tx = mqueue.new(),
|
plc_comms = nil, ---@type plc_comms
|
||||||
mq_comms_rx = mqueue.new()
|
conn_watchdog = nil ---@type watchdog
|
||||||
|
},
|
||||||
|
|
||||||
|
-- message queues
|
||||||
|
q = {
|
||||||
|
mq_rps = mqueue.new(),
|
||||||
|
mq_comms_tx = mqueue.new(),
|
||||||
|
mq_comms_rx = mqueue.new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
local smem_dev = __shared_memory.plc_dev
|
local smem_dev = __shared_memory.plc_dev
|
||||||
local smem_sys = __shared_memory.plc_sys
|
local smem_sys = __shared_memory.plc_sys
|
||||||
|
|
||||||
local plc_state = __shared_memory.plc_state
|
local plc_state = __shared_memory.plc_state
|
||||||
|
|
||||||
-- we need a reactor, can at least do some things even if it isn't formed though
|
-- we need a reactor, can at least do some things even if it isn't formed though
|
||||||
if smem_dev.reactor == nil then
|
if smem_dev.reactor == nil then
|
||||||
println("boot> fission reactor not found");
|
println("boot> fission reactor not found");
|
||||||
log.warning("no reactor on startup")
|
log.warning("no reactor on startup")
|
||||||
|
|
||||||
plc_state.init_ok = false
|
plc_state.init_ok = false
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
plc_state.no_reactor = true
|
plc_state.no_reactor = true
|
||||||
elseif not smem_dev.reactor.isFormed() then
|
elseif not smem_dev.reactor.isFormed() then
|
||||||
println("boot> fission reactor not formed");
|
println("boot> fission reactor not formed");
|
||||||
log.warning("reactor logic adapter present, but reactor is not formed")
|
log.warning("reactor logic adapter present, but reactor is not formed")
|
||||||
|
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
plc_state.reactor_formed = false
|
plc_state.reactor_formed = false
|
||||||
end
|
|
||||||
|
|
||||||
-- 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")
|
|
||||||
|
|
||||||
-- scram reactor if present and enabled
|
|
||||||
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
|
||||||
smem_dev.reactor.scram()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
plc_state.init_ok = false
|
-- modem is required if networked
|
||||||
plc_state.degraded = true
|
if __shared_memory.networked and smem_dev.modem == nil then
|
||||||
plc_state.no_modem = true
|
println("boot> wireless modem not found")
|
||||||
end
|
log.warning("no wireless modem on startup")
|
||||||
|
|
||||||
-- PLC init
|
-- scram reactor if present and enabled
|
||||||
---
|
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||||
--- EVENT_CONSUMER: this function consumes events
|
|
||||||
local function init()
|
|
||||||
if plc_state.init_ok then
|
|
||||||
-- just booting up, no fission allowed (neutrons stay put thanks)
|
|
||||||
if plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
|
||||||
smem_dev.reactor.scram()
|
smem_dev.reactor.scram()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init reactor protection system
|
plc_state.init_ok = false
|
||||||
smem_sys.rps = plc.rps_init(smem_dev.reactor, plc_state.reactor_formed)
|
plc_state.degraded = true
|
||||||
log.debug("init> rps init")
|
plc_state.no_modem = true
|
||||||
|
end
|
||||||
|
|
||||||
if __shared_memory.networked then
|
-- PLC init
|
||||||
-- comms watchdog, 3 second timeout
|
---
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(3)
|
--- EVENT_CONSUMER: this function consumes events
|
||||||
log.debug("init> conn watchdog started")
|
local function init()
|
||||||
|
if plc_state.init_ok then
|
||||||
|
-- just booting up, no fission allowed (neutrons stay put thanks)
|
||||||
|
if plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||||
|
smem_dev.reactor.scram()
|
||||||
|
end
|
||||||
|
|
||||||
-- start comms
|
-- init reactor protection system
|
||||||
smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT,
|
smem_sys.rps = plc.rps_init(smem_dev.reactor, plc_state.reactor_formed)
|
||||||
smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
log.debug("init> rps init")
|
||||||
log.debug("init> comms init")
|
|
||||||
|
if __shared_memory.networked then
|
||||||
|
-- comms watchdog, 3 second timeout
|
||||||
|
smem_sys.conn_watchdog = util.new_watchdog(3)
|
||||||
|
log.debug("init> conn watchdog started")
|
||||||
|
|
||||||
|
-- start comms
|
||||||
|
smem_sys.plc_comms = plc.comms(config.REACTOR_ID, R_PLC_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT,
|
||||||
|
smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
||||||
|
log.debug("init> comms init")
|
||||||
|
else
|
||||||
|
println("boot> starting in offline mode")
|
||||||
|
log.debug("init> running without networking")
|
||||||
|
end
|
||||||
|
|
||||||
|
util.push_event("clock_start")
|
||||||
|
|
||||||
|
println("boot> completed")
|
||||||
|
log.debug("init> boot completed")
|
||||||
else
|
else
|
||||||
println("boot> starting in offline mode")
|
println("boot> system in degraded state, awaiting devices...")
|
||||||
log.debug("init> running without networking")
|
log.warning("init> booted in a degraded state, awaiting peripheral connections...")
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
util.push_event("clock_start")
|
----------------------------------------
|
||||||
|
-- start system
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
println("boot> completed")
|
-- initialize PLC
|
||||||
log.debug("init> boot completed")
|
init()
|
||||||
|
|
||||||
|
-- init threads
|
||||||
|
local main_thread = threads.thread__main(__shared_memory, init)
|
||||||
|
local rps_thread = threads.thread__rps(__shared_memory)
|
||||||
|
|
||||||
|
if __shared_memory.networked then
|
||||||
|
-- init comms threads
|
||||||
|
local comms_thread_tx = threads.thread__comms_tx(__shared_memory)
|
||||||
|
local comms_thread_rx = threads.thread__comms_rx(__shared_memory)
|
||||||
|
|
||||||
|
-- setpoint control only needed when networked
|
||||||
|
local sp_ctrl_thread = threads.thread__setpoint_control(__shared_memory)
|
||||||
|
|
||||||
|
-- run threads
|
||||||
|
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec, comms_thread_tx.p_exec, comms_thread_rx.p_exec, sp_ctrl_thread.p_exec)
|
||||||
|
|
||||||
|
if plc_state.init_ok then
|
||||||
|
-- send status one last time after RPS shutdown
|
||||||
|
smem_sys.plc_comms.send_status(plc_state.no_reactor, plc_state.reactor_formed)
|
||||||
|
smem_sys.plc_comms.send_rps_status()
|
||||||
|
|
||||||
|
-- close connection
|
||||||
|
smem_sys.plc_comms.close()
|
||||||
|
end
|
||||||
else
|
else
|
||||||
println("boot> system in degraded state, awaiting devices...")
|
-- run threads, excluding comms
|
||||||
log.warning("init> booted in a degraded state, awaiting peripheral connections...")
|
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
println_ts("exited")
|
||||||
|
log.info("exited")
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------
|
if not xpcall(main, crash.handler) then crash.exit() end
|
||||||
-- start system
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
-- initialize PLC
|
|
||||||
init()
|
|
||||||
|
|
||||||
-- init threads
|
|
||||||
local main_thread = threads.thread__main(__shared_memory, init)
|
|
||||||
local rps_thread = threads.thread__rps(__shared_memory)
|
|
||||||
|
|
||||||
if __shared_memory.networked then
|
|
||||||
-- init comms threads
|
|
||||||
local comms_thread_tx = threads.thread__comms_tx(__shared_memory)
|
|
||||||
local comms_thread_rx = threads.thread__comms_rx(__shared_memory)
|
|
||||||
|
|
||||||
-- setpoint control only needed when networked
|
|
||||||
local sp_ctrl_thread = threads.thread__setpoint_control(__shared_memory)
|
|
||||||
|
|
||||||
-- run threads
|
|
||||||
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec, comms_thread_tx.p_exec, comms_thread_rx.p_exec, sp_ctrl_thread.p_exec)
|
|
||||||
|
|
||||||
if plc_state.init_ok then
|
|
||||||
-- send status one last time after RPS shutdown
|
|
||||||
smem_sys.plc_comms.send_status(plc_state.no_reactor, plc_state.reactor_formed)
|
|
||||||
smem_sys.plc_comms.send_rps_status()
|
|
||||||
|
|
||||||
-- close connection
|
|
||||||
smem_sys.plc_comms.close()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- run threads, excluding comms
|
|
||||||
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec)
|
|
||||||
end
|
|
||||||
|
|
||||||
println_ts("exited")
|
|
||||||
log.info("exited")
|
|
||||||
|
591
rtu/startup.lua
591
rtu/startup.lua
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
@ -24,7 +25,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
|||||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||||
|
|
||||||
local RTU_VERSION = "beta-v0.9.3"
|
local RTU_VERSION = "beta-v0.9.4"
|
||||||
|
|
||||||
local rtu_t = types.rtu_t
|
local rtu_t = types.rtu_t
|
||||||
|
|
||||||
@ -58,341 +59,351 @@ log.info("BOOTING rtu.startup " .. RTU_VERSION)
|
|||||||
log.info("========================================")
|
log.info("========================================")
|
||||||
println(">> RTU GATEWAY " .. RTU_VERSION .. " <<")
|
println(">> RTU GATEWAY " .. RTU_VERSION .. " <<")
|
||||||
|
|
||||||
|
crash.set_env("rtu", RTU_VERSION)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- startup
|
-- main application
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
-- mount connected devices
|
local function main()
|
||||||
ppm.mount_all()
|
----------------------------------------
|
||||||
|
-- startup
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
---@class rtu_shared_memory
|
-- mount connected devices
|
||||||
local __shared_memory = {
|
ppm.mount_all()
|
||||||
-- RTU system state flags
|
|
||||||
---@class rtu_state
|
|
||||||
rtu_state = {
|
|
||||||
linked = false,
|
|
||||||
shutdown = false
|
|
||||||
},
|
|
||||||
|
|
||||||
-- core RTU devices
|
---@class rtu_shared_memory
|
||||||
rtu_dev = {
|
local __shared_memory = {
|
||||||
modem = ppm.get_wireless_modem()
|
-- RTU system state flags
|
||||||
},
|
---@class rtu_state
|
||||||
|
rtu_state = {
|
||||||
|
linked = false,
|
||||||
|
shutdown = false
|
||||||
|
},
|
||||||
|
|
||||||
-- system objects
|
-- core RTU devices
|
||||||
rtu_sys = {
|
rtu_dev = {
|
||||||
rtu_comms = nil, ---@type rtu_comms
|
modem = ppm.get_wireless_modem()
|
||||||
conn_watchdog = nil, ---@type watchdog
|
},
|
||||||
units = {} ---@type table
|
|
||||||
},
|
|
||||||
|
|
||||||
-- message queues
|
-- system objects
|
||||||
q = {
|
rtu_sys = {
|
||||||
mq_comms = mqueue.new()
|
rtu_comms = nil, ---@type rtu_comms
|
||||||
|
conn_watchdog = nil, ---@type watchdog
|
||||||
|
units = {} ---@type table
|
||||||
|
},
|
||||||
|
|
||||||
|
-- message queues
|
||||||
|
q = {
|
||||||
|
mq_comms = mqueue.new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
local smem_dev = __shared_memory.rtu_dev
|
local smem_dev = __shared_memory.rtu_dev
|
||||||
local smem_sys = __shared_memory.rtu_sys
|
local smem_sys = __shared_memory.rtu_sys
|
||||||
|
|
||||||
-- get modem
|
-- get modem
|
||||||
if smem_dev.modem == nil then
|
if smem_dev.modem == nil then
|
||||||
println("boot> wireless modem not found")
|
println("boot> wireless modem not found")
|
||||||
log.fatal("no wireless modem on startup")
|
log.fatal("no wireless modem on startup")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- interpret config and init units
|
-- interpret config and init units
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local units = __shared_memory.rtu_sys.units
|
local units = __shared_memory.rtu_sys.units
|
||||||
|
|
||||||
local rtu_redstone = config.RTU_REDSTONE
|
local rtu_redstone = config.RTU_REDSTONE
|
||||||
local rtu_devices = config.RTU_DEVICES
|
local rtu_devices = config.RTU_DEVICES
|
||||||
|
|
||||||
-- configure RTU gateway based on config file definitions
|
-- configure RTU gateway based on config file definitions
|
||||||
local function configure()
|
local function configure()
|
||||||
-- redstone interfaces
|
-- redstone interfaces
|
||||||
for entry_idx = 1, #rtu_redstone do
|
for entry_idx = 1, #rtu_redstone do
|
||||||
local rs_rtu = redstone_rtu.new()
|
local rs_rtu = redstone_rtu.new()
|
||||||
local io_table = rtu_redstone[entry_idx].io ---@type table
|
local io_table = rtu_redstone[entry_idx].io ---@type table
|
||||||
local io_reactor = rtu_redstone[entry_idx].for_reactor ---@type integer
|
local io_reactor = rtu_redstone[entry_idx].for_reactor ---@type integer
|
||||||
|
|
||||||
-- CHECK: reactor ID must be >= to 1
|
-- CHECK: reactor ID must be >= to 1
|
||||||
if (not util.is_int(io_reactor)) or (io_reactor <= 0) then
|
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 >= 1"))
|
println(util.c("configure> redstone entry #", entry_idx, " : ", io_reactor, " isn't an integer >= 1"))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- CHECK: io table exists
|
-- CHECK: io table exists
|
||||||
if type(io_table) ~= "table" then
|
if type(io_table) ~= "table" then
|
||||||
println(util.c("configure> redstone entry #", entry_idx, " no IO table found"))
|
println(util.c("configure> redstone entry #", entry_idx, " no IO table found"))
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local capabilities = {}
|
local capabilities = {}
|
||||||
|
|
||||||
log.debug(util.c("configure> starting redstone RTU I/O linking for reactor ", io_reactor, "..."))
|
log.debug(util.c("configure> starting redstone RTU I/O linking for reactor ", io_reactor, "..."))
|
||||||
|
|
||||||
local continue = true
|
local continue = true
|
||||||
|
|
||||||
-- check for duplicate entries
|
-- check for duplicate entries
|
||||||
for i = 1, #units do
|
for i = 1, #units do
|
||||||
local unit = units[i] ---@type rtu_unit_registry_entry
|
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||||
if unit.reactor == io_reactor and unit.type == rtu_t.redstone then
|
if unit.reactor == io_reactor and unit.type == rtu_t.redstone then
|
||||||
-- duplicate entry
|
-- duplicate entry
|
||||||
local message = util.c("configure> skipping definition block #", entry_idx, " for reactor ", io_reactor,
|
local message = util.c("configure> skipping definition block #", entry_idx, " for reactor ", io_reactor,
|
||||||
" with already defined redstone I/O")
|
" with already defined redstone I/O")
|
||||||
println(message)
|
println(message)
|
||||||
log.warning(message)
|
log.warning(message)
|
||||||
continue = false
|
continue = false
|
||||||
break
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- not a duplicate
|
||||||
|
if continue then
|
||||||
|
for i = 1, #io_table do
|
||||||
|
local valid = false
|
||||||
|
local conf = io_table[i]
|
||||||
|
|
||||||
|
-- verify configuration
|
||||||
|
if rsio.is_valid_channel(conf.channel) and rsio.is_valid_side(conf.side) then
|
||||||
|
if conf.bundled_color then
|
||||||
|
valid = rsio.is_color(conf.bundled_color)
|
||||||
|
else
|
||||||
|
valid = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not valid then
|
||||||
|
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)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
-- link redstone in RTU
|
||||||
|
local mode = rsio.get_io_mode(conf.channel)
|
||||||
|
if mode == rsio.IO_MODE.DIGITAL_IN then
|
||||||
|
-- can't have duplicate inputs
|
||||||
|
if util.table_contains(capabilities, conf.channel) then
|
||||||
|
local message = util.c("configure> skipping duplicate input for channel ", rsio.to_string(conf.channel), " on side ", conf.side)
|
||||||
|
println(message)
|
||||||
|
log.warning(message)
|
||||||
|
else
|
||||||
|
rs_rtu.link_di(conf.side, conf.bundled_color)
|
||||||
|
end
|
||||||
|
elseif mode == rsio.IO_MODE.DIGITAL_OUT then
|
||||||
|
rs_rtu.link_do(conf.channel, conf.side, conf.bundled_color)
|
||||||
|
elseif mode == rsio.IO_MODE.ANALOG_IN then
|
||||||
|
-- can't have duplicate inputs
|
||||||
|
if util.table_contains(capabilities, conf.channel) then
|
||||||
|
local message = util.c("configure> skipping duplicate input for channel ", rsio.to_string(conf.channel), " on side ", conf.side)
|
||||||
|
println(message)
|
||||||
|
log.warning(message)
|
||||||
|
else
|
||||||
|
rs_rtu.link_ai(conf.side)
|
||||||
|
end
|
||||||
|
elseif mode == rsio.IO_MODE.ANALOG_OUT then
|
||||||
|
rs_rtu.link_ao(conf.side)
|
||||||
|
else
|
||||||
|
-- should be unreachable code, we already validated channels
|
||||||
|
log.error("configure> fell through if chain attempting to identify IO mode", true)
|
||||||
|
println("configure> encountered a software error, check logs")
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(capabilities, conf.channel)
|
||||||
|
|
||||||
|
log.debug(util.c("configure> linked redstone ", #capabilities, ": ", rsio.to_string(conf.channel),
|
||||||
|
" (", conf.side, ") for reactor ", io_reactor))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class rtu_unit_registry_entry
|
||||||
|
local unit = {
|
||||||
|
name = "redstone_io",
|
||||||
|
type = rtu_t.redstone,
|
||||||
|
index = entry_idx,
|
||||||
|
reactor = io_reactor,
|
||||||
|
device = capabilities, -- use device field for redstone channels
|
||||||
|
formed = nil, ---@type boolean|nil
|
||||||
|
rtu = rs_rtu, ---@type rtu_device|rtu_rs_device
|
||||||
|
modbus_io = modbus.new(rs_rtu, false),
|
||||||
|
pkt_queue = nil, ---@type mqueue|nil
|
||||||
|
thread = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
table.insert(units, unit)
|
||||||
|
|
||||||
|
log.debug(util.c("init> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for reactor ", io_reactor))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- not a duplicate
|
-- mounted peripherals
|
||||||
if continue then
|
for i = 1, #rtu_devices do
|
||||||
for i = 1, #io_table do
|
local name = rtu_devices[i].name
|
||||||
local valid = false
|
local index = rtu_devices[i].index
|
||||||
local conf = io_table[i]
|
local for_reactor = rtu_devices[i].for_reactor
|
||||||
|
|
||||||
-- verify configuration
|
-- CHECK: name is a string
|
||||||
if rsio.is_valid_channel(conf.channel) and rsio.is_valid_side(conf.side) then
|
if type(name) ~= "string" then
|
||||||
if conf.bundled_color then
|
println(util.c("configure> device entry #", i, ": device ", name, " isn't a string"))
|
||||||
valid = rsio.is_color(conf.bundled_color)
|
return false
|
||||||
else
|
end
|
||||||
valid = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not valid then
|
-- CHECK: index is an integer >= 1
|
||||||
local message = util.c("configure> invalid redstone definition at index ", i, " in definition block #", entry_idx,
|
if (not util.is_int(index)) or (index <= 0) then
|
||||||
" (for reactor ", io_reactor, ")")
|
println(util.c("configure> device entry #", i, ": index ", index, " isn't an integer >= 1"))
|
||||||
println(message)
|
return false
|
||||||
log.error(message)
|
end
|
||||||
|
|
||||||
|
-- CHECK: reactor is an integer >= 1
|
||||||
|
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 >= 1"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local device = ppm.get_periph(name)
|
||||||
|
|
||||||
|
local type = nil
|
||||||
|
local rtu_iface = nil ---@type rtu_device
|
||||||
|
local rtu_type = ""
|
||||||
|
local formed = nil ---@type boolean|nil
|
||||||
|
|
||||||
|
if device == nil then
|
||||||
|
local message = util.c("configure> '", name, "' not found, using placeholder")
|
||||||
|
println(message)
|
||||||
|
log.warning(message)
|
||||||
|
|
||||||
|
-- mount a virtual (placeholder) device
|
||||||
|
type, device = ppm.mount_virtual()
|
||||||
|
else
|
||||||
|
type = ppm.get_type(name)
|
||||||
|
end
|
||||||
|
|
||||||
|
if type == "boilerValve" then
|
||||||
|
-- boiler multiblock
|
||||||
|
rtu_type = rtu_t.boiler_valve
|
||||||
|
rtu_iface = boilerv_rtu.new(device)
|
||||||
|
formed = device.isFormed()
|
||||||
|
|
||||||
|
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
||||||
|
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
||||||
|
log.fatal(util.c("configure> failed to check if '", name, "' is a formed boiler multiblock"))
|
||||||
return false
|
return false
|
||||||
else
|
|
||||||
-- link redstone in RTU
|
|
||||||
local mode = rsio.get_io_mode(conf.channel)
|
|
||||||
if mode == rsio.IO_MODE.DIGITAL_IN then
|
|
||||||
-- can't have duplicate inputs
|
|
||||||
if util.table_contains(capabilities, conf.channel) then
|
|
||||||
local message = util.c("configure> skipping duplicate input for channel ", rsio.to_string(conf.channel), " on side ", conf.side)
|
|
||||||
println(message)
|
|
||||||
log.warning(message)
|
|
||||||
else
|
|
||||||
rs_rtu.link_di(conf.side, conf.bundled_color)
|
|
||||||
end
|
|
||||||
elseif mode == rsio.IO_MODE.DIGITAL_OUT then
|
|
||||||
rs_rtu.link_do(conf.channel, conf.side, conf.bundled_color)
|
|
||||||
elseif mode == rsio.IO_MODE.ANALOG_IN then
|
|
||||||
-- can't have duplicate inputs
|
|
||||||
if util.table_contains(capabilities, conf.channel) then
|
|
||||||
local message = util.c("configure> skipping duplicate input for channel ", rsio.to_string(conf.channel), " on side ", conf.side)
|
|
||||||
println(message)
|
|
||||||
log.warning(message)
|
|
||||||
else
|
|
||||||
rs_rtu.link_ai(conf.side)
|
|
||||||
end
|
|
||||||
elseif mode == rsio.IO_MODE.ANALOG_OUT then
|
|
||||||
rs_rtu.link_ao(conf.side)
|
|
||||||
else
|
|
||||||
-- should be unreachable code, we already validated channels
|
|
||||||
log.error("configure> fell through if chain attempting to identify IO mode", true)
|
|
||||||
println("configure> encountered a software error, check logs")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(capabilities, conf.channel)
|
|
||||||
|
|
||||||
log.debug(util.c("configure> linked redstone ", #capabilities, ": ", rsio.to_string(conf.channel),
|
|
||||||
" (", conf.side, ") for reactor ", io_reactor))
|
|
||||||
end
|
end
|
||||||
|
elseif type == "turbineValve" then
|
||||||
|
-- turbine multiblock
|
||||||
|
rtu_type = rtu_t.turbine_valve
|
||||||
|
rtu_iface = turbinev_rtu.new(device)
|
||||||
|
formed = device.isFormed()
|
||||||
|
|
||||||
|
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
||||||
|
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
||||||
|
log.fatal(util.c("configure> failed to check if '", name, "' is a formed turbine multiblock"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
elseif type == "inductionPort" then
|
||||||
|
-- induction matrix multiblock
|
||||||
|
rtu_type = rtu_t.induction_matrix
|
||||||
|
rtu_iface = imatrix_rtu.new(device)
|
||||||
|
formed = device.isFormed()
|
||||||
|
|
||||||
|
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
||||||
|
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
||||||
|
log.fatal(util.c("configure> failed to check if '", name, "' is a formed induction matrix multiblock"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
elseif type == "spsPort" then
|
||||||
|
-- SPS multiblock
|
||||||
|
rtu_type = rtu_t.sps
|
||||||
|
rtu_iface = sps_rtu.new(device)
|
||||||
|
formed = device.isFormed()
|
||||||
|
|
||||||
|
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
||||||
|
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
||||||
|
log.fatal(util.c("configure> failed to check if '", name, "' is a formed SPS multiblock"))
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
elseif type == "solarNeutronActivator" then
|
||||||
|
-- SNA
|
||||||
|
rtu_type = rtu_t.sna
|
||||||
|
rtu_iface = sna_rtu.new(device)
|
||||||
|
elseif type == "environmentDetector" then
|
||||||
|
-- advanced peripherals environment detector
|
||||||
|
rtu_type = rtu_t.env_detector
|
||||||
|
rtu_iface = envd_rtu.new(device)
|
||||||
|
elseif type == ppm.VIRTUAL_DEVICE_TYPE then
|
||||||
|
-- placeholder device
|
||||||
|
rtu_type = "virtual"
|
||||||
|
rtu_iface = rtu.init_unit().interface()
|
||||||
|
else
|
||||||
|
local message = util.c("configure> device '", name, "' is not a known type (", type, ")")
|
||||||
|
println_ts(message)
|
||||||
|
log.fatal(message)
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class rtu_unit_registry_entry
|
---@class rtu_unit_registry_entry
|
||||||
local unit = {
|
local rtu_unit = {
|
||||||
name = "redstone_io",
|
name = name,
|
||||||
type = rtu_t.redstone,
|
type = rtu_type,
|
||||||
index = entry_idx,
|
index = index,
|
||||||
reactor = io_reactor,
|
reactor = for_reactor,
|
||||||
device = capabilities, -- use device field for redstone channels
|
device = device,
|
||||||
formed = nil, ---@type boolean|nil
|
formed = formed,
|
||||||
rtu = rs_rtu, ---@type rtu_device|rtu_rs_device
|
rtu = rtu_iface, ---@type rtu_device|rtu_rs_device
|
||||||
modbus_io = modbus.new(rs_rtu, false),
|
modbus_io = modbus.new(rtu_iface, true),
|
||||||
pkt_queue = nil, ---@type mqueue|nil
|
pkt_queue = mqueue.new(), ---@type mqueue|nil
|
||||||
thread = nil
|
thread = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
table.insert(units, unit)
|
rtu_unit.thread = threads.thread__unit_comms(__shared_memory, rtu_unit)
|
||||||
|
|
||||||
log.debug(util.c("init> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for reactor ", io_reactor))
|
table.insert(units, rtu_unit)
|
||||||
|
|
||||||
|
log.debug(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", rtu_type, ") [", index, "] for reactor ", for_reactor))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- we made it through all that trusting-user-to-write-a-config-file chaos
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mounted peripherals
|
----------------------------------------
|
||||||
for i = 1, #rtu_devices do
|
-- start system
|
||||||
local name = rtu_devices[i].name
|
----------------------------------------
|
||||||
local index = rtu_devices[i].index
|
|
||||||
local for_reactor = rtu_devices[i].for_reactor
|
|
||||||
|
|
||||||
-- CHECK: name is a string
|
log.debug("boot> running configure()")
|
||||||
if type(name) ~= "string" then
|
|
||||||
println(util.c("configure> device entry #", i, ": device ", name, " isn't a string"))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- CHECK: index is an integer >= 1
|
if configure() then
|
||||||
if (not util.is_int(index)) or (index <= 0) then
|
-- start connection watchdog
|
||||||
println(util.c("configure> device entry #", i, ": index ", index, " isn't an integer >= 1"))
|
smem_sys.conn_watchdog = util.new_watchdog(5)
|
||||||
return false
|
log.debug("boot> conn watchdog started")
|
||||||
end
|
|
||||||
|
|
||||||
-- CHECK: reactor is an integer >= 1
|
-- setup comms
|
||||||
if (not util.is_int(for_reactor)) or (for_reactor <= 0) then
|
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT, smem_sys.conn_watchdog)
|
||||||
println(util.c("configure> device entry #", i, ": reactor ", for_reactor, " isn't an integer >= 1"))
|
log.debug("boot> comms init")
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local device = ppm.get_periph(name)
|
-- init threads
|
||||||
|
local main_thread = threads.thread__main(__shared_memory)
|
||||||
|
local comms_thread = threads.thread__comms(__shared_memory)
|
||||||
|
|
||||||
local type = nil
|
-- assemble thread list
|
||||||
local rtu_iface = nil ---@type rtu_device
|
local _threads = { main_thread.p_exec, comms_thread.p_exec }
|
||||||
local rtu_type = ""
|
for i = 1, #units do
|
||||||
local formed = nil ---@type boolean|nil
|
if units[i].thread ~= nil then
|
||||||
|
table.insert(_threads, units[i].thread.p_exec)
|
||||||
if device == nil then
|
|
||||||
local message = util.c("configure> '", name, "' not found, using placeholder")
|
|
||||||
println(message)
|
|
||||||
log.warning(message)
|
|
||||||
|
|
||||||
-- mount a virtual (placeholder) device
|
|
||||||
type, device = ppm.mount_virtual()
|
|
||||||
else
|
|
||||||
type = ppm.get_type(name)
|
|
||||||
end
|
|
||||||
|
|
||||||
if type == "boilerValve" then
|
|
||||||
-- boiler multiblock
|
|
||||||
rtu_type = rtu_t.boiler_valve
|
|
||||||
rtu_iface = boilerv_rtu.new(device)
|
|
||||||
formed = device.isFormed()
|
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
|
||||||
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
|
||||||
log.fatal(util.c("configure> failed to check if '", name, "' is a formed boiler multiblock"))
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
elseif type == "turbineValve" then
|
|
||||||
-- turbine multiblock
|
|
||||||
rtu_type = rtu_t.turbine_valve
|
|
||||||
rtu_iface = turbinev_rtu.new(device)
|
|
||||||
formed = device.isFormed()
|
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
|
||||||
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
|
||||||
log.fatal(util.c("configure> failed to check if '", name, "' is a formed turbine multiblock"))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
elseif type == "inductionPort" then
|
|
||||||
-- induction matrix multiblock
|
|
||||||
rtu_type = rtu_t.induction_matrix
|
|
||||||
rtu_iface = imatrix_rtu.new(device)
|
|
||||||
formed = device.isFormed()
|
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
|
||||||
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
|
||||||
log.fatal(util.c("configure> failed to check if '", name, "' is a formed induction matrix multiblock"))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
elseif type == "spsPort" then
|
|
||||||
-- SPS multiblock
|
|
||||||
rtu_type = rtu_t.sps
|
|
||||||
rtu_iface = sps_rtu.new(device)
|
|
||||||
formed = device.isFormed()
|
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
|
||||||
println_ts(util.c("configure> failed to check if '", name, "' is formed"))
|
|
||||||
log.fatal(util.c("configure> failed to check if '", name, "' is a formed SPS multiblock"))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
elseif type == "solarNeutronActivator" then
|
|
||||||
-- SNA
|
|
||||||
rtu_type = rtu_t.sna
|
|
||||||
rtu_iface = sna_rtu.new(device)
|
|
||||||
elseif type == "environmentDetector" then
|
|
||||||
-- advanced peripherals environment detector
|
|
||||||
rtu_type = rtu_t.env_detector
|
|
||||||
rtu_iface = envd_rtu.new(device)
|
|
||||||
elseif type == ppm.VIRTUAL_DEVICE_TYPE then
|
|
||||||
-- placeholder device
|
|
||||||
rtu_type = "virtual"
|
|
||||||
rtu_iface = rtu.init_unit().interface()
|
|
||||||
else
|
|
||||||
local message = util.c("configure> device '", name, "' is not a known type (", type, ")")
|
|
||||||
println_ts(message)
|
|
||||||
log.fatal(message)
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---@class rtu_unit_registry_entry
|
-- run threads
|
||||||
local rtu_unit = {
|
parallel.waitForAll(table.unpack(_threads))
|
||||||
name = name,
|
else
|
||||||
type = rtu_type,
|
println("configuration failed, exiting...")
|
||||||
index = index,
|
|
||||||
reactor = for_reactor,
|
|
||||||
device = device,
|
|
||||||
formed = formed,
|
|
||||||
rtu = rtu_iface, ---@type rtu_device|rtu_rs_device
|
|
||||||
modbus_io = modbus.new(rtu_iface, true),
|
|
||||||
pkt_queue = mqueue.new(), ---@type mqueue|nil
|
|
||||||
thread = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rtu_unit.thread = threads.thread__unit_comms(__shared_memory, rtu_unit)
|
|
||||||
|
|
||||||
table.insert(units, rtu_unit)
|
|
||||||
|
|
||||||
log.debug(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", rtu_type, ") [", index, "] for reactor ", for_reactor))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we made it through all that trusting-user-to-write-a-config-file chaos
|
println_ts("exited")
|
||||||
return true
|
log.info("exited")
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------
|
if not xpcall(main, crash.handler) then crash.exit() end
|
||||||
-- start system
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
log.debug("boot> running configure()")
|
|
||||||
|
|
||||||
if configure() then
|
|
||||||
-- start connection watchdog
|
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(5)
|
|
||||||
log.debug("boot> conn watchdog started")
|
|
||||||
|
|
||||||
-- setup comms
|
|
||||||
smem_sys.rtu_comms = rtu.comms(RTU_VERSION, smem_dev.modem, config.LISTEN_PORT, config.SERVER_PORT, smem_sys.conn_watchdog)
|
|
||||||
log.debug("boot> comms init")
|
|
||||||
|
|
||||||
-- init threads
|
|
||||||
local main_thread = threads.thread__main(__shared_memory)
|
|
||||||
local comms_thread = threads.thread__comms(__shared_memory)
|
|
||||||
|
|
||||||
-- assemble thread list
|
|
||||||
local _threads = { main_thread.p_exec, comms_thread.p_exec }
|
|
||||||
for i = 1, #units do
|
|
||||||
if units[i].thread ~= nil then
|
|
||||||
table.insert(_threads, units[i].thread.p_exec)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- run threads
|
|
||||||
parallel.waitForAll(table.unpack(_threads))
|
|
||||||
else
|
|
||||||
println("configuration failed, exiting...")
|
|
||||||
end
|
|
||||||
|
|
||||||
println_ts("exited")
|
|
||||||
log.info("exited")
|
|
||||||
|
46
scada-common/crash.lua
Normal file
46
scada-common/crash.lua
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
--
|
||||||
|
-- Crash Handler
|
||||||
|
--
|
||||||
|
|
||||||
|
local comms = require("scada-common.comms")
|
||||||
|
local log = require("scada-common.log")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local crash = {}
|
||||||
|
|
||||||
|
local app = "unknown"
|
||||||
|
local ver = "v0.0.0"
|
||||||
|
local err = ""
|
||||||
|
|
||||||
|
-- set crash environment
|
||||||
|
---@param application string app name
|
||||||
|
---@param version string version
|
||||||
|
function crash.set_env(application, version)
|
||||||
|
app = application
|
||||||
|
ver = version
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle a crash error
|
||||||
|
---@param error string error message
|
||||||
|
function crash.handler(error)
|
||||||
|
err = error
|
||||||
|
log.info("=====> FATAL SOFTWARE FAULT <=====")
|
||||||
|
log.fatal(error)
|
||||||
|
log.info("----------------------------------")
|
||||||
|
log.info(util.c("RUNTIME: ", _HOST))
|
||||||
|
log.info(util.c("LUA VERSION: ", _VERSION))
|
||||||
|
log.info(util.c("APPLICATION: ", app))
|
||||||
|
log.info(util.c("FIRMWARE VERSION: ", ver))
|
||||||
|
log.info(util.c("COMMS VERSION: ", comms.version))
|
||||||
|
log.info("----------------------------------")
|
||||||
|
log.info(debug.traceback("--- begin debug trace ---", 1))
|
||||||
|
log.info("--- end debug trace ---")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- final error print on failed xpcall, app exits here
|
||||||
|
function crash.exit()
|
||||||
|
util.println("fatal error occured in main application:")
|
||||||
|
error(err, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return crash
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
@ -13,7 +14,7 @@ local svsessions = require("supervisor.session.svsessions")
|
|||||||
local config = require("supervisor.config")
|
local config = require("supervisor.config")
|
||||||
local supervisor = require("supervisor.supervisor")
|
local supervisor = require("supervisor.supervisor")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "beta-v0.7.5"
|
local SUPERVISOR_VERSION = "beta-v0.7.6"
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
@ -57,95 +58,105 @@ log.info("BOOTING supervisor.startup " .. SUPERVISOR_VERSION)
|
|||||||
log.info("========================================")
|
log.info("========================================")
|
||||||
println(">> SCADA Supervisor " .. SUPERVISOR_VERSION .. " <<")
|
println(">> SCADA Supervisor " .. SUPERVISOR_VERSION .. " <<")
|
||||||
|
|
||||||
|
crash.set_env("supervisor", SUPERVISOR_VERSION)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- startup
|
-- main application
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
-- mount connected devices
|
local function main()
|
||||||
ppm.mount_all()
|
----------------------------------------
|
||||||
|
-- startup
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
local modem = ppm.get_wireless_modem()
|
-- mount connected devices
|
||||||
if modem == nil then
|
ppm.mount_all()
|
||||||
println("boot> wireless modem not found")
|
|
||||||
log.fatal("no wireless modem on startup")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- start comms, open all channels
|
local modem = ppm.get_wireless_modem()
|
||||||
local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem,
|
if modem == nil then
|
||||||
config.SCADA_DEV_LISTEN, config.SCADA_SV_LISTEN)
|
println("boot> wireless modem not found")
|
||||||
|
log.fatal("no wireless modem on startup")
|
||||||
-- base loop clock (6.67Hz, 3 ticks)
|
return
|
||||||
local MAIN_CLOCK = 0.15
|
|
||||||
local loop_clock = util.new_clock(MAIN_CLOCK)
|
|
||||||
|
|
||||||
-- start clock
|
|
||||||
loop_clock.start()
|
|
||||||
|
|
||||||
-- event loop
|
|
||||||
while true do
|
|
||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
|
||||||
|
|
||||||
-- handle event
|
|
||||||
if event == "peripheral_detach" then
|
|
||||||
local type, device = ppm.handle_unmount(param1)
|
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
|
||||||
if type == "modem" then
|
|
||||||
-- we only care if this is our wireless modem
|
|
||||||
if device == modem then
|
|
||||||
println_ts("wireless modem disconnected!")
|
|
||||||
log.error("comms modem disconnected!")
|
|
||||||
else
|
|
||||||
log.warning("non-comms modem disconnected")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == "peripheral" then
|
|
||||||
local type, device = ppm.mount(param1)
|
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
|
||||||
if type == "modem" then
|
|
||||||
if device.isWireless() then
|
|
||||||
-- reconnected modem
|
|
||||||
modem = device
|
|
||||||
superv_comms.reconnect_modem(modem)
|
|
||||||
|
|
||||||
println_ts("wireless modem reconnected.")
|
|
||||||
log.info("comms modem reconnected.")
|
|
||||||
else
|
|
||||||
log.info("wired modem reconnected.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == "timer" and loop_clock.is_clock(param1) then
|
|
||||||
-- main loop tick
|
|
||||||
|
|
||||||
-- iterate sessions
|
|
||||||
svsessions.iterate_all()
|
|
||||||
|
|
||||||
-- free any closed sessions
|
|
||||||
svsessions.free_all_closed()
|
|
||||||
|
|
||||||
loop_clock.start()
|
|
||||||
elseif event == "timer" then
|
|
||||||
-- a non-clock timer event, check watchdogs
|
|
||||||
svsessions.check_all_watchdogs(param1)
|
|
||||||
elseif event == "modem_message" then
|
|
||||||
-- got a packet
|
|
||||||
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
|
||||||
superv_comms.handle_packet(packet)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- start comms, open all channels
|
||||||
if event == "terminate" or ppm.should_terminate() then
|
local superv_comms = supervisor.comms(SUPERVISOR_VERSION, config.NUM_REACTORS, config.REACTOR_COOLING, modem,
|
||||||
println_ts("closing sessions...")
|
config.SCADA_DEV_LISTEN, config.SCADA_SV_LISTEN)
|
||||||
log.info("terminate requested, closing sessions...")
|
|
||||||
svsessions.close_all()
|
-- base loop clock (6.67Hz, 3 ticks)
|
||||||
log.info("sessions closed")
|
local MAIN_CLOCK = 0.15
|
||||||
break
|
local loop_clock = util.new_clock(MAIN_CLOCK)
|
||||||
|
|
||||||
|
-- start clock
|
||||||
|
loop_clock.start()
|
||||||
|
|
||||||
|
-- event loop
|
||||||
|
while true do
|
||||||
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
|
-- handle event
|
||||||
|
if event == "peripheral_detach" then
|
||||||
|
local type, device = ppm.handle_unmount(param1)
|
||||||
|
|
||||||
|
if type ~= nil and device ~= nil then
|
||||||
|
if type == "modem" then
|
||||||
|
-- we only care if this is our wireless modem
|
||||||
|
if device == modem then
|
||||||
|
println_ts("wireless modem disconnected!")
|
||||||
|
log.error("comms modem disconnected!")
|
||||||
|
else
|
||||||
|
log.warning("non-comms modem disconnected")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "peripheral" then
|
||||||
|
local type, device = ppm.mount(param1)
|
||||||
|
|
||||||
|
if type ~= nil and device ~= nil then
|
||||||
|
if type == "modem" then
|
||||||
|
if device.isWireless() then
|
||||||
|
-- reconnected modem
|
||||||
|
modem = device
|
||||||
|
superv_comms.reconnect_modem(modem)
|
||||||
|
|
||||||
|
println_ts("wireless modem reconnected.")
|
||||||
|
log.info("comms modem reconnected.")
|
||||||
|
else
|
||||||
|
log.info("wired modem reconnected.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "timer" and loop_clock.is_clock(param1) then
|
||||||
|
-- main loop tick
|
||||||
|
|
||||||
|
-- iterate sessions
|
||||||
|
svsessions.iterate_all()
|
||||||
|
|
||||||
|
-- free any closed sessions
|
||||||
|
svsessions.free_all_closed()
|
||||||
|
|
||||||
|
loop_clock.start()
|
||||||
|
elseif event == "timer" then
|
||||||
|
-- a non-clock timer event, check watchdogs
|
||||||
|
svsessions.check_all_watchdogs(param1)
|
||||||
|
elseif event == "modem_message" then
|
||||||
|
-- got a packet
|
||||||
|
local packet = superv_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
superv_comms.handle_packet(packet)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request
|
||||||
|
if event == "terminate" or ppm.should_terminate() then
|
||||||
|
println_ts("closing sessions...")
|
||||||
|
log.info("terminate requested, closing sessions...")
|
||||||
|
svsessions.close_all()
|
||||||
|
log.info("sessions closed")
|
||||||
|
break
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
println_ts("exited")
|
||||||
|
log.info("exited")
|
||||||
end
|
end
|
||||||
|
|
||||||
println_ts("exited")
|
if not xpcall(main, crash.handler) then crash.exit() end
|
||||||
log.info("exited")
|
|
||||||
|
Loading…
Reference in New Issue
Block a user