2022-01-14 21:33:09 +00:00
|
|
|
--
|
|
|
|
-- Reactor Programmable Logic Controller
|
|
|
|
--
|
|
|
|
|
2022-05-14 17:32:42 +00:00
|
|
|
require("/initenv").init_env()
|
|
|
|
|
2023-10-01 21:10:16 +00:00
|
|
|
local comms = require("scada-common.comms")
|
|
|
|
local crash = require("scada-common.crash")
|
|
|
|
local log = require("scada-common.log")
|
|
|
|
local mqueue = require("scada-common.mqueue")
|
|
|
|
local network = require("scada-common.network")
|
|
|
|
local ppm = require("scada-common.ppm")
|
|
|
|
local util = require("scada-common.util")
|
|
|
|
|
|
|
|
local configure = require("reactor-plc.configure")
|
|
|
|
local databus = require("reactor-plc.databus")
|
|
|
|
local plc = require("reactor-plc.plc")
|
|
|
|
local renderer = require("reactor-plc.renderer")
|
|
|
|
local threads = require("reactor-plc.threads")
|
|
|
|
|
2024-07-28 20:41:39 +00:00
|
|
|
local R_PLC_VERSION = "v1.8.2"
|
2022-01-14 21:33:09 +00:00
|
|
|
|
2022-04-05 21:29:27 +00:00
|
|
|
local println = util.println
|
|
|
|
local println_ts = util.println_ts
|
2022-01-14 21:33:09 +00:00
|
|
|
|
2022-06-05 19:09:02 +00:00
|
|
|
----------------------------------------
|
2023-10-01 21:10:16 +00:00
|
|
|
-- get configuration
|
2022-06-05 19:09:02 +00:00
|
|
|
----------------------------------------
|
|
|
|
|
2023-10-01 21:10:16 +00:00
|
|
|
if not plc.load_config() then
|
|
|
|
-- try to reconfigure (user action)
|
|
|
|
local success, error = configure.configure(true)
|
|
|
|
if success then
|
2024-03-06 01:17:52 +00:00
|
|
|
if not plc.load_config() then
|
|
|
|
println("failed to load a valid configuration, please reconfigure")
|
|
|
|
return
|
|
|
|
end
|
2023-10-01 21:10:16 +00:00
|
|
|
else
|
2024-03-06 01:17:52 +00:00
|
|
|
println("configuration error: " .. error)
|
|
|
|
return
|
2023-04-07 12:05:14 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-10-01 21:10:16 +00:00
|
|
|
local config = plc.config
|
|
|
|
|
2022-06-05 19:09:02 +00:00
|
|
|
----------------------------------------
|
|
|
|
-- log init
|
|
|
|
----------------------------------------
|
|
|
|
|
2023-10-01 21:10:16 +00:00
|
|
|
log.init(config.LogPath, config.LogMode, config.LogDebug)
|
2022-04-29 17:32:37 +00:00
|
|
|
|
2022-05-04 17:37:01 +00:00
|
|
|
log.info("========================================")
|
|
|
|
log.info("BOOTING reactor-plc.startup " .. R_PLC_VERSION)
|
|
|
|
log.info("========================================")
|
2022-04-05 21:29:27 +00:00
|
|
|
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
|
2022-04-02 15:22:44 +00:00
|
|
|
|
2023-10-14 16:02:25 +00:00
|
|
|
crash.set_env("reactor-plc", R_PLC_VERSION)
|
2024-03-23 04:49:19 +00:00
|
|
|
crash.dbg_log_env()
|
2022-11-13 20:56:27 +00:00
|
|
|
|
2022-06-05 19:09:02 +00:00
|
|
|
----------------------------------------
|
2022-11-13 20:56:27 +00:00
|
|
|
-- main application
|
2022-06-05 19:09:02 +00:00
|
|
|
----------------------------------------
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
local function main()
|
|
|
|
----------------------------------------
|
|
|
|
-- startup
|
|
|
|
----------------------------------------
|
|
|
|
|
2023-04-08 20:49:54 +00:00
|
|
|
-- record firmware versions and ID
|
|
|
|
databus.tx_versions(R_PLC_VERSION, comms.version)
|
2023-10-01 21:10:16 +00:00
|
|
|
databus.tx_id(config.UnitID)
|
2023-04-08 20:49:54 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- mount connected devices
|
|
|
|
ppm.mount_all()
|
|
|
|
|
2023-06-25 18:00:18 +00:00
|
|
|
-- message authentication init
|
2023-10-14 14:07:56 +00:00
|
|
|
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
2023-10-01 21:10:16 +00:00
|
|
|
network.init_mac(config.AuthKey)
|
2023-06-25 18:00:18 +00:00
|
|
|
end
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- shared memory across threads
|
|
|
|
---@class plc_shared_memory
|
|
|
|
local __shared_memory = {
|
|
|
|
-- networked setting
|
2023-10-01 21:10:16 +00:00
|
|
|
networked = config.Networked,
|
2022-11-13 20:56:27 +00:00
|
|
|
|
|
|
|
-- PLC system state flags
|
|
|
|
---@class plc_state
|
|
|
|
plc_state = {
|
2023-10-01 21:10:16 +00:00
|
|
|
init_ok = true,
|
|
|
|
fp_ok = false,
|
|
|
|
shutdown = false,
|
|
|
|
degraded = true,
|
2022-11-13 20:56:27 +00:00
|
|
|
reactor_formed = true,
|
2023-10-01 21:10:16 +00:00
|
|
|
no_reactor = true,
|
|
|
|
no_modem = true
|
2022-11-13 20:56:27 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
-- control setpoints
|
|
|
|
---@class setpoints
|
|
|
|
setpoints = {
|
|
|
|
burn_rate_en = false,
|
|
|
|
burn_rate = 0.0
|
|
|
|
},
|
|
|
|
|
|
|
|
-- core PLC devices
|
|
|
|
plc_dev = {
|
|
|
|
reactor = ppm.get_fission_reactor(),
|
|
|
|
modem = ppm.get_wireless_modem()
|
|
|
|
},
|
|
|
|
|
|
|
|
-- system objects
|
|
|
|
plc_sys = {
|
2023-10-04 02:52:13 +00:00
|
|
|
rps = nil, ---@type rps
|
|
|
|
nic = nil, ---@type nic
|
|
|
|
plc_comms = nil, ---@type plc_comms
|
|
|
|
conn_watchdog = nil ---@type watchdog
|
2022-11-13 20:56:27 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
-- message queues
|
|
|
|
q = {
|
|
|
|
mq_rps = mqueue.new(),
|
|
|
|
mq_comms_tx = mqueue.new(),
|
|
|
|
mq_comms_rx = mqueue.new()
|
2023-04-08 20:49:54 +00:00
|
|
|
}
|
2022-04-25 15:40:53 +00:00
|
|
|
}
|
2022-04-05 19:56:48 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
local smem_dev = __shared_memory.plc_dev
|
|
|
|
local smem_sys = __shared_memory.plc_sys
|
2022-04-03 16:08:22 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
local plc_state = __shared_memory.plc_state
|
2022-04-05 21:29:27 +00:00
|
|
|
|
2023-06-23 17:52:24 +00:00
|
|
|
-- initial state evaluation
|
|
|
|
plc_state.no_reactor = smem_dev.reactor == nil
|
|
|
|
plc_state.no_modem = smem_dev.modem == nil
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- we need a reactor, can at least do some things even if it isn't formed though
|
2023-06-23 17:52:24 +00:00
|
|
|
if plc_state.no_reactor then
|
2024-03-05 15:56:27 +00:00
|
|
|
println("init> fission reactor not found")
|
2023-02-22 04:50:43 +00:00
|
|
|
log.warning("init> no reactor on startup")
|
2022-10-25 17:29:57 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
plc_state.init_ok = false
|
|
|
|
plc_state.degraded = true
|
|
|
|
elseif not smem_dev.reactor.isFormed() then
|
2024-03-05 15:56:27 +00:00
|
|
|
println("init> fission reactor is not formed")
|
2023-02-22 04:50:43 +00:00
|
|
|
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
2022-04-05 21:29:27 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
plc_state.degraded = true
|
|
|
|
plc_state.reactor_formed = false
|
2022-04-05 13:41:06 +00:00
|
|
|
end
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- modem is required if networked
|
2023-06-23 17:52:24 +00:00
|
|
|
if __shared_memory.networked and plc_state.no_modem then
|
2023-02-22 04:50:43 +00:00
|
|
|
println("init> wireless modem not found")
|
|
|
|
log.warning("init> no wireless modem on startup")
|
2022-01-14 21:33:09 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- scram reactor if present and enabled
|
|
|
|
if (smem_dev.reactor ~= nil) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
2022-10-25 17:29:57 +00:00
|
|
|
smem_dev.reactor.scram()
|
|
|
|
end
|
2022-04-05 20:09:29 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
plc_state.init_ok = false
|
|
|
|
plc_state.degraded = true
|
|
|
|
end
|
2022-05-09 19:00:16 +00:00
|
|
|
|
2023-04-08 04:38:46 +00:00
|
|
|
-- print a log message to the terminal as long as the UI isn't running
|
2023-04-08 20:49:54 +00:00
|
|
|
local function _println_no_fp(message) if not plc_state.fp_ok then println(message) end end
|
2023-04-08 04:38:46 +00:00
|
|
|
|
2023-02-21 21:57:33 +00:00
|
|
|
-- PLC init<br>
|
2022-11-13 20:56:27 +00:00
|
|
|
--- EVENT_CONSUMER: this function consumes events
|
|
|
|
local function init()
|
2023-04-08 04:38:46 +00:00
|
|
|
-- just booting up, no fission allowed (neutrons stay put thanks)
|
|
|
|
if (not plc_state.no_reactor) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
|
|
|
smem_dev.reactor.scram()
|
|
|
|
end
|
2023-04-07 02:10:33 +00:00
|
|
|
|
2023-04-08 04:38:46 +00:00
|
|
|
-- front panel time!
|
|
|
|
if not renderer.ui_ready() then
|
2023-04-12 20:02:29 +00:00
|
|
|
local message
|
2024-03-12 01:25:34 +00:00
|
|
|
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
|
2023-04-12 20:02:29 +00:00
|
|
|
|
2024-03-23 04:26:58 +00:00
|
|
|
-- ...or not
|
2023-04-08 04:38:46 +00:00
|
|
|
if not plc_state.fp_ok then
|
|
|
|
println_ts(util.c("UI error: ", message))
|
|
|
|
println("init> running without front panel")
|
2023-07-11 19:15:44 +00:00
|
|
|
log.error(util.c("front panel GUI render failed with error ", message))
|
2023-04-08 04:38:46 +00:00
|
|
|
log.info("init> running in headless mode without front panel")
|
2022-11-13 20:56:27 +00:00
|
|
|
end
|
2023-04-08 04:38:46 +00:00
|
|
|
end
|
2022-11-13 20:56:27 +00:00
|
|
|
|
2023-04-08 04:38:46 +00:00
|
|
|
if plc_state.init_ok then
|
2022-11-13 20:56:27 +00:00
|
|
|
-- init reactor protection system
|
2023-10-01 21:10:16 +00:00
|
|
|
smem_sys.rps = plc.rps_init(smem_dev.reactor, plc_state.reactor_formed)
|
2022-11-13 20:56:27 +00:00
|
|
|
log.debug("init> rps init")
|
|
|
|
|
|
|
|
if __shared_memory.networked then
|
2023-02-13 17:27:22 +00:00
|
|
|
-- comms watchdog
|
2023-10-01 21:10:16 +00:00
|
|
|
smem_sys.conn_watchdog = util.new_watchdog(config.ConnTimeout)
|
2022-11-13 20:56:27 +00:00
|
|
|
log.debug("init> conn watchdog started")
|
|
|
|
|
2023-06-25 16:59:38 +00:00
|
|
|
-- create network interface then setup comms
|
2023-06-23 17:52:24 +00:00
|
|
|
smem_sys.nic = network.nic(smem_dev.modem)
|
2023-10-01 21:10:16 +00:00
|
|
|
smem_sys.plc_comms = plc.comms(R_PLC_VERSION, smem_sys.nic, smem_dev.reactor, smem_sys.rps, smem_sys.conn_watchdog)
|
2022-11-13 20:56:27 +00:00
|
|
|
log.debug("init> comms init")
|
|
|
|
else
|
2023-04-08 20:49:54 +00:00
|
|
|
_println_no_fp("init> starting in offline mode")
|
2023-02-21 21:57:33 +00:00
|
|
|
log.info("init> running without networking")
|
2022-11-13 20:56:27 +00:00
|
|
|
end
|
|
|
|
|
2023-04-07 12:05:14 +00:00
|
|
|
-- notify user of emergency coolant configuration status
|
2023-10-01 21:10:16 +00:00
|
|
|
if config.EmerCoolEnable then
|
2023-04-07 12:05:14 +00:00
|
|
|
println("init> emergency coolant control ready")
|
|
|
|
log.info("init> running with emergency coolant control available")
|
|
|
|
end
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
util.push_event("clock_start")
|
|
|
|
|
2023-04-08 20:49:54 +00:00
|
|
|
_println_no_fp("init> completed")
|
2023-02-22 04:50:43 +00:00
|
|
|
log.info("init> startup completed")
|
2022-04-05 20:09:29 +00:00
|
|
|
else
|
2023-04-08 20:49:54 +00:00
|
|
|
_println_no_fp("init> system in degraded state, awaiting devices...")
|
2023-02-22 04:50:43 +00:00
|
|
|
log.warning("init> started in a degraded state, awaiting peripheral connections...")
|
2022-04-05 20:09:29 +00:00
|
|
|
end
|
2023-04-08 04:38:46 +00:00
|
|
|
|
2023-04-08 20:49:54 +00:00
|
|
|
databus.tx_hw_status(plc_state)
|
2022-04-05 19:56:48 +00:00
|
|
|
end
|
2022-04-02 15:22:44 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
----------------------------------------
|
|
|
|
-- start system
|
|
|
|
----------------------------------------
|
2022-05-09 19:00:16 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- initialize PLC
|
|
|
|
init()
|
2022-04-05 20:09:29 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- init threads
|
|
|
|
local main_thread = threads.thread__main(__shared_memory, init)
|
|
|
|
local rps_thread = threads.thread__rps(__shared_memory)
|
2022-04-03 16:08:22 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
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)
|
2022-04-29 02:36:45 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- setpoint control only needed when networked
|
|
|
|
local sp_ctrl_thread = threads.thread__setpoint_control(__shared_memory)
|
2022-04-30 02:27:54 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- 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)
|
2022-05-04 14:00:21 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
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()
|
2022-05-04 14:00:21 +00:00
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
-- close connection
|
|
|
|
smem_sys.plc_comms.close()
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- run threads, excluding comms
|
|
|
|
parallel.waitForAll(main_thread.p_exec, rps_thread.p_exec)
|
2022-05-04 14:00:21 +00:00
|
|
|
end
|
2022-11-13 20:56:27 +00:00
|
|
|
|
2023-04-07 02:10:33 +00:00
|
|
|
renderer.close_ui()
|
|
|
|
|
2022-11-13 20:56:27 +00:00
|
|
|
println_ts("exited")
|
|
|
|
log.info("exited")
|
2022-04-27 19:01:10 +00:00
|
|
|
end
|
2022-04-18 14:31:24 +00:00
|
|
|
|
2023-04-21 01:19:16 +00:00
|
|
|
if not xpcall(main, crash.handler) then
|
|
|
|
pcall(renderer.close_ui)
|
|
|
|
crash.exit()
|
|
|
|
else
|
|
|
|
log.close()
|
|
|
|
end
|