#634 work on reactor PLC backplane

This commit is contained in:
Mikayla Fischler 2025-10-26 22:19:37 -04:00
parent 5acc6470e3
commit cddd9f7437
5 changed files with 153 additions and 134 deletions

View File

@ -49,6 +49,8 @@ function backplane.init(config, __shared_memory)
log.info("BKPLN: WIRED PHY_" .. util.trinary(modem, "UP ", "DOWN ") .. _bp.lan_iface) log.info("BKPLN: WIRED PHY_" .. util.trinary(modem, "UP ", "DOWN ") .. _bp.lan_iface)
plc_state.wd_modem = _bp.wd_nic.is_connected()
-- set this as active for now -- set this as active for now
_bp.wl_act = false _bp.wl_act = false
_bp.act_nic = _bp.wd_nic _bp.act_nic = _bp.wd_nic
@ -61,6 +63,8 @@ function backplane.init(config, __shared_memory)
log.info("BKPLN: WIRELESS PHY_" .. util.trinary(modem, "UP ", "DOWN ") .. iface) log.info("BKPLN: WIRELESS PHY_" .. util.trinary(modem, "UP ", "DOWN ") .. iface)
plc_state.wl_modem = _bp.wl_nic.is_connected()
-- set this as active if connected or if both modems are disconnected and this is preferred -- set this as active if connected or if both modems are disconnected and this is preferred
if (modem and _bp.wlan_pref) or not (_bp.act_nic and _bp.act_nic.is_connected()) then if (modem and _bp.wlan_pref) or not (_bp.act_nic and _bp.act_nic.is_connected()) then
_bp.wl_act = true _bp.wl_act = true
@ -68,12 +72,8 @@ function backplane.init(config, __shared_memory)
end end
end end
plc_state.no_modem = not _bp.act_nic.is_connected()
databus.tx_hw_modem(not plc_state.no_modem)
-- comms modem is required if networked -- comms modem is required if networked
if plc_state.no_modem then if not (plc_state.wd_modem or plc_state.wl_modem) then
println("startup> comms modem not found") println("startup> comms modem not found")
log.warning("BKPLN: no comms modem on startup") log.warning("BKPLN: no comms modem on startup")
@ -121,11 +121,14 @@ function backplane.active_nic() return _bp.act_nic end
---@param device table ---@param device table
---@param print_no_fp function ---@param print_no_fp function
function backplane.attach(iface, type, device, print_no_fp) function backplane.attach(iface, type, device, print_no_fp)
local MQ__RPS_CMD = _bp.smem.q_cmds.MQ__RPS_CMD
local networked = _bp.smem.networked local networked = _bp.smem.networked
local state = _bp.smem.plc_state local state = _bp.smem.plc_state
local dev = _bp.smem.plc_dev local dev = _bp.smem.plc_dev
local sys = _bp.smem.plc_sys local sys = _bp.smem.plc_sys
if type ~= nil and device ~= nil then
if state.no_reactor and (type == "fissionReactorLogicAdapter") then if state.no_reactor and (type == "fissionReactorLogicAdapter") then
-- reconnected reactor -- reconnected reactor
dev.reactor = device dev.reactor = device
@ -139,7 +142,7 @@ function backplane.attach(iface, type, device, print_no_fp)
state.reactor_formed = true state.reactor_formed = true
-- determine if we are still in a degraded state -- determine if we are still in a degraded state
if (not networked or not state.no_modem) and state.reactor_formed then if ((not networked) or (state.wd_modem or state.wl_modem)) and state.reactor_formed then
state.degraded = false state.degraded = false
end end
@ -152,30 +155,72 @@ function backplane.attach(iface, type, device, print_no_fp)
-- partial reset of RPS, specific to becoming formed/reconnected -- partial reset of RPS, specific to becoming formed/reconnected
-- without this, auto control can't resume on chunk load -- without this, auto control can't resume on chunk load
sys.rps.reset_formed() sys.rps.reset_reattach()
elseif networked and type == "modem" then elseif networked and type == "modem" then
---@cast device Modem ---@cast device Modem
local is_comms_modem = util.trinary(dev.modem_wired, dev.modem_iface == iface, device.isWireless())
-- note, check init_ok first since nic will be nil if it is false local m_is_wl = device.isWireless()
if is_comms_modem and not (state.init_ok and nic.is_connected()) then
-- reconnected modem
dev.modem = device
state.no_modem = false
if state.init_ok then nic.connect(device) end log.info(util.c("BKPLN: ", util.trinary(m_is_wl, "WIRELESS", "WIRED"), " PHY_ATTACH ", iface))
print_no_fp("comms modem reconnected") local is_wd = _bp.wd_nic and (_bp.lan_iface == iface)
log.info("comms modem reconnected") local is_wl = _bp.wl_nic and (not _bp.wl_nic.is_connected()) and m_is_wl
if is_wd then
-- connect this as the wired NIC
_bp.wd_nic.connect(device)
log.info("BKPLN: WIRED PHY_UP " .. iface)
print_no_fp("wired comms modem reconnected")
state.wd_modem = true
if _bp.act_nic == _bp.wd_nic then
-- set as active
_bp.wl_act = false
_bp.act_nic = _bp.wd_nic
elseif _bp.wl_act and not _bp.wlan_pref then
-- switch back to preferred wired
_bp.wl_act = false
_bp.act_nic = _bp.wd_nic
sys.plc_comms.switch_nic(_bp.act_nic)
log.info("BKPLN: switched comms to wired modem (preferred)")
end
elseif is_wl then
-- connect this as the wireless NIC
_bp.wl_nic.connect(device)
log.info("BKPLN: WIRELESS PHY_UP " .. iface)
print_no_fp("wireless comms modem reconnected")
state.wl_modem = true
if _bp.act_nic == _bp.wl_nic then
-- set as active
_bp.wl_act = true
_bp.act_nic = _bp.wl_nic
elseif (not _bp.wl_act) and _bp.wlan_pref then
-- switch back to preferred wireless
_bp.wl_act = true
_bp.act_nic = _bp.wl_nic
sys.plc_comms.switch_nic(_bp.act_nic)
log.info("BKPLN: switched comms to wireless modem (preferred)")
end
elseif _bp.wl_nic and m_is_wl then
-- the wireless NIC already has a modem
print_no_fp("standby wireless modem connected")
log.info("BKPLN: standby wireless modem connected")
else
print_no_fp("unassigned modem connected")
log.warning("BKPLN: unassigned modem connected")
end
-- determine if we are still in a degraded state -- determine if we are still in a degraded state
if not state.no_reactor then if (state.wd_modem or state.wl_modem) and state.reactor_formed and not state.no_reactor then
state.degraded = false state.degraded = false
end end
elseif device.isWireless() then
log.info("unused wireless modem connected")
else
log.info("non-comms wired modem connected")
end end
end end
end end

