commit
b1ad2084f2
@ -550,7 +550,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, ext, style)
|
|||||||
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
if (string.len(val) > val_max_w) or string.find(val, "\n") then
|
||||||
local lines = util.strwrap(val, inner_width)
|
local lines = util.strwrap(val, inner_width)
|
||||||
height = #lines + 1
|
height = #lines + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@ -380,6 +380,18 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
_send_sv(PROTOCOL.SCADA_MGMT, MGMT_TYPE.CLOSE, {})
|
_send_sv(PROTOCOL.SCADA_MGMT, MGMT_TYPE.CLOSE, {})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- send the resume ready state to the supervisor
|
||||||
|
---@param mode PROCESS process control mode
|
||||||
|
---@param burn_target number burn rate target
|
||||||
|
---@param charge_target number charge level target
|
||||||
|
---@param gen_target number generation rate target
|
||||||
|
---@param limits number[] unit burn rate limits
|
||||||
|
function public.send_ready(mode, burn_target, charge_target, gen_target, limits)
|
||||||
|
_send_sv(PROTOCOL.SCADA_CRDN, CRDN_TYPE.PROCESS_READY, {
|
||||||
|
mode, burn_target, charge_target, gen_target, limits
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
-- send a facility command
|
-- send a facility command
|
||||||
---@param cmd FAC_COMMAND command
|
---@param cmd FAC_COMMAND command
|
||||||
---@param option any? optional option options for the optional options (like waste mode)
|
---@param option any? optional option options for the optional options (like waste mode)
|
||||||
|
|||||||
@ -164,6 +164,7 @@ function iocontrol.init(conf, comms, temp_scale, energy_scale)
|
|||||||
num_turbines = 0,
|
num_turbines = 0,
|
||||||
num_snas = 0,
|
num_snas = 0,
|
||||||
has_tank = conf.cooling.r_cool[i].TankConnection,
|
has_tank = conf.cooling.r_cool[i].TankConnection,
|
||||||
|
aux_coolant = conf.cooling.aux_coolant[i],
|
||||||
|
|
||||||
status_lines = { "", "" },
|
status_lines = { "", "" },
|
||||||
|
|
||||||
@ -1214,7 +1215,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local valve_states = status[6]
|
local valve_states = status[6]
|
||||||
|
|
||||||
if type(valve_states) == "table" then
|
if type(valve_states) == "table" then
|
||||||
if #valve_states == 5 then
|
if #valve_states == 6 then
|
||||||
unit.unit_ps.publish("V_pu_conn", valve_states[1] > 0)
|
unit.unit_ps.publish("V_pu_conn", valve_states[1] > 0)
|
||||||
unit.unit_ps.publish("V_pu_state", valve_states[1] == 2)
|
unit.unit_ps.publish("V_pu_state", valve_states[1] == 2)
|
||||||
unit.unit_ps.publish("V_po_conn", valve_states[2] > 0)
|
unit.unit_ps.publish("V_po_conn", valve_states[2] > 0)
|
||||||
@ -1225,6 +1226,8 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
unit.unit_ps.publish("V_am_state", valve_states[4] == 2)
|
unit.unit_ps.publish("V_am_state", valve_states[4] == 2)
|
||||||
unit.unit_ps.publish("V_emc_conn", valve_states[5] > 0)
|
unit.unit_ps.publish("V_emc_conn", valve_states[5] > 0)
|
||||||
unit.unit_ps.publish("V_emc_state", valve_states[5] == 2)
|
unit.unit_ps.publish("V_emc_state", valve_states[5] == 2)
|
||||||
|
unit.unit_ps.publish("V_aux_conn", valve_states[6] > 0)
|
||||||
|
unit.unit_ps.publish("V_aux_state", valve_states[6] == 2)
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "valve states length mismatch")
|
log.debug(log_header .. "valve states length mismatch")
|
||||||
valid = false
|
valid = false
|
||||||
|
|||||||
@ -139,6 +139,11 @@ function process.init(iocontrol, coord_comms)
|
|||||||
|
|
||||||
log.info("PROCESS: loaded priority groups settings")
|
log.info("PROCESS: loaded priority groups settings")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- report to the supervisor all initial configuration data has been sent
|
||||||
|
-- startup resume can occur if needed
|
||||||
|
local p = ctl_proc
|
||||||
|
pctl.comms.send_ready(p.mode, p.burn_target, p.charge_target, p.gen_target, p.limits)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create a handle to process control for usage of commands that get acknowledgements
|
-- create a handle to process control for usage of commands that get acknowledgements
|
||||||
|
|||||||
@ -137,7 +137,7 @@ function renderer.try_start_fp()
|
|||||||
if not engine.fp_ready then
|
if not engine.fp_ready then
|
||||||
-- show front panel view on terminal
|
-- show front panel view on terminal
|
||||||
status, msg = pcall(function ()
|
status, msg = pcall(function ()
|
||||||
engine.ui.front_panel = DisplayBox{window=term.native(),fg_bg=style.fp.root}
|
engine.ui.front_panel = DisplayBox{window=term.current(),fg_bg=style.fp.root}
|
||||||
panel_view(engine.ui.front_panel, #engine.monitors.unit_displays)
|
panel_view(engine.ui.front_panel, #engine.monitors.unit_displays)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ local renderer = require("coordinator.renderer")
|
|||||||
local sounder = require("coordinator.sounder")
|
local sounder = require("coordinator.sounder")
|
||||||
local threads = require("coordinator.threads")
|
local threads = require("coordinator.threads")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v1.6.4"
|
local COORDINATOR_VERSION = "v1.6.11"
|
||||||
|
|
||||||
local CHUNK_LOAD_DELAY_S = 30.0
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,8 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
|
local term_w, _ = term.getSize()
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=s_hi_bright}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=s_hi_bright}
|
||||||
@ -43,9 +45,9 @@ local function init(parent, id)
|
|||||||
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
||||||
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4}
|
TextBox{parent=entry,x=term_w-16,y=2,text="RTT:",width=4}
|
||||||
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local pkt_rtt = DataIndicator{parent=entry,x=term_w-11,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
TextBox{parent=entry,x=term_w-5,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@ -59,7 +59,7 @@ local function make(parent, x, y, wide, unit_id)
|
|||||||
local tank_conns = facility.tank_conns
|
local tank_conns = facility.tank_conns
|
||||||
local tank_types = facility.tank_fluid_types
|
local tank_types = facility.tank_fluid_types
|
||||||
|
|
||||||
local v_start = 1 + ((unit.unit_id - 1) * 5)
|
local v_start = 1 + ((unit.unit_id - 1) * 6)
|
||||||
local prv_start = 1 + ((unit.unit_id - 1) * 3)
|
local prv_start = 1 + ((unit.unit_id - 1) * 3)
|
||||||
local v_fields = { "pu", "po", "pl", "am" }
|
local v_fields = { "pu", "po", "pl", "am" }
|
||||||
local v_names = {
|
local v_names = {
|
||||||
@ -94,11 +94,21 @@ local function make(parent, x, y, wide, unit_id)
|
|||||||
if unit.num_boilers > 0 then
|
if unit.num_boilers > 0 then
|
||||||
table.insert(rc_pipes, pipe(0, 1, _wide(28, 19), 1, colors.lightBlue, true))
|
table.insert(rc_pipes, pipe(0, 1, _wide(28, 19), 1, colors.lightBlue, true))
|
||||||
table.insert(rc_pipes, pipe(0, 3, _wide(28, 19), 3, colors.orange, true))
|
table.insert(rc_pipes, pipe(0, 3, _wide(28, 19), 3, colors.orange, true))
|
||||||
table.insert(rc_pipes, pipe(_wide(46 ,39), 1, _wide(72,58), 1, colors.blue, true))
|
table.insert(rc_pipes, pipe(_wide(46, 39), 1, _wide(72, 58), 1, colors.blue, true))
|
||||||
table.insert(rc_pipes, pipe(_wide(46,39), 3, _wide(72,58), 3, colors.white, true))
|
table.insert(rc_pipes, pipe(_wide(46, 39), 3, _wide(72, 58), 3, colors.white, true))
|
||||||
|
|
||||||
|
if unit.aux_coolant then
|
||||||
|
local em_water = facility.tank_fluid_types[facility.tank_conns[unit_id]] == COOLANT_TYPE.WATER
|
||||||
|
local offset = util.trinary(unit.has_tank and em_water, 3, 0)
|
||||||
|
table.insert(rc_pipes, pipe(_wide(51, 41) + offset, 0, _wide(51, 41) + offset, 0, colors.blue, true))
|
||||||
|
end
|
||||||
else
|
else
|
||||||
table.insert(rc_pipes, pipe(0, 1, _wide(72,58), 1, colors.blue, true))
|
table.insert(rc_pipes, pipe(0, 1, _wide(72, 58), 1, colors.blue, true))
|
||||||
table.insert(rc_pipes, pipe(0, 3, _wide(72,58), 3, colors.white, true))
|
table.insert(rc_pipes, pipe(0, 3, _wide(72, 58), 3, colors.white, true))
|
||||||
|
|
||||||
|
if unit.aux_coolant then
|
||||||
|
table.insert(rc_pipes, pipe(8, 0, 8, 0, colors.blue, true))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if unit.has_tank then
|
if unit.has_tank then
|
||||||
@ -222,17 +232,21 @@ local function make(parent, x, y, wide, unit_id)
|
|||||||
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
||||||
|
|
||||||
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,fg_bg=wh_gray}
|
||||||
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=style.theme.highlight_box_bright}
|
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=8,thin=true,fg_bg=style.theme.highlight_box_bright}
|
||||||
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
||||||
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c_d,label="CNT",unit="",format="%2d",value=0,width=7}
|
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c_d,label="CNT",unit="",format="%2d",value=0,width=7}
|
||||||
local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c_d,label="PEAK",unit="mB/t",format="%7.2f",value=0,width=17}
|
TextBox{parent=sna_po,y=3,text="PEAK\x1a",width=5,fg_bg=cpair(style.theme.label_dark,colors._INHERIT)}
|
||||||
local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="MAX",unit="mB/t",format="%8.2f",value=0,width=17}
|
TextBox{parent=sna_po,text="MAX \x1a",width=5,fg_bg=cpair(style.theme.label_dark,colors._INHERIT)}
|
||||||
local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="IN",unit="mB/t",format="%9.2f",value=0,width=17}
|
local sna_pk = DataIndicator{parent=sna_po,x=6,y=3,lu_colors=lu_c_d,label="",unit="mB/t",format="%7.2f",value=0,width=17}
|
||||||
|
local sna_max_o = DataIndicator{parent=sna_po,x=6,lu_colors=lu_c_d,label="",unit="mB/t",format="%7.2f",value=0,width=17}
|
||||||
|
local sna_max_i = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="\x1aMAX",unit="mB/t",format="%7.2f",value=0,width=17}
|
||||||
|
local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="\x1aIN",unit="mB/t",format="%8.2f",value=0,width=17}
|
||||||
|
|
||||||
sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end)
|
sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end)
|
||||||
sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update)
|
sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update)
|
||||||
sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update)
|
sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update)
|
||||||
sna_max.register(unit.unit_ps, "sna_max_rate", sna_max.update)
|
sna_max_o.register(unit.unit_ps, "sna_max_rate", sna_max_o.update)
|
||||||
|
sna_max_i.register(unit.unit_ps, "sna_max_rate", function (r) sna_max_i.update(r * 10) end)
|
||||||
sna_in.register(unit.unit_ps, "sna_in", sna_in.update)
|
sna_in.register(unit.unit_ps, "sna_in", sna_in.update)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|||||||
@ -286,7 +286,7 @@ local function init(main)
|
|||||||
|
|
||||||
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=text_col,width=2}
|
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=text_col,width=2}
|
||||||
|
|
||||||
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i * 5),colors=style.ind_grn}
|
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", (i * 6) - 1),colors=style.ind_grn}
|
||||||
local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht}
|
local open = IndicatorLight{parent=main,x=9,y=vy+2,label="OPEN",colors=style.ind_wht}
|
||||||
|
|
||||||
conn.register(units[i].unit_ps, "V_emc_conn", conn.update)
|
conn.register(units[i].unit_ps, "V_emc_conn", conn.update)
|
||||||
@ -294,6 +294,35 @@ local function init(main)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
------------------------------
|
||||||
|
-- auxiliary coolant valves --
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
for i = 1, facility.num_units do
|
||||||
|
if units[i].aux_coolant then
|
||||||
|
local vx
|
||||||
|
local vy = 3 + y_ofs(i)
|
||||||
|
|
||||||
|
if #emcool_pipes == 0 then
|
||||||
|
vx = util.trinary(units[i].num_boilers == 0, 36, 79)
|
||||||
|
else
|
||||||
|
local em_water = tank_types[tank_conns[i]] == COOLANT_TYPE.WATER
|
||||||
|
vx = util.trinary(units[i].num_boilers == 0, 58, util.trinary(units[i].has_tank and em_water, 94, 91))
|
||||||
|
end
|
||||||
|
|
||||||
|
PipeNetwork{parent=main,x=vx-6,y=vy,pipes={pipe(0,1,9,0,colors.blue,true)},bg=style.theme.bg}
|
||||||
|
|
||||||
|
TextBox{parent=main,x=vx,y=vy,text="\x10\x11",fg_bg=text_col,width=2}
|
||||||
|
TextBox{parent=main,x=vx+5,y=vy,text="\x1b",fg_bg=cpair(colors.blue,text_col.bkg),width=1}
|
||||||
|
|
||||||
|
local conn = IndicatorLight{parent=main,x=vx-3,y=vy+1,label=util.sprintf("PV%02d-AUX", i * 6),colors=style.ind_grn}
|
||||||
|
local open = IndicatorLight{parent=main,x=vx-3,y=vy+2,label="OPEN",colors=style.ind_wht}
|
||||||
|
|
||||||
|
conn.register(units[i].unit_ps, "V_aux_conn", conn.update)
|
||||||
|
open.register(units[i].unit_ps, "V_aux_state", open.update)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-------------------
|
-------------------
|
||||||
-- dynamic tanks --
|
-- dynamic tanks --
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@ -39,6 +39,8 @@ local led_grn = style.led_grn
|
|||||||
local function init(panel, num_units)
|
local function init(panel, num_units)
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
|
local term_w, term_h = term.getSize()
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,fg_bg=style.fp_theme.header}
|
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,fg_bg=style.fp_theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
@ -61,7 +63,7 @@ local function init(panel, num_units)
|
|||||||
local modem = LED{parent=system,label="MODEM",colors=led_grn}
|
local modem = LED{parent=system,label="MODEM",colors=led_grn}
|
||||||
|
|
||||||
if not style.colorblind then
|
if not style.colorblind then
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.fp_ind_bkg}}
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.yellow,colors.orange,style.fp_ind_bkg}}
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
network.register(ps, "link_state", network.update)
|
network.register(ps, "link_state", network.update)
|
||||||
else
|
else
|
||||||
@ -131,9 +133,9 @@ local function init(panel, num_units)
|
|||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
local about = Div{parent=main_page,width=15,height=2,y=term_h-3,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00"}
|
local fw_v = TextBox{parent=about,text="FW: v00.00.00"}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00"}
|
local comms_v = TextBox{parent=about,text="NT: v00.00.00"}
|
||||||
|
|
||||||
fw_v.register(ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@ -145,7 +147,7 @@ local function init(panel, num_units)
|
|||||||
-- API page
|
-- API page
|
||||||
|
|
||||||
local api_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local api_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local api_list = ListBox{parent=api_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local api_list = ListBox{parent=api_page,y=1,height=term_h-2,width=term_w,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=api_list,height=1} -- padding
|
local _ = Div{parent=api_list,height=1} -- padding
|
||||||
|
|
||||||
-- assemble page panes
|
-- assemble page panes
|
||||||
|
|||||||
@ -385,7 +385,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit)
|
|||||||
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
if (string.len(val) > val_max_w) or string.find(val, "\n") then
|
||||||
local lines = util.strwrap(val, inner_width)
|
local lines = util.strwrap(val, inner_width)
|
||||||
height = #lines + 1
|
height = #lines + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@ -20,7 +20,7 @@ local pocket = require("pocket.pocket")
|
|||||||
local renderer = require("pocket.renderer")
|
local renderer = require("pocket.renderer")
|
||||||
local threads = require("pocket.threads")
|
local threads = require("pocket.threads")
|
||||||
|
|
||||||
local POCKET_VERSION = "v0.13.0-beta"
|
local POCKET_VERSION = "v0.13.1-beta"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
|||||||
@ -592,7 +592,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, style, exit)
|
|||||||
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
if (string.len(val) > val_max_w) or string.find(val, "\n") then
|
||||||
local lines = util.strwrap(val, inner_width)
|
local lines = util.strwrap(val, inner_width)
|
||||||
height = #lines + 1
|
height = #lines + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@ -40,6 +40,8 @@ local function init(panel)
|
|||||||
|
|
||||||
local disabled_fg = style.fp.disabled_fg
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
|
local term_w, term_h = term.getSize()
|
||||||
|
|
||||||
local header = TextBox{parent=panel,y=1,text="FISSION REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
local header = TextBox{parent=panel,y=1,text="FISSION REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("FISSION REACTOR PLC - UNIT ", id)) end)
|
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("FISSION REACTOR PLC - UNIT ", id)) end)
|
||||||
|
|
||||||
@ -60,7 +62,7 @@ local function init(panel)
|
|||||||
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
||||||
|
|
||||||
if not style.colorblind then
|
if not style.colorblind then
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.ind_bkg}}
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.yellow,colors.orange,style.ind_bkg}}
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
network.register(databus.ps, "link_state", network.update)
|
network.register(databus.ps, "link_state", network.update)
|
||||||
else
|
else
|
||||||
@ -121,7 +123,7 @@ local function init(panel)
|
|||||||
-- status & controls
|
-- status & controls
|
||||||
--
|
--
|
||||||
|
|
||||||
local status = Div{parent=panel,width=19,height=18,x=17,y=3}
|
local status = Div{parent=panel,width=term_w-32,height=18,x=17,y=3}
|
||||||
|
|
||||||
local active = LED{parent=status,x=2,width=12,label="RCT ACTIVE",colors=ind_grn}
|
local active = LED{parent=status,x=2,width=12,label="RCT ACTIVE",colors=ind_grn}
|
||||||
|
|
||||||
@ -131,14 +133,15 @@ local function init(panel)
|
|||||||
emer_cool.register(databus.ps, "emer_cool", emer_cool.update)
|
emer_cool.register(databus.ps, "emer_cool", emer_cool.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
local status_trip_rct = Rectangle{parent=status,width=20,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
local status_trip_rct = Rectangle{parent=status,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
||||||
local status_trip = Div{parent=status_trip_rct,width=18,height=1,fg_bg=s_hi_box}
|
local status_trip = Div{parent=status_trip_rct,height=1,fg_bg=s_hi_box}
|
||||||
local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=ind_red,flash=true,period=flasher.PERIOD.BLINK_250_MS}
|
local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=ind_red,flash=true,period=flasher.PERIOD.BLINK_250_MS}
|
||||||
|
|
||||||
local controls_rct = Rectangle{parent=status,width=17,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
local controls_rct = Rectangle{parent=status,width=status.get_width()-2,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
||||||
local controls = Div{parent=controls_rct,width=15,height=1,fg_bg=s_hi_box}
|
local controls = Div{parent=controls_rct,width=controls_rct.get_width()-2,height=1,fg_bg=s_hi_box}
|
||||||
PushButton{parent=controls,x=1,y=1,min_width=7,text="SCRAM",callback=databus.rps_scram,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.black,colors.red_off)}
|
local button_padding = math.floor((controls.get_width() - 14) / 3)
|
||||||
PushButton{parent=controls,x=9,y=1,min_width=7,text="RESET",callback=databus.rps_reset,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.black,colors.yellow_off)}
|
PushButton{parent=controls,x=button_padding+1,y=1,min_width=7,text="SCRAM",callback=databus.rps_scram,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.black,colors.red_off)}
|
||||||
|
PushButton{parent=controls,x=(2*button_padding)+9,y=1,min_width=7,text="RESET",callback=databus.rps_reset,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.black,colors.yellow_off)}
|
||||||
|
|
||||||
active.register(databus.ps, "reactor_active", active.update)
|
active.register(databus.ps, "reactor_active", active.update)
|
||||||
scram.register(databus.ps, "rps_scram", scram.update)
|
scram.register(databus.ps, "rps_scram", scram.update)
|
||||||
@ -147,9 +150,9 @@ local function init(panel)
|
|||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
local about = Div{parent=panel,width=15,height=2,y=term_h-1,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00"}
|
local fw_v = TextBox{parent=about,text="FW: v00.00.00"}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00"}
|
local comms_v = TextBox{parent=about,text="NT: v00.00.00"}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@ -158,7 +161,7 @@ local function init(panel)
|
|||||||
-- rps list
|
-- rps list
|
||||||
--
|
--
|
||||||
|
|
||||||
local rps = Rectangle{parent=panel,width=16,height=16,x=36,y=3,border=border(1,s_hi_box.bkg),thin=true,fg_bg=s_hi_box}
|
local rps = Rectangle{parent=panel,width=16,height=16,x=term_w-15,y=3,border=border(1,s_hi_box.bkg),thin=true,fg_bg=s_hi_box}
|
||||||
local rps_man = LED{parent=rps,label="MANUAL",colors=ind_red}
|
local rps_man = LED{parent=rps,label="MANUAL",colors=ind_red}
|
||||||
local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=ind_red}
|
local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=ind_red}
|
||||||
local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=ind_red}
|
local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=ind_red}
|
||||||
|
|||||||
@ -23,8 +23,7 @@ local AUTO_ACK = comms.PLC_AUTO_ACK
|
|||||||
|
|
||||||
local RPS_LIMITS = const.RPS_LIMITS
|
local RPS_LIMITS = const.RPS_LIMITS
|
||||||
|
|
||||||
-- I sure hope the devs don't change this error message, not that it would have safety implications
|
-- specific errors thrown when scram/start is used that still count as success
|
||||||
-- I wish they didn't change it to be like this
|
|
||||||
local PCALL_SCRAM_MSG = "Scram requires the reactor to be active."
|
local PCALL_SCRAM_MSG = "Scram requires the reactor to be active."
|
||||||
local PCALL_START_MSG = "Reactor is already active."
|
local PCALL_START_MSG = "Reactor is already active."
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
|
|||||||
local renderer = require("reactor-plc.renderer")
|
local renderer = require("reactor-plc.renderer")
|
||||||
local threads = require("reactor-plc.threads")
|
local threads = require("reactor-plc.threads")
|
||||||
|
|
||||||
local R_PLC_VERSION = "v1.8.14"
|
local R_PLC_VERSION = "v1.8.19"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -169,12 +169,12 @@ local function main()
|
|||||||
-- PLC init<br>
|
-- PLC init<br>
|
||||||
--- EVENT_CONSUMER: this function consumes events
|
--- EVENT_CONSUMER: this function consumes events
|
||||||
local function init()
|
local function init()
|
||||||
-- just booting up, no fission allowed (neutrons stay put thanks)
|
-- scram on boot if networked, otherwise leave the reactor be
|
||||||
if (not plc_state.no_reactor) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
if __shared_memory.networked and (not plc_state.no_reactor) and plc_state.reactor_formed and smem_dev.reactor.getStatus() then
|
||||||
smem_dev.reactor.scram()
|
smem_dev.reactor.scram()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- front panel time!
|
-- setup front panel
|
||||||
if not renderer.ui_ready() then
|
if not renderer.ui_ready() then
|
||||||
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.FrontPanelTheme, config.ColorMode)
|
||||||
|
|||||||
@ -149,7 +149,7 @@ function peripherals.create(tool_ctl, main_pane, cfg_sys, peri_cfg, style)
|
|||||||
reposition("This SNA is for reactor unit # .", 46, 1, 31, 4, 7)
|
reposition("This SNA is for reactor unit # .", 46, 1, 31, 4, 7)
|
||||||
self.p_idx.hide()
|
self.p_idx.hide()
|
||||||
self.p_assign_btn.hide(true)
|
self.p_assign_btn.hide(true)
|
||||||
self.p_desc_ext.set_value("Before adding lots of SNAs: multiply the \"PEAK\" rate on the flow monitor (after connecting at least 1 SNA) by 10 to get the mB/t of waste that they can process. Enough SNAs to provide 2x to 3x of your max burn rate should be a good margin to catch up after night or cloudy weather. Too many devices (such as SNAs) on one RTU can cause lag.")
|
self.p_desc_ext.set_value("Warning: too many devices on one RTU Gateway can cause lag. Note that 10x the \"PEAK\x1a\" rate on the flow monitor gives you the mB/t of waste that the SNA(s) can process. Enough SNAs to provide 2x to 3x of that unit's max burn rate should be a good margin to catch up after night or cloudy weather.")
|
||||||
elseif type == "dynamicValve" then
|
elseif type == "dynamicValve" then
|
||||||
reposition("This is the below system's # dynamic tank.", 29, 4, 17, 6, 8)
|
reposition("This is the below system's # dynamic tank.", 29, 4, 17, 6, 8)
|
||||||
self.p_assign_btn.show()
|
self.p_assign_btn.show()
|
||||||
|
|||||||
@ -74,11 +74,12 @@ local PORT_DESC_MAP = {
|
|||||||
{ IO.R_PLC_FAULT, "RPS PLC Fault" },
|
{ IO.R_PLC_FAULT, "RPS PLC Fault" },
|
||||||
{ IO.R_PLC_TIMEOUT, "RPS Supervisor Timeout" },
|
{ IO.R_PLC_TIMEOUT, "RPS Supervisor Timeout" },
|
||||||
{ IO.U_ALARM, "Unit Alarm" },
|
{ IO.U_ALARM, "Unit Alarm" },
|
||||||
{ IO.U_EMER_COOL, "Unit Emergency Cool. Valve" }
|
{ IO.U_EMER_COOL, "Unit Emergency Cool. Valve" },
|
||||||
|
{ IO.U_AUX_COOL, "Unit Auxiliary Cool. Valve" }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- designation (0 = facility, 1 = unit)
|
-- designation (0 = facility, 1 = unit)
|
||||||
local PORT_DSGN = { [-1] = 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }
|
local PORT_DSGN = { [-1] = 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1 }
|
||||||
|
|
||||||
assert(#PORT_DESC_MAP == rsio.NUM_PORTS)
|
assert(#PORT_DESC_MAP == rsio.NUM_PORTS)
|
||||||
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
||||||
|
|||||||
@ -646,7 +646,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, ext, style)
|
|||||||
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
if (string.len(val) > val_max_w) or string.find(val, "\n") then
|
||||||
local lines = util.strwrap(val, inner_width)
|
local lines = util.strwrap(val, inner_width)
|
||||||
height = #lines + 1
|
height = #lines + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@ -35,13 +35,15 @@ local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC
|
|||||||
local function init(panel, units)
|
local function init(panel, units)
|
||||||
local disabled_fg = style.fp.disabled_fg
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
|
local term_w, term_h = term.getSize()
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- system indicators
|
-- system indicators
|
||||||
--
|
--
|
||||||
|
|
||||||
local system = Div{parent=panel,width=14,height=18,x=2,y=3}
|
local system = Div{parent=panel,width=14,height=term_h-5,x=2,y=3}
|
||||||
|
|
||||||
local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
|
local on = LED{parent=system,label="STATUS",colors=cpair(colors.green,colors.red)}
|
||||||
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
|
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=ind_grn}
|
||||||
@ -53,7 +55,7 @@ local function init(panel, units)
|
|||||||
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
||||||
|
|
||||||
if not style.colorblind then
|
if not style.colorblind then
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.ind_bkg}}
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.yellow,colors.orange,style.ind_bkg}}
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
network.register(databus.ps, "link_state", network.update)
|
network.register(databus.ps, "link_state", network.update)
|
||||||
else
|
else
|
||||||
@ -100,17 +102,17 @@ local function init(panel, units)
|
|||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=disabled_fg}
|
TextBox{parent=system,x=9,y=4,width=6,text=comp_id,fg_bg=disabled_fg}
|
||||||
|
|
||||||
TextBox{parent=system,x=1,y=14,text="SPEAKERS",width=8,fg_bg=style.fp.text_fg}
|
TextBox{parent=system,y=term_h-5,text="SPEAKERS",width=8,fg_bg=style.fp.text_fg}
|
||||||
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=style.theme.field_box}
|
local speaker_count = DataIndicator{parent=system,x=10,y=term_h-5,label="",format="%3d",value=0,width=3,fg_bg=style.theme.field_box}
|
||||||
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about label
|
-- about label
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
local about = Div{parent=panel,width=15,height=2,y=term_h-1,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00"}
|
local fw_v = TextBox{parent=about,text="FW: v00.00.00"}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00"}
|
local comms_v = TextBox{parent=about,text="NT: v00.00.00"}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@ -119,10 +121,10 @@ local function init(panel, units)
|
|||||||
-- unit status list
|
-- unit status list
|
||||||
--
|
--
|
||||||
|
|
||||||
local threads = Div{parent=panel,width=8,height=18,x=17,y=3}
|
local threads = Div{parent=panel,width=8,height=term_h-3,x=17,y=3}
|
||||||
|
|
||||||
-- display up to 16 units
|
-- display as many units as we can with 1 line of padding above and below
|
||||||
local list_length = math.min(#units, 16)
|
local list_length = math.min(#units, term_h - 3)
|
||||||
|
|
||||||
-- show routine statuses
|
-- show routine statuses
|
||||||
for i = 1, list_length do
|
for i = 1, list_length do
|
||||||
@ -131,7 +133,7 @@ local function init(panel, units)
|
|||||||
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
|
rt_unit.register(databus.ps, "routine__unit_" .. i, rt_unit.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
local unit_hw_statuses = Div{parent=panel,height=18,x=25,y=3}
|
local unit_hw_statuses = Div{parent=panel,height=term_h-3,x=25,y=3}
|
||||||
|
|
||||||
-- show hardware statuses
|
-- show hardware statuses
|
||||||
for i = 1, list_length do
|
for i = 1, list_length do
|
||||||
@ -150,7 +152,7 @@ local function init(panel, units)
|
|||||||
|
|
||||||
-- assignment (unit # or facility)
|
-- assignment (unit # or facility)
|
||||||
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
||||||
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,fg_bg=disabled_fg}
|
TextBox{parent=unit_hw_statuses,y=i,x=term_w-32,text=for_unit,fg_bg=disabled_fg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,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 = "v1.11.0"
|
local RTU_VERSION = "v1.11.6"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_HW_STATE = databus.RTU_HW_STATE
|
local RTU_HW_STATE = databus.RTU_HW_STATE
|
||||||
|
|||||||
@ -17,7 +17,7 @@ local max_distance = nil
|
|||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
||||||
comms.version = "3.0.4"
|
comms.version = "3.0.5"
|
||||||
comms.api_version = "0.0.9"
|
comms.api_version = "0.0.9"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
@ -60,18 +60,19 @@ local MGMT_TYPE = {
|
|||||||
---@enum CRDN_TYPE
|
---@enum CRDN_TYPE
|
||||||
local CRDN_TYPE = {
|
local CRDN_TYPE = {
|
||||||
INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator
|
INITIAL_BUILDS = 0, -- initial, complete builds packet to the coordinator
|
||||||
FAC_BUILDS = 1, -- facility RTU builds
|
PROCESS_READY = 1, -- process init is complete + last set of info for supervisor startup recovery
|
||||||
FAC_STATUS = 2, -- state of facility and facility devices
|
FAC_BUILDS = 2, -- facility RTU builds
|
||||||
FAC_CMD = 3, -- faility command
|
FAC_STATUS = 3, -- state of facility and facility devices
|
||||||
UNIT_BUILDS = 4, -- build of each reactor unit (reactor + RTUs)
|
FAC_CMD = 4, -- faility command
|
||||||
UNIT_STATUSES = 5, -- state of each of the reactor units
|
UNIT_BUILDS = 5, -- build of each reactor unit (reactor + RTUs)
|
||||||
UNIT_CMD = 6, -- command a reactor unit
|
UNIT_STATUSES = 6, -- state of each of the reactor units
|
||||||
API_GET_FAC = 7, -- API: get the facility general data
|
UNIT_CMD = 7, -- command a reactor unit
|
||||||
API_GET_FAC_DTL = 8, -- API: get (detailed) data for the facility app
|
API_GET_FAC = 8, -- API: get the facility general data
|
||||||
API_GET_UNIT = 9, -- API: get reactor unit data
|
API_GET_FAC_DTL = 9, -- API: get (detailed) data for the facility app
|
||||||
API_GET_CTRL = 10, -- API: get data for the control app
|
API_GET_UNIT = 10, -- API: get reactor unit data
|
||||||
API_GET_PROC = 11, -- API: get data for the process app
|
API_GET_CTRL = 11, -- API: get data for the control app
|
||||||
API_GET_WASTE = 12 -- API: get data for the waste app
|
API_GET_PROC = 12, -- API: get data for the process app
|
||||||
|
API_GET_WASTE = 13 -- API: get data for the waste app
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum ESTABLISH_ACK
|
---@enum ESTABLISH_ACK
|
||||||
|
|||||||
@ -72,6 +72,8 @@ local rs = {}
|
|||||||
|
|
||||||
rs.IMATRIX_CHARGE_LOW = 0.05 -- activation threshold (less than) for F_MATRIX_LOW
|
rs.IMATRIX_CHARGE_LOW = 0.05 -- activation threshold (less than) for F_MATRIX_LOW
|
||||||
rs.IMATRIX_CHARGE_HIGH = 0.95 -- activation threshold (greater than) for F_MATRIX_HIGH
|
rs.IMATRIX_CHARGE_HIGH = 0.95 -- activation threshold (greater than) for F_MATRIX_HIGH
|
||||||
|
rs.AUX_COOL_ENABLE = 0.60 -- actiation threshold (less than or equal) for U_AUX_COOL
|
||||||
|
rs.AUX_COOL_DISABLE = 1.00 -- deactivation threshold (greater than or equal) for U_AUX_COOL
|
||||||
|
|
||||||
constants.RS_THRESHOLDS = rs
|
constants.RS_THRESHOLDS = rs
|
||||||
|
|
||||||
|
|||||||
@ -78,6 +78,7 @@ local IO_PORT = {
|
|||||||
-- unit outputs
|
-- unit outputs
|
||||||
U_ALARM = 25, -- active high, unit alarm
|
U_ALARM = 25, -- active high, unit alarm
|
||||||
U_EMER_COOL = 26, -- active low, emergency coolant control
|
U_EMER_COOL = 26, -- active low, emergency coolant control
|
||||||
|
U_AUX_COOL = 30, -- active low, auxiliary coolant control
|
||||||
|
|
||||||
-- analog outputs --
|
-- analog outputs --
|
||||||
|
|
||||||
@ -90,8 +91,8 @@ rsio.IO_DIR = IO_DIR
|
|||||||
rsio.IO_MODE = IO_MODE
|
rsio.IO_MODE = IO_MODE
|
||||||
rsio.IO = IO_PORT
|
rsio.IO = IO_PORT
|
||||||
|
|
||||||
rsio.NUM_PORTS = 29
|
rsio.NUM_PORTS = 30
|
||||||
rsio.NUM_DIG_PORTS = 28
|
rsio.NUM_DIG_PORTS = 29
|
||||||
rsio.NUM_ANA_PORTS = 1
|
rsio.NUM_ANA_PORTS = 1
|
||||||
|
|
||||||
-- self checks
|
-- self checks
|
||||||
@ -149,6 +150,7 @@ local MODES = {
|
|||||||
[IO.R_PLC_TIMEOUT] = IO_MODE.DIGITAL_OUT,
|
[IO.R_PLC_TIMEOUT] = IO_MODE.DIGITAL_OUT,
|
||||||
[IO.U_ALARM] = IO_MODE.DIGITAL_OUT,
|
[IO.U_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||||
[IO.U_EMER_COOL] = IO_MODE.DIGITAL_OUT,
|
[IO.U_EMER_COOL] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.U_AUX_COOL] = IO_MODE.DIGITAL_OUT,
|
||||||
[IO.F_MATRIX_CHG] = IO_MODE.ANALOG_OUT
|
[IO.F_MATRIX_CHG] = IO_MODE.ANALOG_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,10 +210,11 @@ local RS_DIO_MAP = {
|
|||||||
[IO.R_PLC_TIMEOUT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_PLC_TIMEOUT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
|
|
||||||
[IO.U_ALARM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.U_ALARM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
[IO.U_EMER_COOL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
[IO.U_EMER_COOL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
|
[IO.U_AUX_COOL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rsio.NUM_DIG_PORTS == #RS_DIO_MAP, "RS_DIO_MAP length incorrect")
|
assert(rsio.NUM_DIG_PORTS == util.table_len(RS_DIO_MAP), "RS_DIO_MAP length incorrect")
|
||||||
|
|
||||||
-- get the I/O direction of a port
|
-- get the I/O direction of a port
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
|
|||||||
@ -24,7 +24,7 @@ local t_pack = table.pack
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
-- scada-common version
|
-- scada-common version
|
||||||
util.version = "1.4.10"
|
util.version = "1.4.12"
|
||||||
|
|
||||||
util.TICK_TIME_S = 0.05
|
util.TICK_TIME_S = 0.05
|
||||||
util.TICK_TIME_MS = 50
|
util.TICK_TIME_MS = 50
|
||||||
|
|||||||
@ -185,8 +185,9 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style)
|
|||||||
local fac_c_6 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
local fac_c_6 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
||||||
local fac_c_7 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
local fac_c_7 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
||||||
local fac_c_8 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
local fac_c_8 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
||||||
|
local fac_c_9 = Div{parent=fac_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local fac_pane = MultiPane{parent=fac_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3,fac_c_4,fac_c_5,fac_c_6,fac_c_7, fac_c_8}}
|
local fac_pane = MultiPane{parent=fac_cfg,x=1,y=4,panes={fac_c_1,fac_c_2,fac_c_3,fac_c_4,fac_c_5,fac_c_6,fac_c_7,fac_c_8,fac_c_9}}
|
||||||
|
|
||||||
TextBox{parent=fac_cfg,x=1,y=2,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=fac_cfg,x=1,y=2,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
@ -205,10 +206,18 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style)
|
|||||||
nu_error.hide(true)
|
nu_error.hide(true)
|
||||||
tmp_cfg.UnitCount = count
|
tmp_cfg.UnitCount = count
|
||||||
|
|
||||||
local confs = tool_ctl.cooling_elems
|
local c_confs = tool_ctl.cooling_elems
|
||||||
if count >= 2 then confs[2].line.show() else confs[2].line.hide(true) end
|
local a_confs = tool_ctl.aux_cool_elems
|
||||||
if count >= 3 then confs[3].line.show() else confs[3].line.hide(true) end
|
|
||||||
if count == 4 then confs[4].line.show() else confs[4].line.hide(true) end
|
for i = 2, 4 do
|
||||||
|
if count >= i then
|
||||||
|
c_confs[i].line.show()
|
||||||
|
a_confs[i].line.show()
|
||||||
|
else
|
||||||
|
c_confs[i].line.hide(true)
|
||||||
|
a_confs[i].line.hide(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
fac_pane.set_value(2)
|
fac_pane.set_value(2)
|
||||||
else nu_error.show() end
|
else nu_error.show() end
|
||||||
@ -285,6 +294,14 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style)
|
|||||||
else elem.div.hide(true) end
|
else elem.div.hide(true) end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not any_has_tank then
|
||||||
|
tmp_cfg.FacilityTankMode = 0
|
||||||
|
tmp_cfg.FacilityTankDefs = {}
|
||||||
|
tmp_cfg.FacilityTankList = {}
|
||||||
|
tmp_cfg.FacilityTankConns = {}
|
||||||
|
tmp_cfg.TankFluidTypes = {}
|
||||||
|
end
|
||||||
|
|
||||||
if any_has_tank then fac_pane.set_value(3) else main_pane.set_value(3) end
|
if any_has_tank then fac_pane.set_value(3) else main_pane.set_value(3) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -672,25 +689,48 @@ function facility.create(tool_ctl, main_pane, cfg_sys, fac_cfg, style)
|
|||||||
PushButton{parent=fac_c_7,x=1,y=14,text="\x1b Back",callback=back_from_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=fac_c_7,x=1,y=14,text="\x1b Back",callback=back_from_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=fac_c_7,x=44,y=14,text="Next \x1a",callback=submit_tank_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=fac_c_7,x=44,y=14,text="Next \x1a",callback=submit_tank_fluids,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
--#region Auxiliary Coolant
|
||||||
|
|
||||||
|
TextBox{parent=fac_c_8,height=5,text="Auxiliary water coolant can be enabled for units to provide extra water during turbine ramp-up. For water cooled reactors, this goes to the reactor. For sodium cooled reactors, water goes to the boiler."}
|
||||||
|
|
||||||
|
for i = 1, 4 do
|
||||||
|
local line = Div{parent=fac_c_8,x=1,y=7+i,height=1}
|
||||||
|
|
||||||
|
TextBox{parent=line,text="Unit "..i.." -",width=8}
|
||||||
|
local aux_cool = Checkbox{parent=line,x=10,y=1,label="Has Auxiliary Coolant",default=ini_cfg.AuxiliaryCoolant[i],box_fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
|
tool_ctl.aux_cool_elems[i] = { line = line, enable = aux_cool }
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_aux_cool()
|
||||||
|
tmp_cfg.AuxiliaryCoolant = {}
|
||||||
|
|
||||||
|
for i = 1, tmp_cfg.UnitCount do
|
||||||
|
tmp_cfg.AuxiliaryCoolant[i] = tool_ctl.aux_cool_elems[i].enable.get_value()
|
||||||
|
end
|
||||||
|
|
||||||
|
fac_pane.set_value(9)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=fac_c_8,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=fac_c_8,x=44,y=14,text="Next \x1a",callback=submit_aux_cool,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
--#region Extended Idling
|
--#region Extended Idling
|
||||||
|
|
||||||
TextBox{parent=fac_c_8,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."}
|
TextBox{parent=fac_c_9,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."}
|
||||||
TextBox{parent=fac_c_8,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."}
|
TextBox{parent=fac_c_9,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."}
|
||||||
|
|
||||||
local ext_idling = Checkbox{parent=fac_c_8,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)}
|
local ext_idling = Checkbox{parent=fac_c_9,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
local function back_from_idling()
|
|
||||||
fac_pane.set_value(tri(tmp_cfg.FacilityTankMode == 0, 3, 7))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function submit_idling()
|
local function submit_idling()
|
||||||
tmp_cfg.ExtChargeIdling = ext_idling.get_value()
|
tmp_cfg.ExtChargeIdling = ext_idling.get_value()
|
||||||
main_pane.set_value(3)
|
main_pane.set_value(3)
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=fac_c_8,x=1,y=14,text="\x1b Back",callback=back_from_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=fac_c_9,x=1,y=14,text="\x1b Back",callback=function()fac_pane.set_value(8)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=fac_c_8,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=fac_c_9,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
|||||||
@ -402,6 +402,10 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit
|
|||||||
try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i])
|
try_set(tool_ctl.tank_elems[i].tank_opt, ini_cfg.FacilityTankDefs[i])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
for i = 1, #ini_cfg.AuxiliaryCoolant do
|
||||||
|
try_set(tool_ctl.aux_cool_elems[i].enable, ini_cfg.AuxiliaryCoolant[i])
|
||||||
|
end
|
||||||
|
|
||||||
tool_ctl.en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0)
|
tool_ctl.en_fac_tanks.set_value(ini_cfg.FacilityTankMode > 0)
|
||||||
|
|
||||||
tool_ctl.view_cfg.enable()
|
tool_ctl.view_cfg.enable()
|
||||||
@ -588,7 +592,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit
|
|||||||
end
|
end
|
||||||
|
|
||||||
if val == "" then val = "no facility tanks" end
|
if val == "" then val = "no facility tanks" end
|
||||||
elseif f[1] == "FacilityTankMode" and raw == 0 then val = "0 (n/a, unit mode)"
|
elseif f[1] == "FacilityTankMode" and raw == 0 then val = "no facility tanks"
|
||||||
elseif f[1] == "FacilityTankDefs" and type(cfg.FacilityTankDefs) == "table" then
|
elseif f[1] == "FacilityTankDefs" and type(cfg.FacilityTankDefs) == "table" then
|
||||||
local tank_name_list = { table.unpack(cfg.FacilityTankList) } ---@type (string|integer)[]
|
local tank_name_list = { table.unpack(cfg.FacilityTankList) } ---@type (string|integer)[]
|
||||||
local next_f = 1
|
local next_f = 1
|
||||||
@ -625,6 +629,13 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit
|
|||||||
|
|
||||||
val = ""
|
val = ""
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for idx = 1, #tank_list do
|
||||||
|
if tank_list[idx] > 0 then count = count + 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
local bullet = tri(count < 2, "", " \x07 ")
|
||||||
|
|
||||||
for idx = 1, #tank_list do
|
for idx = 1, #tank_list do
|
||||||
local prefix = "?"
|
local prefix = "?"
|
||||||
local fluid = "water"
|
local fluid = "water"
|
||||||
@ -642,11 +653,28 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit
|
|||||||
fluid = "sodium"
|
fluid = "sodium"
|
||||||
end
|
end
|
||||||
|
|
||||||
val = val .. tri(val == "", "", "\n") .. util.sprintf(" \x07 tank %s - %s", prefix, fluid)
|
val = val .. tri(val == "", "", "\n") .. util.sprintf(bullet .. "tank %s - %s", prefix, fluid)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if val == "" then val = "no emergency coolant tanks" end
|
if val == "" then val = "no emergency coolant tanks" end
|
||||||
|
elseif f[1] == "AuxiliaryCoolant" then
|
||||||
|
val = ""
|
||||||
|
|
||||||
|
local count = 0
|
||||||
|
for idx = 1, #cfg.AuxiliaryCoolant do
|
||||||
|
if cfg.AuxiliaryCoolant[idx] then count = count + 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
local bullet = tri(count < 2, "", " \x07 ")
|
||||||
|
|
||||||
|
for idx = 1, #cfg.AuxiliaryCoolant do
|
||||||
|
if cfg.AuxiliaryCoolant[idx] then
|
||||||
|
val = val .. tri(val == "", "", "\n") .. util.sprintf(bullet .. "unit %d", idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if val == "" then val = "no auxiliary coolant" end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not skip then
|
if not skip then
|
||||||
@ -655,7 +683,7 @@ function system.create(tool_ctl, main_pane, cfg_sys, divs, fac_pane, style, exit
|
|||||||
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = tri(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
alternate = not alternate
|
alternate = not alternate
|
||||||
|
|
||||||
if string.len(val) > val_max_w then
|
if (string.len(val) > val_max_w) or string.find(val, "\n") then
|
||||||
local lines = util.strwrap(val, inner_width)
|
local lines = util.strwrap(val, inner_width)
|
||||||
height = #lines + 1
|
height = #lines + 1
|
||||||
end
|
end
|
||||||
|
|||||||
@ -72,7 +72,8 @@ local tool_ctl = {
|
|||||||
load_legacy = nil, ---@type function
|
load_legacy = nil, ---@type function
|
||||||
|
|
||||||
cooling_elems = {}, ---@type { line: Div, turbines: NumberField, boilers: NumberField, tank: Checkbox }[]
|
cooling_elems = {}, ---@type { line: Div, turbines: NumberField, boilers: NumberField, tank: Checkbox }[]
|
||||||
tank_elems = {} ---@type { div: Div, tank_opt: Radio2D, no_tank: TextBox }[]
|
tank_elems = {}, ---@type { div: Div, tank_opt: Radio2D, no_tank: TextBox }[]
|
||||||
|
aux_cool_elems = {} ---@type { line: Div, enable: Checkbox }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class svr_config
|
---@class svr_config
|
||||||
@ -84,6 +85,7 @@ local tmp_cfg = {
|
|||||||
FacilityTankList = {}, ---@type integer[] list of tanks by slot (0 = none or covered by an above tank, 1 = unit tank, 2 = facility tank)
|
FacilityTankList = {}, ---@type integer[] list of tanks by slot (0 = none or covered by an above tank, 1 = unit tank, 2 = facility tank)
|
||||||
FacilityTankConns = {}, ---@type integer[] map of unit tank connections (indicies are units, values are tank indicies in the tank list)
|
FacilityTankConns = {}, ---@type integer[] map of unit tank connections (indicies are units, values are tank indicies in the tank list)
|
||||||
TankFluidTypes = {}, ---@type integer[] which type of fluid each tank in the tank list should be containing
|
TankFluidTypes = {}, ---@type integer[] which type of fluid each tank in the tank list should be containing
|
||||||
|
AuxiliaryCoolant = {}, ---@type boolean[] if a unit has auxiliary coolant
|
||||||
ExtChargeIdling = false,
|
ExtChargeIdling = false,
|
||||||
SVR_Channel = nil, ---@type integer
|
SVR_Channel = nil, ---@type integer
|
||||||
PLC_Channel = nil, ---@type integer
|
PLC_Channel = nil, ---@type integer
|
||||||
@ -117,6 +119,7 @@ local fields = {
|
|||||||
{ "FacilityTankList", "Facility Tank List", {} }, -- hidden
|
{ "FacilityTankList", "Facility Tank List", {} }, -- hidden
|
||||||
{ "FacilityTankConns", "Facility Tank Connections", {} }, -- hidden
|
{ "FacilityTankConns", "Facility Tank Connections", {} }, -- hidden
|
||||||
{ "TankFluidTypes", "Tank Fluid Types", {} },
|
{ "TankFluidTypes", "Tank Fluid Types", {} },
|
||||||
|
{ "AuxiliaryCoolant", "Auxiliary Water Coolant", {} },
|
||||||
{ "ExtChargeIdling", "Extended Charge Idling", false },
|
{ "ExtChargeIdling", "Extended Charge Idling", false },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "PLC_Channel", "PLC Channel", 16241 },
|
{ "PLC_Channel", "PLC Channel", 16241 },
|
||||||
|
|||||||
@ -31,6 +31,17 @@ local START_STATUS = {
|
|||||||
BLADE_MISMATCH = 2
|
BLADE_MISMATCH = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---@enum RECOVERY_STATE
|
||||||
|
local RCV_STATE = {
|
||||||
|
INACTIVE = 0,
|
||||||
|
PRIMED = 1,
|
||||||
|
RUNNING = 2,
|
||||||
|
STOPPED = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
local CHARGE_SCALER = 1000000 -- convert MFE to FE
|
||||||
|
local GEN_SCALER = 1000 -- convert kFE to FE
|
||||||
|
|
||||||
---@class facility_management
|
---@class facility_management
|
||||||
local facility = {}
|
local facility = {}
|
||||||
|
|
||||||
@ -41,7 +52,7 @@ function facility.new(config)
|
|||||||
---@class _facility_self
|
---@class _facility_self
|
||||||
local self = {
|
local self = {
|
||||||
units = {}, ---@type reactor_unit[]
|
units = {}, ---@type reactor_unit[]
|
||||||
types = { AUTO_SCRAM = AUTO_SCRAM, START_STATUS = START_STATUS },
|
types = { AUTO_SCRAM = AUTO_SCRAM, START_STATUS = START_STATUS, RCV_STATE = RCV_STATE },
|
||||||
status_text = { "START UP", "initializing..." },
|
status_text = { "START UP", "initializing..." },
|
||||||
all_sys_ok = false,
|
all_sys_ok = false,
|
||||||
allow_testing = false,
|
allow_testing = false,
|
||||||
@ -53,7 +64,8 @@ function facility.new(config)
|
|||||||
fac_tank_defs = config.FacilityTankDefs,
|
fac_tank_defs = config.FacilityTankDefs,
|
||||||
fac_tank_list = config.FacilityTankList,
|
fac_tank_list = config.FacilityTankList,
|
||||||
fac_tank_conns = config.FacilityTankConns,
|
fac_tank_conns = config.FacilityTankConns,
|
||||||
tank_fluid_types = config.TankFluidTypes
|
tank_fluid_types = config.TankFluidTypes,
|
||||||
|
aux_coolant = config.AuxiliaryCoolant
|
||||||
},
|
},
|
||||||
-- rtus
|
-- rtus
|
||||||
rtu_gw_conn_count = 0,
|
rtu_gw_conn_count = 0,
|
||||||
@ -66,12 +78,15 @@ function facility.new(config)
|
|||||||
-- redstone I/O control
|
-- redstone I/O control
|
||||||
io_ctl = nil, ---@type rs_controller
|
io_ctl = nil, ---@type rs_controller
|
||||||
-- process control
|
-- process control
|
||||||
|
recovery = RCV_STATE.INACTIVE, ---@type RECOVERY_STATE
|
||||||
|
recovery_boot_state = nil, ---@type sv_boot_state|nil
|
||||||
|
last_unit_states = {}, ---@type boolean[]
|
||||||
units_ready = false,
|
units_ready = false,
|
||||||
mode = PROCESS.INACTIVE,
|
mode = PROCESS.INACTIVE, ---@type PROCESS
|
||||||
last_mode = PROCESS.INACTIVE,
|
last_mode = PROCESS.INACTIVE, ---@type PROCESS
|
||||||
return_mode = PROCESS.INACTIVE,
|
return_mode = PROCESS.INACTIVE, ---@type PROCESS
|
||||||
mode_set = PROCESS.MAX_BURN,
|
mode_set = PROCESS.MAX_BURN, ---@type PROCESS
|
||||||
start_fail = START_STATUS.OK,
|
start_fail = START_STATUS.OK, ---@type START_STATUS
|
||||||
max_burn_combined = 0.0, -- maximum burn rate to clamp at
|
max_burn_combined = 0.0, -- maximum burn rate to clamp at
|
||||||
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
burn_target = 0.1, -- burn rate target for aggregate burn mode
|
||||||
charge_setpoint = 0, -- FE charge target setpoint
|
charge_setpoint = 0, -- FE charge target setpoint
|
||||||
@ -101,8 +116,8 @@ function facility.new(config)
|
|||||||
last_error = 0.0,
|
last_error = 0.0,
|
||||||
last_time = 0.0,
|
last_time = 0.0,
|
||||||
-- waste processing
|
-- waste processing
|
||||||
waste_product = WASTE.PLUTONIUM,
|
waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
||||||
current_waste_product = WASTE.PLUTONIUM,
|
current_waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
||||||
pu_fallback = false,
|
pu_fallback = false,
|
||||||
sps_low_power = false,
|
sps_low_power = false,
|
||||||
disabled_sps = false,
|
disabled_sps = false,
|
||||||
@ -126,14 +141,16 @@ function facility.new(config)
|
|||||||
imtx_faulted_times = { 0, 0, 0 }
|
imtx_faulted_times = { 0, 0, 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--#region SETUP
|
||||||
|
|
||||||
-- provide self to facility update functions
|
-- provide self to facility update functions
|
||||||
local f_update = fac_update(self)
|
local f_update = fac_update(self)
|
||||||
|
|
||||||
-- create units
|
-- create units
|
||||||
for i = 1, config.UnitCount do
|
for i = 1, config.UnitCount do
|
||||||
table.insert(self.units,
|
table.insert(self.units, unit.new(i, self.cooling_conf.r_cool[i].BoilerCount, self.cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling, self.cooling_conf.aux_coolant[i]))
|
||||||
unit.new(i, self.cooling_conf.r_cool[i].BoilerCount, self.cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling))
|
|
||||||
table.insert(self.group_map, AUTO_GROUP.MANUAL)
|
table.insert(self.group_map, AUTO_GROUP.MANUAL)
|
||||||
|
table.insert(self.last_unit_states, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- list for RTU session management
|
-- list for RTU session management
|
||||||
@ -149,6 +166,70 @@ function facility.new(config)
|
|||||||
table.insert(self.test_tone_states, false)
|
table.insert(self.test_tone_states, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- init next boot state
|
||||||
|
settings.set("LastProcessState", PROCESS.INACTIVE)
|
||||||
|
settings.set("LastUnitStates", self.last_unit_states)
|
||||||
|
if not settings.save("/supervisor.settings") then
|
||||||
|
log.warning("FAC: failed to save initial control state into supervisor settings file")
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
|
-- check an auto process control configuration and save it if its valid (does not start the process)
|
||||||
|
---@param auto_cfg start_auto_config configuration
|
||||||
|
---@return boolean ready, number[] unit_limits
|
||||||
|
local function _auto_check_and_save(auto_cfg)
|
||||||
|
local ready = false
|
||||||
|
|
||||||
|
-- load up current limits
|
||||||
|
local limits = {}
|
||||||
|
for i = 1, config.UnitCount do
|
||||||
|
limits[i] = self.units[i].get_control_inf().lim_br100 * 100
|
||||||
|
end
|
||||||
|
|
||||||
|
-- only allow changes if not running
|
||||||
|
if self.mode == PROCESS.INACTIVE then
|
||||||
|
if (type(auto_cfg.mode) == "number") and (auto_cfg.mode > PROCESS.INACTIVE) and (auto_cfg.mode <= PROCESS.GEN_RATE) then
|
||||||
|
self.mode_set = auto_cfg.mode
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
||||||
|
self.burn_target = auto_cfg.burn_target
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
||||||
|
self.charge_setpoint = auto_cfg.charge_target * CHARGE_SCALER
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
||||||
|
self.gen_rate_setpoint = auto_cfg.gen_target * GEN_SCALER
|
||||||
|
end
|
||||||
|
|
||||||
|
if (type(auto_cfg.limits) == "table") and (#auto_cfg.limits == config.UnitCount) then
|
||||||
|
for i = 1, config.UnitCount do
|
||||||
|
local limit = auto_cfg.limits[i]
|
||||||
|
|
||||||
|
if (type(limit) == "number") and (limit >= 0.1) then
|
||||||
|
limits[i] = limit
|
||||||
|
self.units[i].set_burn_limit(limit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ready = self.mode_set > 0
|
||||||
|
|
||||||
|
if ((self.mode_set == PROCESS.CHARGE) and (self.charge_setpoint <= 0)) or
|
||||||
|
((self.mode_set == PROCESS.GEN_RATE) and (self.gen_rate_setpoint <= 0)) or
|
||||||
|
((self.mode_set == PROCESS.BURN_RATE) and (self.burn_target < 0.1)) then
|
||||||
|
ready = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return ready, limits
|
||||||
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
---@class facility
|
---@class facility
|
||||||
@ -239,6 +320,9 @@ function facility.new(config)
|
|||||||
|
|
||||||
-- update (iterate) the facility management
|
-- update (iterate) the facility management
|
||||||
function public.update()
|
function public.update()
|
||||||
|
-- run reboot recovery routine if needed
|
||||||
|
f_update.boot_recovery()
|
||||||
|
|
||||||
-- run process control and evaluate automatic SCRAM
|
-- run process control and evaluate automatic SCRAM
|
||||||
f_update.pre_auto()
|
f_update.pre_auto()
|
||||||
f_update.auto_control(config.ExtChargeIdling)
|
f_update.auto_control(config.ExtChargeIdling)
|
||||||
@ -267,6 +351,50 @@ function facility.new(config)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Startup Recovery
|
||||||
|
|
||||||
|
-- on exit, use this to clear the boot state so we don't resume when exiting cleanly
|
||||||
|
function public.clear_boot_state()
|
||||||
|
settings.unset("LastProcessState")
|
||||||
|
settings.unset("LastUnitStates")
|
||||||
|
|
||||||
|
if not settings.save("/supervisor.settings") then
|
||||||
|
log.warning("facility.clear_boot_state(): failed to save supervisor settings file")
|
||||||
|
else
|
||||||
|
log.debug("FAC: cleared boot state on exit")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initialize facility resume boot recovery
|
||||||
|
---@param state sv_boot_state|nil
|
||||||
|
function public.boot_recovery_init(state)
|
||||||
|
if self.recovery == RCV_STATE.INACTIVE and state then
|
||||||
|
self.recovery_boot_state = state
|
||||||
|
self.recovery = RCV_STATE.PRIMED
|
||||||
|
log.info("FAC: startup resume ready")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- attempt facility resume boot recovery
|
||||||
|
---@param auto_cfg start_auto_config configuration
|
||||||
|
function public.boot_recovery_start(auto_cfg)
|
||||||
|
if self.recovery == RCV_STATE.PRIMED then
|
||||||
|
self.recovery = util.trinary(_auto_check_and_save(auto_cfg), RCV_STATE.RUNNING, RCV_STATE.STOPPED)
|
||||||
|
log.info(util.c("FAC: startup resume ", util.trinary(self.recovery == RCV_STATE.RUNNING, "started", "failed")))
|
||||||
|
else self.recovery = RCV_STATE.STOPPED end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- used on certain coordinator commands to end reboot recovery (remain in current operational state)
|
||||||
|
function public.cancel_recovery()
|
||||||
|
if self.recovery == RCV_STATE.RUNNING then
|
||||||
|
self.recovery = RCV_STATE.STOPPED
|
||||||
|
self.recovery_boot_state = nil
|
||||||
|
log.info("FAC: process startup resume cancelled by user operation")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Commands
|
--#region Commands
|
||||||
|
|
||||||
-- SCRAM all reactor units
|
-- SCRAM all reactor units
|
||||||
@ -290,59 +418,13 @@ function facility.new(config)
|
|||||||
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
||||||
|
|
||||||
-- set automatic control configuration and start the process
|
-- set automatic control configuration and start the process
|
||||||
---@param auto_cfg sys_auto_config configuration
|
---@param auto_cfg start_auto_config configuration
|
||||||
---@return table response ready state (successfully started) and current configuration (after updating)
|
---@return table response ready state (successfully started) and current configuration (after updating)
|
||||||
function public.auto_start(auto_cfg)
|
function public.auto_start(auto_cfg)
|
||||||
local charge_scaler = 1000000 -- convert MFE to FE
|
local ready, limits = _auto_check_and_save(auto_cfg)
|
||||||
local gen_scaler = 1000 -- convert kFE to FE
|
|
||||||
local ready = false
|
|
||||||
|
|
||||||
-- load up current limits
|
if ready and self.units_ready then
|
||||||
local limits = {}
|
self.mode = self.mode_set
|
||||||
for i = 1, config.UnitCount do
|
|
||||||
limits[i] = self.units[i].get_control_inf().lim_br100 * 100
|
|
||||||
end
|
|
||||||
|
|
||||||
-- only allow changes if not running
|
|
||||||
if self.mode == PROCESS.INACTIVE then
|
|
||||||
if (type(auto_cfg.mode) == "number") and (auto_cfg.mode > PROCESS.INACTIVE) and (auto_cfg.mode <= PROCESS.GEN_RATE) then
|
|
||||||
self.mode_set = auto_cfg.mode
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
|
||||||
self.burn_target = auto_cfg.burn_target
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
|
||||||
self.charge_setpoint = auto_cfg.charge_target * charge_scaler
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
|
||||||
self.gen_rate_setpoint = auto_cfg.gen_target * gen_scaler
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(auto_cfg.limits) == "table") and (#auto_cfg.limits == config.UnitCount) then
|
|
||||||
for i = 1, config.UnitCount do
|
|
||||||
local limit = auto_cfg.limits[i]
|
|
||||||
|
|
||||||
if (type(limit) == "number") and (limit >= 0.1) then
|
|
||||||
limits[i] = limit
|
|
||||||
self.units[i].set_burn_limit(limit)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ready = self.mode_set > 0
|
|
||||||
|
|
||||||
if ((self.mode_set == PROCESS.CHARGE) and (self.charge_setpoint <= 0)) or
|
|
||||||
((self.mode_set == PROCESS.GEN_RATE) and (self.gen_rate_setpoint <= 0)) or
|
|
||||||
((self.mode_set == PROCESS.BURN_RATE) and (self.burn_target < 0.1)) then
|
|
||||||
ready = false
|
|
||||||
end
|
|
||||||
|
|
||||||
ready = ready and self.units_ready
|
|
||||||
|
|
||||||
if ready then self.mode = self.mode_set end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
log.debug(util.c("FAC: process start ", util.trinary(ready, "accepted", "rejected")))
|
log.debug(util.c("FAC: process start ", util.trinary(ready, "accepted", "rejected")))
|
||||||
@ -351,8 +433,8 @@ function facility.new(config)
|
|||||||
ready,
|
ready,
|
||||||
self.mode_set,
|
self.mode_set,
|
||||||
self.burn_target,
|
self.burn_target,
|
||||||
self.charge_setpoint / charge_scaler,
|
self.charge_setpoint / CHARGE_SCALER,
|
||||||
self.gen_rate_setpoint / gen_scaler,
|
self.gen_rate_setpoint / GEN_SCALER,
|
||||||
limits
|
limits
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,17 +1,21 @@
|
|||||||
local audio = require("scada-common.audio")
|
local audio = require("scada-common.audio")
|
||||||
local const = require("scada-common.constants")
|
local const = require("scada-common.constants")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local qtypes = require("supervisor.session.rtu.qtypes")
|
local plc = require("supervisor.session.plc")
|
||||||
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
|
local qtypes = require("supervisor.session.rtu.qtypes")
|
||||||
|
|
||||||
local TONE = audio.TONE
|
local TONE = audio.TONE
|
||||||
|
|
||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
local PRIO = types.ALARM_PRIORITY
|
local PRIO = types.ALARM_PRIORITY
|
||||||
local ALARM_STATE = types.ALARM_STATE
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
|
local AUTO_GROUP = types.AUTO_GROUP
|
||||||
local CONTAINER_MODE = types.CONTAINER_MODE
|
local CONTAINER_MODE = types.CONTAINER_MODE
|
||||||
local PROCESS = types.PROCESS
|
local PROCESS = types.PROCESS
|
||||||
local PROCESS_NAMES = types.PROCESS_NAMES
|
local PROCESS_NAMES = types.PROCESS_NAMES
|
||||||
@ -131,6 +135,54 @@ end
|
|||||||
|
|
||||||
--#region PUBLIC FUNCTIONS
|
--#region PUBLIC FUNCTIONS
|
||||||
|
|
||||||
|
-- run reboot recovery routine if needed
|
||||||
|
function update.boot_recovery()
|
||||||
|
local RCV_STATE = self.types.RCV_STATE
|
||||||
|
|
||||||
|
-- attempt reboot recovery if in progress
|
||||||
|
if self.recovery == RCV_STATE.RUNNING then
|
||||||
|
local was_inactive = self.recovery_boot_state.mode == PROCESS.INACTIVE or self.recovery_boot_state.mode == PROCESS.SYSTEM_ALARM_IDLE
|
||||||
|
|
||||||
|
-- try to start auto control
|
||||||
|
if self.recovery_boot_state.mode ~= nil and self.units_ready then
|
||||||
|
if not was_inactive then
|
||||||
|
self.mode = self.mode_set
|
||||||
|
log.info("FAC: process startup resume initiated")
|
||||||
|
end
|
||||||
|
|
||||||
|
self.recovery_boot_state.mode = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local recovered = self.recovery_boot_state.mode == nil or was_inactive
|
||||||
|
|
||||||
|
-- restore manual control reactors
|
||||||
|
for i = 1, #self.units do
|
||||||
|
local u = self.units[i]
|
||||||
|
|
||||||
|
if self.recovery_boot_state.unit_states[i] and self.group_map[i] == AUTO_GROUP.MANUAL then
|
||||||
|
recovered = false
|
||||||
|
|
||||||
|
if u.get_control_inf().ready then
|
||||||
|
local plc_s = svsessions.get_reactor_session(i)
|
||||||
|
if plc_s ~= nil then
|
||||||
|
plc_s.in_queue.push_command(plc.PLC_S_CMDS.ENABLE)
|
||||||
|
log.info("FAC: startup resume enabling manually controlled reactor unit #" .. i)
|
||||||
|
|
||||||
|
-- only execute once
|
||||||
|
self.recovery_boot_state.unit_states[i] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if recovered then
|
||||||
|
self.recovery = RCV_STATE.STOPPED
|
||||||
|
self.recovery_boot_state = nil
|
||||||
|
log.info("FAC: startup resume sequence completed")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- automatic control pre-update logic
|
-- automatic control pre-update logic
|
||||||
function update.pre_auto()
|
function update.pre_auto()
|
||||||
-- unlink RTU sessions if they are closed
|
-- unlink RTU sessions if they are closed
|
||||||
@ -243,6 +295,11 @@ function update.auto_control(ExtChargeIdling)
|
|||||||
|
|
||||||
log.debug(util.c("FAC: state changed from ", PROCESS_NAMES[self.last_mode + 1], " to ", PROCESS_NAMES[self.mode + 1]))
|
log.debug(util.c("FAC: state changed from ", PROCESS_NAMES[self.last_mode + 1], " to ", PROCESS_NAMES[self.mode + 1]))
|
||||||
|
|
||||||
|
settings.set("LastProcessState", self.mode)
|
||||||
|
if not settings.save("/supervisor.settings") then
|
||||||
|
log.warning("facility_update.auto_control(): failed to save supervisor settings file")
|
||||||
|
end
|
||||||
|
|
||||||
if (self.last_mode == PROCESS.INACTIVE) or (self.last_mode == PROCESS.GEN_RATE_FAULT_IDLE) then
|
if (self.last_mode == PROCESS.INACTIVE) or (self.last_mode == PROCESS.GEN_RATE_FAULT_IDLE) then
|
||||||
self.start_fail = START_STATUS.OK
|
self.start_fail = START_STATUS.OK
|
||||||
|
|
||||||
@ -642,15 +699,16 @@ function update.auto_safety()
|
|||||||
self.ascram_reason = AUTO_SCRAM.NONE
|
self.ascram_reason = AUTO_SCRAM.NONE
|
||||||
|
|
||||||
-- reset PLC RPS trips if we should
|
-- reset PLC RPS trips if we should
|
||||||
for i = 1, #self.units do
|
for i = 1, #self.prio_defs do
|
||||||
local u = self.units[i]
|
for _, u in pairs(self.prio_defs[i]) do
|
||||||
u.auto_cond_rps_reset()
|
u.auto_cond_rps_reset()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update last mode and set next mode
|
-- update last mode, set next mode, and update saved state as needed
|
||||||
function update.post_auto()
|
function update.post_auto()
|
||||||
self.last_mode = self.mode
|
self.last_mode = self.mode
|
||||||
self.mode = next_mode
|
self.mode = next_mode
|
||||||
@ -792,6 +850,7 @@ end
|
|||||||
function update.unit_mgmt()
|
function update.unit_mgmt()
|
||||||
local insufficent_po_rate = false
|
local insufficent_po_rate = false
|
||||||
local need_emcool = false
|
local need_emcool = false
|
||||||
|
local write_state = false
|
||||||
|
|
||||||
for i = 1, #self.units do
|
for i = 1, #self.units do
|
||||||
local u = self.units[i]
|
local u = self.units[i]
|
||||||
@ -807,6 +866,21 @@ function update.unit_mgmt()
|
|||||||
if (self.cooling_conf.fac_tank_mode > 0) and u.is_emer_cool_tripped() and (self.cooling_conf.fac_tank_defs[i] == 2) then
|
if (self.cooling_conf.fac_tank_mode > 0) and u.is_emer_cool_tripped() and (self.cooling_conf.fac_tank_defs[i] == 2) then
|
||||||
need_emcool = true
|
need_emcool = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check for enabled state changes to save
|
||||||
|
if self.last_unit_states[i] ~= u.is_reactor_enabled() then
|
||||||
|
self.last_unit_states[i] = u.is_reactor_enabled()
|
||||||
|
write_state = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- record unit control states
|
||||||
|
|
||||||
|
if write_state then
|
||||||
|
settings.set("LastUnitStates", self.last_unit_states)
|
||||||
|
if not settings.save("/supervisor.settings") then
|
||||||
|
log.warning("facility_update.unit_mgmt(): failed to save supervisor settings file")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update waste product
|
-- update waste product
|
||||||
|
|||||||
@ -25,6 +25,8 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local label_fg = style.fp.label_fg
|
local label_fg = style.fp.label_fg
|
||||||
|
|
||||||
|
local term_w, _ = term.getSize()
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
||||||
@ -40,9 +42,9 @@ local function init(parent, id)
|
|||||||
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,fg_bg=label_fg}
|
||||||
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4}
|
TextBox{parent=entry,x=term_w-16,y=2,text="RTT:",width=4}
|
||||||
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local pdg_rtt = DataIndicator{parent=entry,x=term_w-11,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
TextBox{parent=entry,x=term_w-5,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,8 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local label_fg = style.fp.label_fg
|
local label_fg = style.fp.label_fg
|
||||||
|
|
||||||
|
local term_w, _ = term.getSize()
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
||||||
@ -40,13 +42,13 @@ local function init(parent, id)
|
|||||||
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.fp.label_d_fg}
|
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.fp.label_d_fg}
|
||||||
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=21,y=2,text="FW:",width=3}
|
TextBox{parent=entry,x=term_w-30,y=2,text="FW:",width=3}
|
||||||
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
local rtu_fw_v = TextBox{parent=entry,x=term_w-26,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4}
|
TextBox{parent=entry,x=term_w-15,y=2,text="RTT:",width=4}
|
||||||
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local rtu_rtt = DataIndicator{parent=entry,x=term_w-11,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,fg_bg=label_fg}
|
TextBox{parent=entry,x=term_w-5,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,8 @@ local function init(panel)
|
|||||||
local label_fg = style.fp.label_fg
|
local label_fg = style.fp.label_fg
|
||||||
local label_d_fg = style.fp.label_d_fg
|
local label_d_fg = style.fp.label_d_fg
|
||||||
|
|
||||||
|
local term_w, term_h = term.getSize()
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,fg_bg=style.theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
@ -73,9 +75,9 @@ local function init(panel)
|
|||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
local about = Div{parent=main_page,width=15,height=2,y=term_h-3,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00"}
|
local fw_v = TextBox{parent=about,text="FW: v00.00.00"}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00"}
|
local comms_v = TextBox{parent=about,text="NT: v00.00.00"}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@ -87,7 +89,7 @@ local function init(panel)
|
|||||||
-- plc sessions page
|
-- plc sessions page
|
||||||
|
|
||||||
local plc_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local plc_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local plc_list = Div{parent=plc_page,x=2,y=2,width=49}
|
local plc_list = Div{parent=plc_page,x=2,y=2,width=term_w-2}
|
||||||
|
|
||||||
for i = 1, supervisor.config.UnitCount do
|
for i = 1, supervisor.config.UnitCount do
|
||||||
local ps_prefix = "plc_" .. i .. "_"
|
local ps_prefix = "plc_" .. i .. "_"
|
||||||
@ -103,13 +105,13 @@ local function init(panel)
|
|||||||
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,fg_bg=label_d_fg}
|
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,fg_bg=label_d_fg}
|
||||||
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3}
|
TextBox{parent=plc_entry,x=term_w-28,y=2,text="FW:",width=3}
|
||||||
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
local plc_fw_v = TextBox{parent=plc_entry,x=term_w-24,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4}
|
TextBox{parent=plc_entry,x=term_w-14,y=2,text="RTT:",width=4}
|
||||||
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=label_fg}
|
local plc_rtt = DataIndicator{parent=plc_entry,x=term_w-9,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=label_fg}
|
||||||
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,fg_bg=label_fg}
|
TextBox{parent=plc_entry,x=term_w-4,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
||||||
|
|
||||||
@ -119,13 +121,13 @@ local function init(panel)
|
|||||||
-- rtu sessions page
|
-- rtu sessions page
|
||||||
|
|
||||||
local rtu_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local rtu_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local rtu_list = ListBox{parent=rtu_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=cpair(colors.black,colors.ivory),nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local rtu_list = ListBox{parent=rtu_page,y=1,height=term_h-2,width=term_w,scroll_height=1000,fg_bg=cpair(colors.black,colors.ivory),nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=rtu_list,height=1} -- padding
|
local _ = Div{parent=rtu_list,height=1} -- padding
|
||||||
|
|
||||||
-- coordinator session page
|
-- coordinator session page
|
||||||
|
|
||||||
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=s_hi_bright}
|
local crd_box = Div{parent=crd_page,x=2,y=2,width=term_w-2,height=4,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green_hc,colors.green_off)}
|
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green_hc,colors.green_off)}
|
||||||
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
||||||
@ -138,27 +140,27 @@ local function init(panel)
|
|||||||
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,fg_bg=label_fg}
|
||||||
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4}
|
TextBox{parent=crd_box,x=term_w-15,y=2,text="RTT:",width=4}
|
||||||
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
local crd_rtt = DataIndicator{parent=crd_box,x=term_w-10,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,fg_bg=label_fg}
|
TextBox{parent=crd_box,x=term_w-4,y=2,text="ms",width=4,fg_bg=label_fg}
|
||||||
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
||||||
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
||||||
|
|
||||||
-- pocket sessions page
|
-- pocket sessions page
|
||||||
|
|
||||||
local pkt_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local pkt_page = Div{parent=page_div,y=1,hidden=true}
|
||||||
local pdg_list = ListBox{parent=pkt_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local pdg_list = ListBox{parent=pkt_page,y=1,height=term_h-2,width=term_w,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=pdg_list,height=1} -- padding
|
local _ = Div{parent=pdg_list,height=1} -- padding
|
||||||
|
|
||||||
-- RTU device ID check/diagnostics page
|
-- RTU device ID check/diagnostics page
|
||||||
|
|
||||||
local chk_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local chk_page = Div{parent=page_div,y=1,hidden=true}
|
||||||
local chk_list = ListBox{parent=chk_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local chk_list = ListBox{parent=chk_page,y=1,height=term_h-2,width=term_w,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=chk_list,height=1} -- padding
|
local _ = Div{parent=chk_list,height=1} -- padding
|
||||||
|
|
||||||
-- info page
|
-- info page
|
||||||
|
|
||||||
local info_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local info_page = Div{parent=page_div,y=1,hidden=true}
|
||||||
local info = Div{parent=info_page,height=6,x=2,y=2}
|
local info = Div{parent=info_page,height=6,x=2,y=2}
|
||||||
|
|
||||||
TextBox{parent=info,text="SVR \x1a Supervisor Status"}
|
TextBox{parent=info,text="SVR \x1a Supervisor Status"}
|
||||||
@ -168,7 +170,7 @@ local function init(panel)
|
|||||||
TextBox{parent=info,text="PKT \x1a Pocket Connections"}
|
TextBox{parent=info,text="PKT \x1a Pocket Connections"}
|
||||||
TextBox{parent=info,text="DEV \x1a RTU Device/Configuration Alerts"}
|
TextBox{parent=info,text="DEV \x1a RTU Device/Configuration Alerts"}
|
||||||
|
|
||||||
local notes = Div{parent=info_page,width=49,height=8,x=2,y=9,fg_bg=style.fp.disabled_fg}
|
local notes = Div{parent=info_page,width=term_w-2,height=8,x=2,y=9,fg_bg=style.fp.disabled_fg}
|
||||||
|
|
||||||
TextBox{parent=notes,text="The DEV tab will show missing devices and devices that connected with incorrect information. Missing entries will indicate how the configuration should be, duplicate entries will indicate what is a duplicate, and out-of-range entries will indicate the invalid entry. An out-of-range example is a #2 turbine when you should only have 1 turbine for that unit."}
|
TextBox{parent=notes,text="The DEV tab will show missing devices and devices that connected with incorrect information. Missing entries will indicate how the configuration should be, duplicate entries will indicate what is a duplicate, and out-of-range entries will indicate the invalid entry. An out-of-range example is a #2 turbine when you should only have 1 turbine for that unit."}
|
||||||
|
|
||||||
|
|||||||
@ -234,6 +234,23 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
if pkt.type == CRDN_TYPE.INITIAL_BUILDS then
|
if pkt.type == CRDN_TYPE.INITIAL_BUILDS then
|
||||||
-- acknowledgement to coordinator receiving builds
|
-- acknowledgement to coordinator receiving builds
|
||||||
self.acks.builds = true
|
self.acks.builds = true
|
||||||
|
elseif pkt.type == CRDN_TYPE.PROCESS_READY then
|
||||||
|
if pkt.length == 5 then
|
||||||
|
-- coordinator has sent all initial process data, power-on recovery is now possible
|
||||||
|
|
||||||
|
---@type start_auto_config
|
||||||
|
local config = {
|
||||||
|
mode = pkt.data[1],
|
||||||
|
burn_target = pkt.data[2],
|
||||||
|
charge_target = pkt.data[3],
|
||||||
|
gen_target = pkt.data[4],
|
||||||
|
limits = pkt.data[5]
|
||||||
|
}
|
||||||
|
|
||||||
|
facility.boot_recovery_start(config)
|
||||||
|
else
|
||||||
|
log.debug(log_tag .. "CRDN process ready packet length mismatch")
|
||||||
|
end
|
||||||
elseif pkt.type == CRDN_TYPE.FAC_BUILDS then
|
elseif pkt.type == CRDN_TYPE.FAC_BUILDS then
|
||||||
-- acknowledgement to coordinator receiving builds
|
-- acknowledgement to coordinator receiving builds
|
||||||
self.acks.fac_builds = true
|
self.acks.fac_builds = true
|
||||||
@ -243,8 +260,11 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
|
|
||||||
if cmd == FAC_COMMAND.SCRAM_ALL then
|
if cmd == FAC_COMMAND.SCRAM_ALL then
|
||||||
facility.scram_all()
|
facility.scram_all()
|
||||||
|
facility.cancel_recovery()
|
||||||
_send(CRDN_TYPE.FAC_CMD, { cmd, true })
|
_send(CRDN_TYPE.FAC_CMD, { cmd, true })
|
||||||
elseif cmd == FAC_COMMAND.STOP then
|
elseif cmd == FAC_COMMAND.STOP then
|
||||||
|
facility.cancel_recovery()
|
||||||
|
|
||||||
local was_active = facility.auto_is_active()
|
local was_active = facility.auto_is_active()
|
||||||
|
|
||||||
if was_active then
|
if was_active then
|
||||||
@ -253,15 +273,16 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
|
|
||||||
_send(CRDN_TYPE.FAC_CMD, { cmd, was_active })
|
_send(CRDN_TYPE.FAC_CMD, { cmd, was_active })
|
||||||
elseif cmd == FAC_COMMAND.START then
|
elseif cmd == FAC_COMMAND.START then
|
||||||
|
facility.cancel_recovery()
|
||||||
|
|
||||||
if pkt.length == 6 then
|
if pkt.length == 6 then
|
||||||
---@type sys_auto_config
|
---@class start_auto_config
|
||||||
---@diagnostic disable-next-line: missing-fields
|
|
||||||
local config = {
|
local config = {
|
||||||
mode = pkt.data[2],
|
mode = pkt.data[2], ---@type PROCESS
|
||||||
burn_target = pkt.data[3],
|
burn_target = pkt.data[3], ---@type number
|
||||||
charge_target = pkt.data[4],
|
charge_target = pkt.data[4], ---@type number
|
||||||
gen_target = pkt.data[5],
|
gen_target = pkt.data[5], ---@type number
|
||||||
limits = pkt.data[6]
|
limits = pkt.data[6] ---@type number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
_send(CRDN_TYPE.FAC_CMD, { cmd, table.unpack(facility.auto_start(config)) })
|
_send(CRDN_TYPE.FAC_CMD, { cmd, table.unpack(facility.auto_start(config)) })
|
||||||
@ -313,8 +334,11 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
local manual = facility.get_group(uid) == AUTO_GROUP.MANUAL
|
local manual = facility.get_group(uid) == AUTO_GROUP.MANUAL
|
||||||
|
|
||||||
if cmd == UNIT_COMMAND.SCRAM then
|
if cmd == UNIT_COMMAND.SCRAM then
|
||||||
|
facility.cancel_recovery()
|
||||||
out_queue.push_data(SV_Q_DATA.SCRAM, data)
|
out_queue.push_data(SV_Q_DATA.SCRAM, data)
|
||||||
elseif cmd == UNIT_COMMAND.START then
|
elseif cmd == UNIT_COMMAND.START then
|
||||||
|
facility.cancel_recovery()
|
||||||
|
|
||||||
if manual then
|
if manual then
|
||||||
out_queue.push_data(SV_Q_DATA.START, data)
|
out_queue.push_data(SV_Q_DATA.START, data)
|
||||||
else
|
else
|
||||||
@ -324,6 +348,8 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
elseif cmd == UNIT_COMMAND.RESET_RPS then
|
elseif cmd == UNIT_COMMAND.RESET_RPS then
|
||||||
out_queue.push_data(SV_Q_DATA.RESET_RPS, data)
|
out_queue.push_data(SV_Q_DATA.RESET_RPS, data)
|
||||||
elseif cmd == UNIT_COMMAND.SET_BURN then
|
elseif cmd == UNIT_COMMAND.SET_BURN then
|
||||||
|
facility.cancel_recovery()
|
||||||
|
|
||||||
if pkt.length == 3 then
|
if pkt.length == 3 then
|
||||||
if manual then
|
if manual then
|
||||||
out_queue.push_data(SV_Q_DATA.SET_BURN, data)
|
out_queue.push_data(SV_Q_DATA.SET_BURN, data)
|
||||||
@ -354,6 +380,8 @@ function coordinator.new_session(id, s_addr, i_seq_num, in_queue, out_queue, tim
|
|||||||
log.debug(log_tag .. "CRDN unit command reset alarm missing alarm id")
|
log.debug(log_tag .. "CRDN unit command reset alarm missing alarm id")
|
||||||
end
|
end
|
||||||
elseif cmd == UNIT_COMMAND.SET_GROUP then
|
elseif cmd == UNIT_COMMAND.SET_GROUP then
|
||||||
|
facility.cancel_recovery()
|
||||||
|
|
||||||
if (pkt.length == 3) and (type(pkt.data[3]) == "number") and
|
if (pkt.length == 3) and (type(pkt.data[3]) == "number") and
|
||||||
(pkt.data[3] >= AUTO_GROUP.MANUAL) and (pkt.data[3] <= AUTO_GROUP.BACKUP) then
|
(pkt.data[3] >= AUTO_GROUP.MANUAL) and (pkt.data[3] <= AUTO_GROUP.BACKUP) then
|
||||||
facility.set_group(unit.get_id(), pkt.data[3])
|
facility.set_group(unit.get_id(), pkt.data[3])
|
||||||
|
|||||||
@ -53,15 +53,15 @@ local PERIODICS = {
|
|||||||
---@param in_queue mqueue in message queue
|
---@param in_queue mqueue in message queue
|
||||||
---@param out_queue mqueue out message queue
|
---@param out_queue mqueue out message queue
|
||||||
---@param timeout number communications timeout
|
---@param timeout number communications timeout
|
||||||
|
---@param initial_reset boolean[] initial PLC reset on timeout flags, indexed by reactor_id
|
||||||
---@param fp_ok boolean if the front panel UI is running
|
---@param fp_ok boolean if the front panel UI is running
|
||||||
function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue, timeout, fp_ok)
|
function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue, timeout, initial_reset, fp_ok)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
local log_tag = "plc_session(" .. id .. "): "
|
local log_tag = "plc_session(" .. id .. "): "
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
commanded_state = false,
|
|
||||||
commanded_burn_rate = 0.0,
|
commanded_burn_rate = 0.0,
|
||||||
auto_cmd_token = 0,
|
auto_cmd_token = 0,
|
||||||
ramping_rate = false,
|
ramping_rate = false,
|
||||||
@ -72,6 +72,7 @@ function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue,
|
|||||||
connected = true,
|
connected = true,
|
||||||
received_struct = false,
|
received_struct = false,
|
||||||
received_status_cache = false,
|
received_status_cache = false,
|
||||||
|
received_rps_status = false,
|
||||||
conn_watchdog = util.new_watchdog(timeout),
|
conn_watchdog = util.new_watchdog(timeout),
|
||||||
last_rtt = 0,
|
last_rtt = 0,
|
||||||
-- periodic messages
|
-- periodic messages
|
||||||
@ -381,6 +382,16 @@ function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue,
|
|||||||
local status = pcall(_copy_rps_status, pkt.data)
|
local status = pcall(_copy_rps_status, pkt.data)
|
||||||
if status then
|
if status then
|
||||||
-- copied in RPS status data OK
|
-- copied in RPS status data OK
|
||||||
|
self.received_rps_status = true
|
||||||
|
|
||||||
|
-- try initial reset if needed
|
||||||
|
if initial_reset[reactor_id] then
|
||||||
|
initial_reset[reactor_id] = false
|
||||||
|
if self.sDB.rps_trip_cause == "timeout" then
|
||||||
|
_send(RPLC_TYPE.RPS_AUTO_RESET, {})
|
||||||
|
log.debug(log_tag .. "initial RPS reset on timeout status sent")
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
-- error copying RPS status data
|
-- error copying RPS status data
|
||||||
log.error(log_tag .. "failed to parse RPS status packet data")
|
log.error(log_tag .. "failed to parse RPS status packet data")
|
||||||
@ -394,6 +405,16 @@ function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue,
|
|||||||
local status = pcall(_copy_rps_status, { true, table.unpack(pkt.data) })
|
local status = pcall(_copy_rps_status, { true, table.unpack(pkt.data) })
|
||||||
if status then
|
if status then
|
||||||
-- copied in RPS status data OK
|
-- copied in RPS status data OK
|
||||||
|
self.received_rps_status = true
|
||||||
|
|
||||||
|
-- try initial reset if needed
|
||||||
|
if initial_reset[reactor_id] then
|
||||||
|
initial_reset[reactor_id] = false
|
||||||
|
if self.sDB.rps_trip_cause == "timeout" then
|
||||||
|
_send(RPLC_TYPE.RPS_AUTO_RESET, {})
|
||||||
|
log.debug(log_tag .. "initial RPS reset on timeout alarm sent")
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
-- error copying RPS status data
|
-- error copying RPS status data
|
||||||
log.error(log_tag .. "failed to parse RPS alarm status data")
|
log.error(log_tag .. "failed to parse RPS alarm status data")
|
||||||
@ -487,6 +508,10 @@ function plc.new_session(id, s_addr, i_seq_num, reactor_id, in_queue, out_queue,
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.get_db() return self.sDB end
|
function public.get_db() return self.sDB end
|
||||||
|
|
||||||
|
-- check if the reactor structure, status, and RPS status have been received
|
||||||
|
---@nodiscard
|
||||||
|
function public.check_received_all_data() return self.received_struct and self.received_status_cache and self.received_rps_status end
|
||||||
|
|
||||||
-- check if ramping is completed by first verifying auto command token ack
|
-- check if ramping is completed by first verifying auto command token ack
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_ramp_complete()
|
function public.is_ramp_complete()
|
||||||
|
|||||||
@ -45,6 +45,7 @@ local self = {
|
|||||||
fp_ok = false,
|
fp_ok = false,
|
||||||
config = nil, ---@type svr_config
|
config = nil, ---@type svr_config
|
||||||
facility = nil, ---@type facility|nil
|
facility = nil, ---@type facility|nil
|
||||||
|
plc_ini_reset = {},
|
||||||
-- lists of connected sessions
|
-- lists of connected sessions
|
||||||
---@diagnostic disable: missing-fields
|
---@diagnostic disable: missing-fields
|
||||||
sessions = {
|
sessions = {
|
||||||
@ -391,6 +392,7 @@ function svsessions.init(nic, fp_ok, config, facility)
|
|||||||
conns.tanks[1] = true
|
conns.tanks[1] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.plc_ini_reset[i] = true
|
||||||
self.dev_dbg.connected.units[i] = conns
|
self.dev_dbg.connected.units[i] = conns
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -486,7 +488,7 @@ function svsessions.establish_plc_session(source_addr, i_seq_num, for_reactor, v
|
|||||||
|
|
||||||
local id = self.next_ids.plc
|
local id = self.next_ids.plc
|
||||||
|
|
||||||
plc_s.instance = plc.new_session(id, source_addr, i_seq_num, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
plc_s.instance = plc.new_session(id, source_addr, i_seq_num, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.plc_ini_reset, self.fp_ok)
|
||||||
table.insert(self.sessions.plc, plc_s)
|
table.insert(self.sessions.plc, plc_s)
|
||||||
|
|
||||||
local units = self.facility.get_units()
|
local units = self.facility.get_units()
|
||||||
|
|||||||
@ -10,6 +10,7 @@ local log = require("scada-common.log")
|
|||||||
local network = require("scada-common.network")
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
@ -22,7 +23,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v1.6.2"
|
local SUPERVISOR_VERSION = "v1.6.8"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -72,6 +73,21 @@ if config.FacilityTankMode > 0 then
|
|||||||
cfv.assert_type_int(def)
|
cfv.assert_type_int(def)
|
||||||
cfv.assert_range(def, 0, 2)
|
cfv.assert_range(def, 0, 2)
|
||||||
assert(cfv.valid(), "startup> invalid facility tank definition for reactor unit " .. i)
|
assert(cfv.valid(), "startup> invalid facility tank definition for reactor unit " .. i)
|
||||||
|
|
||||||
|
local entry = config.FacilityTankList[i]
|
||||||
|
cfv.assert_type_int(entry)
|
||||||
|
cfv.assert_range(entry, 0, 2)
|
||||||
|
assert(cfv.valid(), "startup> invalid facility tank list entry for tank " .. i)
|
||||||
|
|
||||||
|
local conn = config.FacilityTankConns[i]
|
||||||
|
cfv.assert_type_int(conn)
|
||||||
|
cfv.assert_range(conn, 0, #config.FacilityTankDefs)
|
||||||
|
assert(cfv.valid(), "startup> invalid facility tank connection for reactor unit " .. i)
|
||||||
|
|
||||||
|
local type = config.TankFluidTypes[i]
|
||||||
|
cfv.assert_type_int(type)
|
||||||
|
cfv.assert_range(type, 0, types.COOLANT_TYPE.SODIUM)
|
||||||
|
assert(cfv.valid(), "startup> invalid tank fluid type for tank " .. i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -147,6 +163,9 @@ local function main()
|
|||||||
-- halve the rate heartbeat LED flash
|
-- halve the rate heartbeat LED flash
|
||||||
local heartbeat_toggle = true
|
local heartbeat_toggle = true
|
||||||
|
|
||||||
|
-- init startup recovery
|
||||||
|
sv_facility.boot_recovery_init(supervisor.boot_state)
|
||||||
|
|
||||||
-- event loop
|
-- event loop
|
||||||
while true do
|
while true do
|
||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
@ -237,6 +256,8 @@ local function main()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
sv_facility.clear_boot_state()
|
||||||
|
|
||||||
renderer.close_ui()
|
renderer.close_ui()
|
||||||
|
|
||||||
util.println_ts("exited")
|
util.println_ts("exited")
|
||||||
|
|||||||
@ -19,10 +19,24 @@ local config = {}
|
|||||||
|
|
||||||
supervisor.config = config
|
supervisor.config = config
|
||||||
|
|
||||||
-- load the supervisor configuration
|
-- control state from last unexpected shutdown
|
||||||
|
supervisor.boot_state = nil ---@type sv_boot_state|nil
|
||||||
|
|
||||||
|
-- load the supervisor configuration and startup state
|
||||||
function supervisor.load_config()
|
function supervisor.load_config()
|
||||||
if not settings.load("/supervisor.settings") then return false end
|
if not settings.load("/supervisor.settings") then return false end
|
||||||
|
|
||||||
|
---@class sv_boot_state
|
||||||
|
local boot_state = {
|
||||||
|
mode = settings.get("LastProcessState"), ---@type PROCESS
|
||||||
|
unit_states = settings.get("LastUnitStates") ---@type boolean[]
|
||||||
|
}
|
||||||
|
|
||||||
|
-- only record boot state if likely valid
|
||||||
|
if type(boot_state.mode) == "number" and type(boot_state.unit_states) == "table" then
|
||||||
|
supervisor.boot_state = boot_state
|
||||||
|
end
|
||||||
|
|
||||||
config.UnitCount = settings.get("UnitCount")
|
config.UnitCount = settings.get("UnitCount")
|
||||||
config.CoolingConfig = settings.get("CoolingConfig")
|
config.CoolingConfig = settings.get("CoolingConfig")
|
||||||
config.FacilityTankMode = settings.get("FacilityTankMode")
|
config.FacilityTankMode = settings.get("FacilityTankMode")
|
||||||
@ -30,6 +44,7 @@ function supervisor.load_config()
|
|||||||
config.FacilityTankList = settings.get("FacilityTankList")
|
config.FacilityTankList = settings.get("FacilityTankList")
|
||||||
config.FacilityTankConns = settings.get("FacilityTankConns")
|
config.FacilityTankConns = settings.get("FacilityTankConns")
|
||||||
config.TankFluidTypes = settings.get("TankFluidTypes")
|
config.TankFluidTypes = settings.get("TankFluidTypes")
|
||||||
|
config.AuxiliaryCoolant = settings.get("AuxiliaryCoolant")
|
||||||
config.ExtChargeIdling = settings.get("ExtChargeIdling")
|
config.ExtChargeIdling = settings.get("ExtChargeIdling")
|
||||||
|
|
||||||
config.SVR_Channel = settings.get("SVR_Channel")
|
config.SVR_Channel = settings.get("SVR_Channel")
|
||||||
@ -64,6 +79,7 @@ function supervisor.load_config()
|
|||||||
cfv.assert_type_table(config.FacilityTankList)
|
cfv.assert_type_table(config.FacilityTankList)
|
||||||
cfv.assert_type_table(config.FacilityTankConns)
|
cfv.assert_type_table(config.FacilityTankConns)
|
||||||
cfv.assert_type_table(config.TankFluidTypes)
|
cfv.assert_type_table(config.TankFluidTypes)
|
||||||
|
cfv.assert_type_table(config.AuxiliaryCoolant)
|
||||||
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
||||||
|
|
||||||
cfv.assert_type_bool(config.ExtChargeIdling)
|
cfv.assert_type_bool(config.ExtChargeIdling)
|
||||||
@ -246,20 +262,32 @@ function supervisor.comms(_version, nic, fp_ok, facility)
|
|||||||
-- PLC linking request
|
-- PLC linking request
|
||||||
if packet.length == 4 and type(packet.data[4]) == "number" then
|
if packet.length == 4 and type(packet.data[4]) == "number" then
|
||||||
local reactor_id = packet.data[4]
|
local reactor_id = packet.data[4]
|
||||||
local plc_id = svsessions.establish_plc_session(src_addr, i_seq_num, reactor_id, firmware_v)
|
|
||||||
|
|
||||||
if plc_id == false then
|
-- check ID validity
|
||||||
-- reactor already has a PLC assigned
|
if reactor_id < 1 or reactor_id > config.UnitCount then
|
||||||
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
-- reactor index out of range
|
||||||
log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id))
|
if last_ack ~= ESTABLISH_ACK.DENY then
|
||||||
|
log.warning(util.c("PLC_ESTABLISH: denied assignment ", reactor_id, " outside of configured unit count ", config.UnitCount))
|
||||||
end
|
end
|
||||||
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
else
|
else
|
||||||
-- got an ID; assigned to a reactor successfully
|
-- try to establish the session
|
||||||
println(util.c("PLC (", firmware_v, ") [@", src_addr, "] \xbb reactor ", reactor_id, " connected"))
|
local plc_id = svsessions.establish_plc_session(src_addr, i_seq_num, reactor_id, firmware_v)
|
||||||
log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", src_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id))
|
|
||||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
if plc_id == false then
|
||||||
|
-- reactor already has a PLC assigned
|
||||||
|
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
||||||
|
log.warning(util.c("PLC_ESTABLISH: assignment collision with reactor ", reactor_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.COLLISION)
|
||||||
|
else
|
||||||
|
-- got an ID; assigned to a reactor successfully
|
||||||
|
println(util.c("PLC (", firmware_v, ") [@", src_addr, "] \xbb reactor ", reactor_id, " connected"))
|
||||||
|
log.info(util.c("PLC_ESTABLISH: PLC (", firmware_v, ") [@", src_addr, "] reactor unit ", reactor_id, " PLC connected with session ID ", plc_id))
|
||||||
|
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type")
|
log.debug("PLC_ESTABLISH: packet length mismatch/bad parameter type")
|
||||||
|
|||||||
@ -66,7 +66,8 @@ local unit = {}
|
|||||||
---@param num_boilers integer number of boilers expected
|
---@param num_boilers integer number of boilers expected
|
||||||
---@param num_turbines integer number of turbines expected
|
---@param num_turbines integer number of turbines expected
|
||||||
---@param ext_idle boolean extended idling mode
|
---@param ext_idle boolean extended idling mode
|
||||||
function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
---@param aux_coolant boolean if this unit has auxiliary coolant
|
||||||
|
function unit.new(reactor_id, num_boilers, num_turbines, ext_idle, aux_coolant)
|
||||||
-- time (ms) to idle for auto idling
|
-- time (ms) to idle for auto idling
|
||||||
local IDLE_TIME = util.trinary(ext_idle, 60000, 10000)
|
local IDLE_TIME = util.trinary(ext_idle, 60000, 10000)
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
plc_i = nil, ---@type plc_session
|
plc_i = nil, ---@type plc_session
|
||||||
num_boilers = num_boilers,
|
num_boilers = num_boilers,
|
||||||
num_turbines = num_turbines,
|
num_turbines = num_turbines,
|
||||||
|
aux_coolant = aux_coolant,
|
||||||
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
||||||
-- rtus
|
-- rtus
|
||||||
rtu_list = {}, ---@type unit_session[][]
|
rtu_list = {}, ---@type unit_session[][]
|
||||||
@ -92,7 +94,8 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
io_ctl = nil, ---@type rs_controller
|
io_ctl = nil, ---@type rs_controller
|
||||||
---@diagnostic disable-next-line: missing-fields
|
---@diagnostic disable-next-line: missing-fields
|
||||||
valves = {}, ---@type unit_valves
|
valves = {}, ---@type unit_valves
|
||||||
emcool_opened = false,
|
em_cool_opened = false,
|
||||||
|
aux_cool_opened = false,
|
||||||
-- auto control
|
-- auto control
|
||||||
auto_engaged = false,
|
auto_engaged = false,
|
||||||
auto_idle = false,
|
auto_idle = false,
|
||||||
@ -111,6 +114,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
damage_est_last = 0,
|
damage_est_last = 0,
|
||||||
waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
waste_product = WASTE.PLUTONIUM, ---@type WASTE_PRODUCT
|
||||||
status_text = { "UNKNOWN", "awaiting connection..." },
|
status_text = { "UNKNOWN", "awaiting connection..." },
|
||||||
|
enable_aux_cool = false,
|
||||||
-- logic for alarms
|
-- logic for alarms
|
||||||
had_reactor = false,
|
had_reactor = false,
|
||||||
turbine_flow_stable = false,
|
turbine_flow_stable = false,
|
||||||
@ -373,6 +377,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
local waste_po = _make_valve_iface(IO.WASTE_POPL)
|
local waste_po = _make_valve_iface(IO.WASTE_POPL)
|
||||||
local waste_sps = _make_valve_iface(IO.WASTE_AM)
|
local waste_sps = _make_valve_iface(IO.WASTE_AM)
|
||||||
local emer_cool = _make_valve_iface(IO.U_EMER_COOL)
|
local emer_cool = _make_valve_iface(IO.U_EMER_COOL)
|
||||||
|
local aux_cool = _make_valve_iface(IO.U_AUX_COOL)
|
||||||
|
|
||||||
---@class unit_valves
|
---@class unit_valves
|
||||||
self.valves = {
|
self.valves = {
|
||||||
@ -380,7 +385,8 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
waste_sna = waste_sna,
|
waste_sna = waste_sna,
|
||||||
waste_po = waste_po,
|
waste_po = waste_po,
|
||||||
waste_sps = waste_sps,
|
waste_sps = waste_sps,
|
||||||
emer_cool = emer_cool
|
emer_cool = emer_cool,
|
||||||
|
aux_cool = aux_cool
|
||||||
}
|
}
|
||||||
|
|
||||||
-- route reactor waste for a given waste product
|
-- route reactor waste for a given waste product
|
||||||
@ -606,7 +612,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
if #self.redstone > 0 then
|
if #self.redstone > 0 then
|
||||||
logic.handle_redstone(self)
|
logic.handle_redstone(self)
|
||||||
elseif not self.plc_cache.rps_trip then
|
elseif not self.plc_cache.rps_trip then
|
||||||
self.emcool_opened = false
|
self.em_cool_opened = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -724,7 +730,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
|
|
||||||
-- queue a command to clear timeout/auto-scram if set
|
-- queue a command to clear timeout/auto-scram if set
|
||||||
function public.auto_cond_rps_reset()
|
function public.auto_cond_rps_reset()
|
||||||
if self.plc_s ~= nil and self.plc_i ~= nil and (not self.auto_was_alarmed) and (not self.emcool_opened) then
|
if self.plc_s ~= nil and self.plc_i ~= nil and (not self.auto_was_alarmed) and (not self.em_cool_opened) then
|
||||||
local rps = self.plc_i.get_rps()
|
local rps = self.plc_i.get_rps()
|
||||||
if rps.timeout or rps.automatic then
|
if rps.timeout or rps.automatic then
|
||||||
self.plc_i.auto_lock(true) -- if it timed out/restarted, auto lock was lost, so re-lock it
|
self.plc_i.auto_lock(true) -- if it timed out/restarted, auto lock was lost, so re-lock it
|
||||||
@ -840,6 +846,12 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check the active state of the reactor (if connected)
|
||||||
|
---@nodiscard
|
||||||
|
function public.is_reactor_enabled()
|
||||||
|
if self.plc_i ~= nil then return self.plc_i.get_status().status else return false end
|
||||||
|
end
|
||||||
|
|
||||||
-- check if the reactor is connected, is stopped, the RPS is not tripped, and no alarms are active
|
-- check if the reactor is connected, is stopped, the RPS is not tripped, and no alarms are active
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_safe_idle()
|
function public.is_safe_idle()
|
||||||
@ -859,7 +871,7 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
|
|
||||||
-- check if emergency coolant activation has been tripped
|
-- check if emergency coolant activation has been tripped
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.is_emer_cool_tripped() return self.emcool_opened end
|
function public.is_emer_cool_tripped() return self.em_cool_opened end
|
||||||
|
|
||||||
-- get build properties of machines
|
-- get build properties of machines
|
||||||
--
|
--
|
||||||
@ -1053,7 +1065,8 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
v.waste_sna.check(),
|
v.waste_sna.check(),
|
||||||
v.waste_po.check(),
|
v.waste_po.check(),
|
||||||
v.waste_sps.check(),
|
v.waste_sps.check(),
|
||||||
v.emer_cool.check()
|
v.emer_cool.check(),
|
||||||
|
v.aux_cool.check()
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@ local FLOW_STABILITY_DELAY_MS = const.FLOW_STABILITY_DELAY_MS
|
|||||||
|
|
||||||
local ANNUNC_LIMS = const.ANNUNCIATOR_LIMITS
|
local ANNUNC_LIMS = const.ANNUNCIATOR_LIMITS
|
||||||
local ALARM_LIMS = const.ALARM_LIMITS
|
local ALARM_LIMS = const.ALARM_LIMITS
|
||||||
|
local RS_THRESH = const.RS_THRESHOLDS
|
||||||
|
|
||||||
---@class unit_logic_extension
|
---@class unit_logic_extension
|
||||||
local logic = {}
|
local logic = {}
|
||||||
@ -54,6 +55,10 @@ function logic.update_annunciator(self)
|
|||||||
-- variables for boiler, or reactor if no boilers used
|
-- variables for boiler, or reactor if no boilers used
|
||||||
local total_boil_rate = 0.0
|
local total_boil_rate = 0.0
|
||||||
|
|
||||||
|
-- auxiliary coolant control
|
||||||
|
local need_aux_cool = false
|
||||||
|
local dis_aux_cool = true
|
||||||
|
|
||||||
--#region Reactor
|
--#region Reactor
|
||||||
|
|
||||||
annunc.AutoControl = self.auto_engaged
|
annunc.AutoControl = self.auto_engaged
|
||||||
@ -67,11 +72,10 @@ function logic.update_annunciator(self)
|
|||||||
local plc_db = self.plc_i.get_db()
|
local plc_db = self.plc_i.get_db()
|
||||||
|
|
||||||
-- update ready state
|
-- update ready state
|
||||||
-- - can't be tripped
|
-- - must be connected to a formed reactor
|
||||||
-- - must have received status at least once
|
-- - can't have a tripped RPS
|
||||||
-- - must have received struct at least once
|
-- - must have received status, struct, and RPS status at least once
|
||||||
plc_ready = plc_db.formed and (not plc_db.no_reactor) and (not plc_db.rps_tripped) and
|
plc_ready = plc_db.formed and (not plc_db.no_reactor) and (not plc_db.rps_tripped) and self.plc_i.check_received_all_data()
|
||||||
(next(self.plc_i.get_status()) ~= nil) and (next(self.plc_i.get_struct()) ~= nil)
|
|
||||||
|
|
||||||
-- update auto control limit
|
-- update auto control limit
|
||||||
if (plc_db.mek_struct.max_burn > 0) and ((self.db.control.lim_br100 / 100) > plc_db.mek_struct.max_burn) then
|
if (plc_db.mek_struct.max_burn > 0) and ((self.db.control.lim_br100 / 100) > plc_db.mek_struct.max_burn) then
|
||||||
@ -149,6 +153,9 @@ function logic.update_annunciator(self)
|
|||||||
-- if no boilers, use reactor heating rate to check for boil rate mismatch
|
-- if no boilers, use reactor heating rate to check for boil rate mismatch
|
||||||
if num_boilers == 0 then
|
if num_boilers == 0 then
|
||||||
total_boil_rate = plc_db.mek_status.heating_rate
|
total_boil_rate = plc_db.mek_status.heating_rate
|
||||||
|
|
||||||
|
need_aux_cool = plc_db.mek_status.ccool_fill <= RS_THRESH.AUX_COOL_ENABLE
|
||||||
|
dis_aux_cool = plc_db.mek_status.ccool_fill >= RS_THRESH.AUX_COOL_DISABLE
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.plc_cache.ok = false
|
self.plc_cache.ok = false
|
||||||
@ -216,6 +223,9 @@ function logic.update_annunciator(self)
|
|||||||
|
|
||||||
annunc.BoilerOnline[idx] = true
|
annunc.BoilerOnline[idx] = true
|
||||||
annunc.WaterLevelLow[idx] = boiler.tanks.water_fill < ANNUNC_LIMS.WaterLevelLow
|
annunc.WaterLevelLow[idx] = boiler.tanks.water_fill < ANNUNC_LIMS.WaterLevelLow
|
||||||
|
|
||||||
|
need_aux_cool = need_aux_cool or (boiler.tanks.water_fill <= RS_THRESH.AUX_COOL_ENABLE)
|
||||||
|
dis_aux_cool = dis_aux_cool and (boiler.tanks.water_fill >= RS_THRESH.AUX_COOL_DISABLE)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check heating rate low
|
-- check heating rate low
|
||||||
@ -342,11 +352,11 @@ function logic.update_annunciator(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if rotation_stable then
|
if rotation_stable then
|
||||||
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached rotational stability (", rotation, ")"))
|
log.debug(util.c("UNIT ", self.r_id, " turbine ", idx, " reached rotational stability (", rotation, ")"))
|
||||||
end
|
end
|
||||||
|
|
||||||
if flow_stable then
|
if flow_stable then
|
||||||
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached flow stability (", turbine.state.flow_rate, " mB/t)"))
|
log.debug(util.c("UNIT ", self.r_id, " turbine ", idx, " reached flow stability (", turbine.state.flow_rate, " mB/t)"))
|
||||||
end
|
end
|
||||||
|
|
||||||
turbines_stable = turbines_stable and (rotation_stable or flow_stable)
|
turbines_stable = turbines_stable and (rotation_stable or flow_stable)
|
||||||
@ -358,7 +368,7 @@ function logic.update_annunciator(self)
|
|||||||
|
|
||||||
turbines_stable = false
|
turbines_stable = false
|
||||||
|
|
||||||
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reset stability (new rate ", turbine.state.steam_input_rate, " != ", last.input_rate," mB/t)"))
|
log.debug(util.c("UNIT ", self.r_id, " turbine ", idx, " reset stability (new rate ", turbine.state.steam_input_rate, " != ", last.input_rate," mB/t)"))
|
||||||
end
|
end
|
||||||
|
|
||||||
last.input_rate = turbine.state.steam_input_rate
|
last.input_rate = turbine.state.steam_input_rate
|
||||||
@ -407,6 +417,12 @@ function logic.update_annunciator(self)
|
|||||||
|
|
||||||
-- update auto control ready state for this unit
|
-- update auto control ready state for this unit
|
||||||
self.db.control.ready = plc_ready and boilers_ready and turbines_ready
|
self.db.control.ready = plc_ready and boilers_ready and turbines_ready
|
||||||
|
|
||||||
|
-- update auxiliary coolant command
|
||||||
|
if plc_ready then
|
||||||
|
self.enable_aux_cool = self.plc_i.get_db().mek_status.status and
|
||||||
|
(self.enable_aux_cool or need_aux_cool) and not (dis_aux_cool and self.turbine_flow_stable)
|
||||||
|
else self.enable_aux_cool = false end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update an alarm state given conditions
|
-- update an alarm state given conditions
|
||||||
@ -729,7 +745,7 @@ function logic.update_status_text(self)
|
|||||||
self.status_text = { "RCS TRANSIENT", "check coolant system" }
|
self.status_text = { "RCS TRANSIENT", "check coolant system" }
|
||||||
-- elseif is_active(self.alarms.RPSTransient) then
|
-- elseif is_active(self.alarms.RPSTransient) then
|
||||||
-- RPS status handled when checking reactor status
|
-- RPS status handled when checking reactor status
|
||||||
elseif self.emcool_opened then
|
elseif self.em_cool_opened then
|
||||||
self.status_text = { "EMERGENCY COOLANT OPENED", "reset RPS to close valve" }
|
self.status_text = { "EMERGENCY COOLANT OPENED", "reset RPS to close valve" }
|
||||||
-- connection dependent states
|
-- connection dependent states
|
||||||
elseif self.plc_i ~= nil then
|
elseif self.plc_i ~= nil then
|
||||||
@ -887,7 +903,7 @@ function logic.handle_redstone(self)
|
|||||||
(annunc.CoolantLevelLow or (boiler_water_low and rps.ex_hcool)) and
|
(annunc.CoolantLevelLow or (boiler_water_low and rps.ex_hcool)) and
|
||||||
is_active(self.alarms.ReactorOverTemp))
|
is_active(self.alarms.ReactorOverTemp))
|
||||||
|
|
||||||
if enable_emer_cool and not self.emcool_opened then
|
if enable_emer_cool and not self.em_cool_opened then
|
||||||
log.debug(util.c(">> Emergency Coolant Enable Detail Report (Unit ", self.r_id, ") <<"))
|
log.debug(util.c(">> Emergency Coolant Enable Detail Report (Unit ", self.r_id, ") <<"))
|
||||||
log.debug(util.c("| CoolantLevelLow[", annunc.CoolantLevelLow, "] CoolantLevelLowLow[", rps.low_cool, "] ExcessHeatedCoolant[", rps.ex_hcool, "]"))
|
log.debug(util.c("| CoolantLevelLow[", annunc.CoolantLevelLow, "] CoolantLevelLowLow[", rps.low_cool, "] ExcessHeatedCoolant[", rps.ex_hcool, "]"))
|
||||||
log.debug(util.c("| ReactorOverTemp[", AISTATE_NAMES[self.alarms.ReactorOverTemp.state], "]"))
|
log.debug(util.c("| ReactorOverTemp[", AISTATE_NAMES[self.alarms.ReactorOverTemp.state], "]"))
|
||||||
@ -911,13 +927,13 @@ function logic.handle_redstone(self)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if annunc.EmergencyCoolant > 1 and self.emcool_opened then
|
if annunc.EmergencyCoolant > 1 and self.em_cool_opened then
|
||||||
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve closed"))
|
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve closed"))
|
||||||
log.info(util.c("UNIT ", self.r_id, " turbines set to not dump steam"))
|
log.info(util.c("UNIT ", self.r_id, " turbines set to not dump steam"))
|
||||||
end
|
end
|
||||||
|
|
||||||
self.emcool_opened = false
|
self.em_cool_opened = false
|
||||||
elseif enable_emer_cool or self.emcool_opened then
|
elseif enable_emer_cool or self.em_cool_opened then
|
||||||
-- set turbines to dump excess steam
|
-- set turbines to dump excess steam
|
||||||
for i = 1, #self.turbines do
|
for i = 1, #self.turbines do
|
||||||
local session = self.turbines[i]
|
local session = self.turbines[i]
|
||||||
@ -938,16 +954,33 @@ function logic.handle_redstone(self)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if annunc.EmergencyCoolant > 1 and not self.emcool_opened then
|
if annunc.EmergencyCoolant > 1 and not self.em_cool_opened then
|
||||||
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve opened"))
|
log.info(util.c("UNIT ", self.r_id, " emergency coolant valve opened"))
|
||||||
log.info(util.c("UNIT ", self.r_id, " turbines set to dump excess steam"))
|
log.info(util.c("UNIT ", self.r_id, " turbines set to dump excess steam"))
|
||||||
end
|
end
|
||||||
|
|
||||||
self.emcool_opened = true
|
self.em_cool_opened = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set valve state always
|
-- set valve state always
|
||||||
if self.emcool_opened then self.valves.emer_cool.open() else self.valves.emer_cool.close() end
|
if self.em_cool_opened then self.valves.emer_cool.open() else self.valves.emer_cool.close() end
|
||||||
|
|
||||||
|
-----------------------
|
||||||
|
-- Auxiliary Coolant --
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
if self.aux_coolant then
|
||||||
|
if self.enable_aux_cool and (not self.aux_cool_opened) then
|
||||||
|
log.info(util.c("UNIT ", self.r_id, " auxiliary coolant valve opened"))
|
||||||
|
self.aux_cool_opened = true
|
||||||
|
elseif (not self.enable_aux_cool) and self.aux_cool_opened then
|
||||||
|
log.info(util.c("UNIT ", self.r_id, " auxiliary coolant valve closed"))
|
||||||
|
self.aux_cool_opened = false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set valve state always
|
||||||
|
if self.aux_cool_opened then self.valves.aux_cool.open() else self.valves.aux_cool.close() end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return logic
|
return logic
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user