View File

@ -830,6 +830,22 @@ function plc.comms(version, nic, reactor, rps, conn_watchdog)
---@class plc_comms ---@class plc_comms
local public = {} local public = {}
-- switch the current active NIC
---@param _nic nic
function public.switch_nic(_nic)
nic.closeAll()
if _nic.isWireless() then
comms.set_trusted_range(config.TrustedRange)
end
-- configure receive channels
_nic.closeAll()
_nic.open(config.PLC_Channel)
nic = _nic
end
-- reconnect a newly connected reactor -- reconnect a newly connected reactor
---@param new_reactor table ---@param new_reactor table
function public.reconnect_reactor(new_reactor) function public.reconnect_reactor(new_reactor)

View File

@ -18,15 +18,14 @@ local ui = {
} }
-- try to start the UI -- try to start the UI
---@param theme FP_THEME front panel theme ---@param config plc_config configuration
---@param color_mode COLOR_MODE color mode
---@return boolean success, any error_msg ---@return boolean success, any error_msg
function renderer.try_start_ui(theme, color_mode) function renderer.try_start_ui(config)
local status, msg = true, nil local status, msg = true, nil
if ui.display == nil then if ui.display == nil then
-- set theme -- set theme
style.set_theme(theme, color_mode) style.set_theme(config.FrontPanelTheme, config.ColorMode)
-- reset terminal -- reset terminal
term.setTextColor(colors.white) term.setTextColor(colors.white)
@ -40,7 +39,7 @@ function renderer.try_start_ui(theme, color_mode)
end end
-- apply color mode -- apply color mode
local c_mode_overrides = style.theme.color_modes[color_mode] local c_mode_overrides = style.theme.color_modes[config.ColorMode]
for i = 1, #c_mode_overrides do for i = 1, #c_mode_overrides do
term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex) term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
end end
@ -48,7 +47,7 @@ function renderer.try_start_ui(theme, color_mode)
-- init front panel view -- init front panel view
status, msg = pcall(function () status, msg = pcall(function ()
ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root} ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root}
panel_view(ui.display) panel_view(ui.display, config)
end) end)
if status then if status then

View File

@ -91,9 +91,10 @@ local function main()
fp_ok = false, fp_ok = false,
shutdown = false, shutdown = false,
degraded = true, degraded = true,
reactor_formed = true,
no_reactor = true, no_reactor = true,
no_modem = true reactor_formed = true,
wd_modem = false,
wl_modem = false
}, },
-- control setpoints -- control setpoints
@ -123,6 +124,18 @@ local function main()
mq_rps = mqueue.new(), mq_rps = mqueue.new(),
mq_comms_tx = mqueue.new(), mq_comms_tx = mqueue.new(),
mq_comms_rx = mqueue.new() mq_comms_rx = mqueue.new()
},
-- message queue commands
q_cmds = {
MQ__RPS_CMD = {
SCRAM = 1,
DEGRADED_SCRAM = 2,
TRIP_TIMEOUT = 3
},
MQ__COMM_CMD = {
SEND_STATUS = 1
}
} }
} }
@ -142,7 +155,7 @@ local function main()
-- setup front panel -- setup front panel
local message local message
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode) plc_state.fp_ok, message = renderer.try_start_ui(config)
-- ...or not -- ...or not
if not plc_state.fp_ok then if not plc_state.fp_ok then

View File

@ -4,6 +4,7 @@ local ppm = require("scada-common.ppm")
local tcd = require("scada-common.tcd") local tcd = require("scada-common.tcd")
local util = require("scada-common.util") local util = require("scada-common.util")
local backplane = require("reactor-plc.backplane")
local databus = require("reactor-plc.databus") local databus = require("reactor-plc.databus")
local renderer = require("reactor-plc.renderer") local renderer = require("reactor-plc.renderer")
@ -18,16 +19,6 @@ local SP_CTRL_SLEEP = 250 -- 250ms, 5 ticks
local BURN_RATE_RAMP_mB_s = 5.0 local BURN_RATE_RAMP_mB_s = 5.0
local MQ__RPS_CMD = {
SCRAM = 1,
DEGRADED_SCRAM = 2,
TRIP_TIMEOUT = 3
}
local MQ__COMM_CMD = {
SEND_STATUS = 1
}
-- main thread -- main thread
---@nodiscard ---@nodiscard
---@param smem plc_shared_memory ---@param smem plc_shared_memory
@ -54,6 +45,9 @@ function threads.thread__main(smem)
local plc_state = smem.plc_state local plc_state = smem.plc_state
local plc_dev = smem.plc_dev local plc_dev = smem.plc_dev
local MQ__RPS_CMD = smem.q_cmds.MQ__RPS_CMD
local MQ__COMM_CMD = smem.q_cmds.MQ__COMM_CMD
-- start clock -- start clock
loop_clock.start() loop_clock.start()
@ -175,60 +169,8 @@ function threads.thread__main(smem)
elseif event == "peripheral" then elseif event == "peripheral" then
-- peripheral connect -- peripheral connect
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 plc_state.no_reactor and (type == "fissionReactorLogicAdapter") then backplane.attach(param1, type, device, println_ts)
-- reconnected reactor
plc_dev.reactor = device
plc_state.no_reactor = false
println_ts("reactor reconnected")
log.info("reactor reconnected")
-- we need to assume formed here as we cannot check in this main loop
-- RPS will identify if it isn't and this will get set false later
plc_state.reactor_formed = true
-- determine if we are still in a degraded state
if (not networked or not plc_state.no_modem) and plc_state.reactor_formed then
plc_state.degraded = false
end
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
rps.reconnect_reactor(plc_dev.reactor)
if networked then
plc_comms.reconnect_reactor(plc_dev.reactor)
end
-- partial reset of RPS, specific to becoming formed/reconnected
-- without this, auto control can't resume on chunk load
rps.reset_reattach()
elseif networked and type == "modem" then
---@cast device Modem
local is_comms_modem = util.trinary(plc_dev.modem_wired, plc_dev.modem_iface == param1, device.isWireless())
-- note, check init_ok first since nic will be nil if it is false
if is_comms_modem and not nic.is_connected() then
-- reconnected modem
plc_dev.modem = device
plc_state.no_modem = false
nic.connect(device)
println_ts("comms modem reconnected")
log.info("comms modem reconnected")
-- determine if we are still in a degraded state
if plc_state.reactor_formed and not plc_state.no_reactor then
plc_state.degraded = false
end
elseif device.isWireless() then
log.info("unused wireless modem connected")
else
log.info("non-comms wired modem connected")
end
end
end end
-- update indicators -- update indicators
@ -295,6 +237,8 @@ function threads.thread__rps(smem)
local rps_queue = smem.q.mq_rps local rps_queue = smem.q.mq_rps
local MQ__RPS_CMD = smem.q_cmds.MQ__RPS_CMD
local was_linked = false local was_linked = false
local last_update = util.time() local last_update = util.time()
@ -428,6 +372,8 @@ function threads.thread__comms_tx(smem)
local plc_state = smem.plc_state local plc_state = smem.plc_state
local comms_queue = smem.q.mq_comms_tx local comms_queue = smem.q.mq_comms_tx
local MQ__COMM_CMD = smem.q_cmds.MQ__COMM_CMD
local last_update = util.time() local last_update = util.time()
-- thread loop -- thread loop