#232 completed display of flow/dynamic tank/sps, dynamically sized

This commit is contained in:
Mikayla Fischler
2023-08-19 13:38:05 -04:00
parent ce780c3d72
commit d17e2b8321
10 changed files with 387 additions and 126 deletions

View File

@ -243,12 +243,13 @@ end
---@nodiscard ---@nodiscard
---@param version string coordinator version ---@param version string coordinator version
---@param nic nic network interface device ---@param nic nic network interface device
---@param num_units integer number of configured units for number of monitors, checked against SV
---@param crd_channel integer port of configured supervisor ---@param crd_channel integer port of configured supervisor
---@param svr_channel integer listening port for supervisor replys ---@param svr_channel integer listening port for supervisor replys
---@param pkt_channel integer listening port for pocket API ---@param pkt_channel integer listening port for pocket API
---@param range integer trusted device connection range ---@param range integer trusted device connection range
---@param sv_watchdog watchdog ---@param sv_watchdog watchdog
function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel, range, sv_watchdog) function coordinator.comms(version, nic, num_units, crd_channel, svr_channel, pkt_channel, range, sv_watchdog)
local self = { local self = {
sv_linked = false, sv_linked = false,
sv_addr = comms.BROADCAST, sv_addr = comms.BROADCAST,
@ -707,21 +708,16 @@ function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel,
-- reset to disconnected before validating -- reset to disconnected before validating
iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED) iocontrol.fp_link_state(types.PANEL_LINK_STATE.DISCONNECTED)
if type(config) == "table" and #config > 1 then if type(config) == "table" and #config == 2 then
-- get configuration -- get configuration
---@class facility_conf ---@class facility_conf
local conf = { local conf = {
num_units = config[1], ---@type integer num_units = config[1], ---@type integer
defs = {} -- boilers and turbines cooling = config[2] ---@type sv_cooling_conf
} }
if (#config - 1) == (conf.num_units * 2) then if conf.num_units == num_units then
-- record sequence of pairs of [#boilers, #turbines] per unit
for i = 2, #config do
table.insert(conf.defs, config[i])
end
-- init io controller -- init io controller
iocontrol.init(conf, public) iocontrol.init(conf, public)
@ -733,7 +729,7 @@ function coordinator.comms(version, nic, crd_channel, svr_channel, pkt_channel,
iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED) iocontrol.fp_link_state(types.PANEL_LINK_STATE.LINKED)
else else
self.sv_config_err = true self.sv_config_err = true
log.warning("invalid supervisor configuration definitions received, establish failed") log.warning("supervisor config's number of units don't match coordinator's config, establish failed")
end end
else else
log.debug("invalid supervisor configuration table received, establish failed") log.debug("invalid supervisor configuration table received, establish failed")

View File

@ -38,9 +38,7 @@ local function __generic_ack(success) end
---@param comms_v string comms version ---@param comms_v string comms version
function iocontrol.init_fp(firmware_v, comms_v) function iocontrol.init_fp(firmware_v, comms_v)
---@class ioctl_front_panel ---@class ioctl_front_panel
io.fp = { io.fp = { ps = psil.create() }
ps = psil.create()
}
io.fp.ps.publish("version", firmware_v) io.fp.ps.publish("version", firmware_v)
io.fp.ps.publish("comms_version", comms_v) io.fp.ps.publish("comms_version", comms_v)
@ -52,7 +50,9 @@ end
function iocontrol.init(conf, comms) function iocontrol.init(conf, comms)
---@class ioctl_facility ---@class ioctl_facility
io.facility = { io.facility = {
num_units = conf.num_units, ---@type integer num_units = conf.num_units,
tank_mode = conf.cooling.fac_tank_mode,
tank_defs = conf.cooling.fac_tank_list,
all_sys_ok = false, all_sys_ok = false,
rtu_count = 0, rtu_count = 0,
@ -116,6 +116,7 @@ function iocontrol.init(conf, comms)
num_boilers = 0, num_boilers = 0,
num_turbines = 0, num_turbines = 0,
num_snas = 0, num_snas = 0,
has_tank = conf.cooling.r_cool[i].TANK,
control_state = false, control_state = false,
burn_rate_cmd = 0.0, burn_rate_cmd = 0.0,
@ -191,14 +192,14 @@ function iocontrol.init(conf, comms)
} }
-- create boiler tables -- create boiler tables
for _ = 1, conf.defs[(i * 2) - 1] do for _ = 1, conf.cooling.r_cool[i].BOILERS do
local data = {} ---@type boilerv_session_db local data = {} ---@type boilerv_session_db
table.insert(entry.boiler_ps_tbl, psil.create()) table.insert(entry.boiler_ps_tbl, psil.create())
table.insert(entry.boiler_data_tbl, data) table.insert(entry.boiler_data_tbl, data)
end end
-- create turbine tables -- create turbine tables
for _ = 1, conf.defs[i * 2] do for _ = 1, conf.cooling.r_cool[i].TURBINES do
local data = {} ---@type turbinev_session_db local data = {} ---@type turbinev_session_db
table.insert(entry.turbine_ps_tbl, psil.create()) table.insert(entry.turbine_ps_tbl, psil.create())
table.insert(entry.turbine_data_tbl, data) table.insert(entry.turbine_data_tbl, data)
@ -210,6 +211,18 @@ function iocontrol.init(conf, comms)
table.insert(io.units, entry) table.insert(io.units, entry)
end end
-- on facility tank mode 0, setup tank list to match unit TANK option
if io.facility.tank_mode == 0 then
for i = 1, #io.units do
io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TANK, 1, 0)
end
-- on other facility modes, overwrite unit TANK option with facility tank list
else
for i = 1, #io.units do
io.units[i].has_tank = conf.cooling.fac_tank_list[i] > 0
end
end
-- pass IO control here since it can't be require'd due to a require loop -- pass IO control here since it can't be require'd due to a require loop
process.init(io, comms) process.init(io, comms)
end end

View File

@ -162,8 +162,8 @@ local function main()
-- create network interface then setup comms -- create network interface then setup comms
local nic = network.nic(modem) local nic = network.nic(modem)
local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.CRD_CHANNEL, config.SVR_CHANNEL, local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, config.NUM_UNITS, config.CRD_CHANNEL,
config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog) config.SVR_CHANNEL, config.PKT_CHANNEL, config.TRUSTED_RANGE, conn_watchdog)
log.debug("startup> comms init") log.debug("startup> comms init")
log_comms("comms initialized") log_comms("comms initialized")

View File

@ -38,8 +38,9 @@ local ind_wht = style.ind_wht
---@param parent graphics_element parent ---@param parent graphics_element parent
---@param x integer top left x ---@param x integer top left x
---@param y integer top left y ---@param y integer top left y
---@param wide boolean whether to render wide version
---@param unit ioctl_unit unit database entry ---@param unit ioctl_unit unit database entry
local function make(parent, x, y, unit) local function make(parent, x, y, wide, unit)
local height = 16 local height = 16
local v_start = 1 + ((unit.unit_id - 1) * 4) local v_start = 1 + ((unit.unit_id - 1) * 4)
@ -56,8 +57,10 @@ local function make(parent, x, y, unit)
assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height()) assert(parent.get_height() >= (y + height), "flow display not of sufficient vertical resolution (add an additional row of monitors) " .. y .. "," .. parent.get_height())
local function _wide(a, b) return util.trinary(wide, a, b) end
-- bounding box div -- bounding box div
local root = Div{parent=parent,x=x,y=y,width=114,height=height} local root = Div{parent=parent,x=x,y=y,width=_wide(136, 114),height=height}
local lg_gray = cpair(colors.lightGray, colors.gray) local lg_gray = cpair(colors.lightGray, colors.gray)
local wh_gray = cpair(colors.white, colors.gray) local wh_gray = cpair(colors.white, colors.gray)
@ -70,46 +73,62 @@ local function make(parent, x, y, unit)
TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=reactor,y=1,text="FISSION REACTOR",alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=reactor,y=3,text="UNIT #"..unit.unit_id,alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} TextBox{parent=root,x=19,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
TextBox{parent=root,x=4,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray} TextBox{parent=root,x=3,y=5,text="\x19",width=1,height=1,fg_bg=lg_gray}
local rc_pipes = {} local rc_pipes = {}
table.insert(rc_pipes, pipe(0, 1, 19, 1, colors.lightBlue, true)) local emc_x = 42 -- emergency coolant connection x point
table.insert(rc_pipes, pipe(0, 3, 19, 3, colors.orange, true))
table.insert(rc_pipes, pipe(39, 1, 58, 1, colors.blue, true))
table.insert(rc_pipes, pipe(39, 3, 58, 3, colors.white, true))
table.insert(rc_pipes, pipe(78, 0, 83, 0, colors.white, true)) if unit.num_boilers > 0 then
table.insert(rc_pipes, pipe(78, 2, 83, 2, colors.white, true)) table.insert(rc_pipes, pipe(0, 1, _wide(28, 19), 1, colors.lightBlue, true))
table.insert(rc_pipes, pipe(78, 4, 83, 4, colors.white, 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), 3, _wide(72,58), 3, colors.white, true))
else
emc_x = 3
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))
end
if unit.has_tank then
table.insert(rc_pipes, pipe(emc_x, 1, emc_x, 0, colors.blue, true, true))
end
local prv_yo = math.max(3 - unit.num_turbines, 0)
for i = 1, unit.num_turbines do
local py = 2 * (i - 1) + prv_yo
table.insert(rc_pipes, pipe(_wide(92, 78), py, _wide(104, 83), py, colors.white, true))
end
PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray} PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray}
local hc_rate = DataIndicator{parent=root,x=22,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} if unit.num_boilers > 0 then
local cc_rate = DataIndicator{parent=root,x=22,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
local boiler = Rectangle{parent=root,x=40,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray}
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1} TextBox{parent=boiler,y=3,text="BOILERS",alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=root,x=40,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
TextBox{parent=root,x=58,y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray} TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
local wt_rate = DataIndicator{parent=root,x=61,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
local st_rate = DataIndicator{parent=root,x=61,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg} local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
else
local turbine = Rectangle{parent=root,x=79,y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray} local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1} local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=287000000,commas=true,width=16,fg_bg=bw_fg_bg}
TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=root,x=79,y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray}
local function _relief(rx, ry, name)
TextBox{parent=root,x=rx,y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1}
local conn = TriIndicatorLight{parent=root,x=rx+4,y=ry,label=name,c1=colors.gray,c2=colors.yellow,c3=colors.red}
end end
_relief(103, 1, v_names[5]) local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray}
_relief(103, 3, v_names[6]) TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=TEXT_ALIGN.CENTER,height=1}
_relief(103, 5, v_names[7]) TextBox{parent=turbine,y=3,text="GENERATORS",alignment=TEXT_ALIGN.CENTER,height=1}
TextBox{parent=root,x=_wide(93,79),y=2,text="\x1a \x80 \x1b",width=1,height=3,fg_bg=lg_gray}
for i = 1, unit.num_turbines do
local ry = 1 + (2 * (i - 1)) + prv_yo
TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1}
local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=colors.gray,c2=colors.yellow,c3=colors.red}
end
---------------------- ----------------------
-- WASTE PROCESSING -- -- WASTE PROCESSING --
@ -118,21 +137,24 @@ local function make(parent, x, y, unit)
local waste = Div{parent=root,x=3,y=6} local waste = Div{parent=root,x=3,y=6}
local waste_pipes = { local waste_pipes = {
pipe(0, 0, 16, 1, colors.brown, true), pipe(0, 0, _wide(19, 16), 1, colors.brown, true),
pipe(12, 1, 16, 5, colors.brown, true), pipe(_wide(14, 13), 1, _wide(19, 17), 5, colors.brown, true),
pipe(18, 1, 44, 1, colors.brown, true), pipe(_wide(22, 19), 1, _wide(49, 45), 1, colors.brown, true),
pipe(18, 5, 23, 5, colors.brown, true), pipe(_wide(22, 19), 5, _wide(28, 24), 5, colors.brown, true),
pipe(52, 1, 80, 1, colors.green, true),
pipe(42, 4, 60, 4, colors.cyan, true), pipe(_wide(64, 53), 1, _wide(95, 81), 1, colors.green, true),
pipe(56, 4, 60, 8, colors.cyan, true),
pipe(62, 4, 80, 4, colors.cyan, true), pipe(_wide(48, 43), 4, _wide(71, 61), 4, colors.cyan, true),
pipe(62, 8, 110, 8, colors.cyan, true), pipe(_wide(66, 57), 4, _wide(71, 61), 8, colors.cyan, true),
pipe(93, 1, 94, 3, colors.black, true, true), pipe(_wide(74, 63), 4, _wide(95, 81), 4, colors.cyan, true),
pipe(93, 4, 109, 6, colors.black, true, true), pipe(_wide(74, 63), 8, _wide(133, 111), 8, colors.cyan, true),
pipe(109, 6, 107, 6, colors.black, true, true)
pipe(_wide(108, 94), 1, _wide(132, 110), 6, colors.black, true, true),
pipe(_wide(108, 94), 4, _wide(111, 95), 1, colors.black, true, true),
pipe(_wide(132, 110), 6, _wide(130, 108), 6, colors.black, true, true)
} }
PipeNetwork{parent=waste,x=2,y=1,pipes=waste_pipes,bg=colors.lightGray} PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=colors.lightGray}
local function _valve(vx, vy, n) local function _valve(vx, vy, n)
TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1} TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1}
@ -142,29 +164,29 @@ local function make(parent, x, y, unit)
local function _machine(mx, my, name) local function _machine(mx, my, name)
local l = string.len(name) + 2 local l = string.len(name) + 2
TextBox{parent=waste,x=mx,y=my,text=util.strrep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1} TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=TEXT_ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1}
TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1} TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=TEXT_ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1}
end end
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=bw_fg_bg} local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=1234.56,width=12,fg_bg=bw_fg_bg}
local pu_rate = DataIndicator{parent=waste,x=70,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg}
local po_rate = DataIndicator{parent=waste,x=45,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg}
local popl_rate = DataIndicator{parent=waste,x=70,y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg}
local poam_rate = DataIndicator{parent=waste,x=70,y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg}
local spent_rate = DataIndicator{parent=waste,x=99,y=4,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg} local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=123.456,width=12,fg_bg=bw_fg_bg}
_valve(18, 2, 1) _valve(_wide(21, 18), 2, 1)
_valve(18, 6, 2) _valve(_wide(21, 18), 6, 2)
_valve(62, 5, 3) _valve(_wide(73, 62), 5, 3)
_valve(62, 9, 4) _valve(_wide(73, 62), 9, 4)
_machine(45, 1, "CENTRIFUGE \x1a"); _machine(_wide(51, 45), 1, "CENTRIFUGE \x1a");
_machine(83, 1, "PRC [Pu] \x1a"); _machine(_wide(97, 83), 1, "PRC [Pu] \x1a");
_machine(83, 4, "PRC [Po] \x1a"); _machine(_wide(97, 83), 4, "PRC [Po] \x1a");
_machine(94, 6, "SPENT WASTE \x1b") _machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
TextBox{parent=waste,x=25,y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray} TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=TEXT_ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
local sna_po = Rectangle{parent=waste,x=25,y=4,border=border(1, colors.gray, true),width=19,height=7,thin=true,fg_bg=bw_fg_bg} 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=bw_fg_bg}
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,label="CNT",unit="",format="%2d",value=99,width=7} local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=99,width=7}
local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17} local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=1000,width=17}

View File

@ -38,6 +38,9 @@ local function init(main)
local facility = iocontrol.get_db().facility local facility = iocontrol.get_db().facility
local units = iocontrol.get_db().units local units = iocontrol.get_db().units
local tank_defs = facility.tank_defs
local tank_draw = { table.unpack(tank_defs) }
-- window header message -- window header message
local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header} local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
-- max length example: "01:23:45 AM - Wednesday, September 28 2022" -- max length example: "01:23:45 AM - Wednesday, September 28 2022"
@ -46,52 +49,266 @@ local function init(main)
datetime.register(facility.ps, "date_time", datetime.set_value) datetime.register(facility.ps, "date_time", datetime.set_value)
local po_pipes = {} local po_pipes = {}
local water_pipes = {} local water_pipes = {}
local fac_tanks = true -- get the y offset for this unit index
local function y_ofs(idx) return ((idx - 1) * 20) end
for i = 1, 4 do local function calc_fdef(start_idx, end_idx)
local y = ((i - 1) * 20) local first, last = 4, 0
table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true)) for i = start_idx, end_idx do
table.insert(water_pipes, pipe(2, y, 82, y, colors.blue, true)) if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(82, y, 82, y + 2, colors.blue, true)) last = i
if fac_tanks and i > 1 then table.insert(water_pipes, pipe(21, y - 19, 21, y, colors.blue, true)) end if i < first then first = i end
end
end
return first, last
end end
if facility.tank_mode == 0 or facility.tank_mode == 8 then
-- (0) tanks belong to reactor units OR (8) 4 total facility tanks (A B C D)
for i = 1, facility.num_units do
if units[i].has_tank then
local y = y_ofs(i)
table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true))
table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true))
local u = units[i] ---@type ioctl_unit
local x = util.trinary(u.num_boilers == 0, 45, 84)
table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true))
end
end
else
-- setup connections for units with emergency coolant, always the same
for i = 1, #tank_defs do
if tank_defs[i] > 0 then
local y = y_ofs(i)
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(1, y, 21, y, colors.blue, true))
else
table.insert(water_pipes, pipe(2, y, 2, y + 5, colors.blue, true))
table.insert(water_pipes, pipe(2, y, 21, y, colors.blue, true))
end
local u = units[i] ---@type ioctl_unit
local x = util.trinary(u.num_boilers == 0, 45, 84)
table.insert(water_pipes, pipe(21, y, x, y + 2, colors.blue, true, true))
end
end
if facility.tank_mode == 1 then
-- (1) 1 total facility tank (A A A A)
local first_fdef, last_fdef = calc_fdef(1, #tank_defs)
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
elseif facility.tank_mode == 2 then
-- (2) 2 total facility tanks (A A A B)
local first_fdef, last_fdef = calc_fdef(1, math.min(3, #tank_defs))
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == 4 then
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
end
elseif i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
elseif facility.tank_mode == 3 then
-- (3) 2 total facility tanks (A A B B)
for _, a in pairs({ 1, 3 }) do
local b = a + 1
if tank_defs[a] == 2 then
table.insert(water_pipes, pipe(0, y_ofs(a), 1, y_ofs(a) + 6, colors.blue, true))
if tank_defs[b] == 2 then
table.insert(water_pipes, pipe(0, y_ofs(b) - 13, 1, y_ofs(b), colors.blue, true))
tank_draw[b] = 0
end
elseif tank_defs[b] == 2 then
table.insert(water_pipes, pipe(0, y_ofs(b), 1, y_ofs(b) + 6, colors.blue, true))
end
end
elseif facility.tank_mode == 4 then
-- (4) 2 total facility tanks (A B B B)
local first_fdef, last_fdef = calc_fdef(2, #tank_defs)
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == 1 then
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
end
elseif i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
elseif facility.tank_mode == 5 then
-- (5) 3 total facility tanks (A A B C)
local first_fdef, last_fdef = calc_fdef(1, math.min(2, #tank_defs))
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == 3 or i == 4 then
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
end
elseif i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
elseif facility.tank_mode == 6 then
-- (6) 3 total facility tanks (A B B C)
local first_fdef, last_fdef = calc_fdef(2, math.min(3, #tank_defs))
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == 1 or i == 4 then
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
end
elseif i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
elseif facility.tank_mode == 7 then
-- (7) 3 total facility tanks (A B C C)
local first_fdef, last_fdef = calc_fdef(3, #tank_defs)
for i = 1, #tank_defs do
local y = y_ofs(i)
if i == 1 or i == 2 then
if tank_defs[i] == 2 then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
end
elseif i == first_fdef then
table.insert(water_pipes, pipe(0, y, 1, y + 6, colors.blue, true))
elseif i > first_fdef then
if tank_defs[i] == 2 then tank_draw[i] = 0 end
if i == last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y, colors.blue, true))
elseif i < last_fdef then
table.insert(water_pipes, pipe(0, y - 13, 0, y + 6, colors.blue, true))
end
end
end
end
end
local flow_x = 3
if #water_pipes > 0 then
flow_x = 25
PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray} PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray}
end
for i = 1, facility.num_units do for i = 1, facility.num_units do
local y_offset = ((i - 1) * 20) local y_offset = y_ofs(i)
unit_flow(main, 25, 5 + y_offset, units[i]) unit_flow(main, flow_x, 5 + y_offset, #water_pipes == 0, units[i])
table.insert(po_pipes, pipe(0, 3 + y_offset, 8, 0, colors.cyan, true, true)) table.insert(po_pipes, pipe(0, 3 + y_offset, 8, 0, colors.cyan, true, true))
local vx, vy = 11, 3 + y_offset
TextBox{parent=main,x=vx,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1}
local conn = IndicatorLight{parent=main,x=vx-3,y=vy+1,label=util.sprintf("PV%02d", i + 13),colors=cpair(colors.green,colors.gray)}
local state = IndicatorLight{parent=main,x=vx-3,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)}
local tank = Div{parent=main,x=2,y=8+y_offset,width=20,height=12}
TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)}
TextBox{parent=tank,text="DYNAMIC TANK "..i,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)}
local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10}
local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14}
TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label}
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg}
TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label}
local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
end end
PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray} PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray}
-- TANK VALVES --
local next_f_id = 1
for i = 1, #tank_defs do
if tank_defs[i] > 0 then
local vy = 3 + y_ofs(i)
TextBox{parent=main,x=12,y=vy,text="\x10\x11",fg_bg=cpair(colors.black,colors.lightGray),width=2,height=1}
local conn = IndicatorLight{parent=main,x=9,y=vy+1,label=util.sprintf("PV%02d-EMC", i + 13),colors=cpair(colors.green,colors.gray)}
local state = IndicatorLight{parent=main,x=9,y=vy+2,label="STATE",colors=cpair(colors.white,colors.white)}
end
end
-- DYNAMIC TANKS --
for i = 1, #tank_draw do
if tank_draw[i] > 0 then
local id = "U-" .. i
if tank_draw[i] == 2 then
id = "F-" .. next_f_id
next_f_id = next_f_id + 1
end
local y_offset = y_ofs(i)
local tank = Div{parent=main,x=3,y=8+y_offset,width=20,height=12}
TextBox{parent=tank,text=" ",height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)}
TextBox{parent=tank,text="DYNAMIC TANK "..id,alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=cpair(colors.white,colors.gray)}
local tank_box = Rectangle{parent=tank,border=border(1, colors.gray, true),width=20,height=10}
local status = StateIndicator{parent=tank_box,x=3,y=1,states=style.dtank.states,value=1,min_width=14}
TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label}
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg}
TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label}
local ccool = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
end
end
-- SPS --
local sps = Div{parent=main,x=140,y=3,height=12} local sps = Div{parent=main,x=140,y=3,height=12}
TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)} TextBox{parent=sps,text=" ",width=24,height=1,x=1,y=1,fg_bg=cpair(colors.lightGray,colors.gray)}
TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=cpair(colors.white,colors.gray)} TextBox{parent=sps,text="SPS",alignment=TEXT_ALIGN.CENTER,width=24,height=1,fg_bg=cpair(colors.white,colors.gray)}
local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10} local sps_box = Rectangle{parent=sps,border=border(1, colors.gray, true),width=24,height=10}
local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14} local status = StateIndicator{parent=sps_box,x=5,y=1,states=style.sps.states,value=1,min_width=14}
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label} TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label}
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg}
TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label} TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label}
local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg} local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg}
end end

View File

@ -10,27 +10,39 @@ config.RTU_CHANNEL = 16242
config.CRD_CHANNEL = 16243 config.CRD_CHANNEL = 16243
-- pocket comms channel -- pocket comms channel
config.PKT_CHANNEL = 16244 config.PKT_CHANNEL = 16244
-- max trusted modem message distance (0 to disable check) -- max trusted modem message distance
-- (0 to disable check)
config.TRUSTED_RANGE = 0 config.TRUSTED_RANGE = 0
-- time in seconds (>= 2) before assuming a remote device is no longer active -- time in seconds (>= 2) before assuming a remote
-- device is no longer active
config.PLC_TIMEOUT = 5 config.PLC_TIMEOUT = 5
config.RTU_TIMEOUT = 5 config.RTU_TIMEOUT = 5
config.CRD_TIMEOUT = 5 config.CRD_TIMEOUT = 5
config.PKT_TIMEOUT = 5 config.PKT_TIMEOUT = 5
-- facility authentication key (do NOT use one of your passwords) -- facility authentication key
-- (do NOT use one of your passwords)
-- this enables verifying that messages are authentic -- this enables verifying that messages are authentic
-- all devices on the same network must use the same key -- all devices on this network must use this key
-- config.AUTH_KEY = "SCADAfacility123" -- config.AUTH_KEY = "SCADAfacility123"
-- expected number of reactors -- expected number of reactors
config.NUM_REACTORS = 4 config.NUM_REACTORS = 4
-- expected number of boilers/turbines for each reactor -- expected number of devices for each unit
config.REACTOR_COOLING = { config.REACTOR_COOLING = {
{ BOILERS = 1, TURBINES = 1 }, -- reactor unit 1 -- reactor unit 1
{ BOILERS = 1, TURBINES = 1 }, -- reactor unit 2 { BOILERS = 1, TURBINES = 1, TANK = false },
{ BOILERS = 1, TURBINES = 1 }, -- reactor unit 3 -- reactor unit 2
{ BOILERS = 1, TURBINES = 1 } -- reactor unit 4 { BOILERS = 1, TURBINES = 1, TANK = false },
-- reactor unit 3
{ BOILERS = 1, TURBINES = 1, TANK = false },
-- reactor unit 4
{ BOILERS = 1, TURBINES = 1, TANK = false }
} }
-- advanced facility dynamic tank configuration
-- (see wiki for details)
-- by default, dynamic tanks are for each unit
config.FAC_TANK_MODE = 0
config.FAC_TANK_LIST = { 0, 0, 0, 0 }
-- log path -- log path
config.LOG_PATH = "/log.txt" config.LOG_PATH = "/log.txt"

View File

@ -54,7 +54,7 @@ local facility = {}
-- create a new facility management object -- create a new facility management object
---@nodiscard ---@nodiscard
---@param num_reactors integer number of reactor units ---@param num_reactors integer number of reactor units
---@param cooling_conf table cooling configurations of reactor units ---@param cooling_conf sv_cooling_conf cooling configurations of reactor units
function facility.new(num_reactors, cooling_conf) function facility.new(num_reactors, cooling_conf)
local self = { local self = {
units = {}, units = {},
@ -118,7 +118,7 @@ function facility.new(num_reactors, cooling_conf)
-- create units -- create units
for i = 1, num_reactors do for i = 1, num_reactors do
table.insert(self.units, unit.new(i, cooling_conf[i].BOILERS, cooling_conf[i].TURBINES)) table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BOILERS, cooling_conf.r_cool[i].TURBINES))
table.insert(self.group_map, 0) table.insert(self.group_map, 0)
end end

View File

@ -198,7 +198,7 @@ end
---@param nic nic network interface device ---@param nic nic network interface device
---@param fp_ok boolean front panel active ---@param fp_ok boolean front panel active
---@param num_reactors integer number of reactors ---@param num_reactors integer number of reactors
---@param cooling_conf table cooling configuration definition ---@param cooling_conf sv_cooling_conf cooling configuration definition
function svsessions.init(nic, fp_ok, num_reactors, cooling_conf) function svsessions.init(nic, fp_ok, num_reactors, cooling_conf)
self.nic = nic self.nic = nic
self.fp_ok = fp_ok self.fp_ok = fp_ok

View File

@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v0.20.5" local SUPERVISOR_VERSION = "v1.0.0"
local println = util.println local println = util.println
local println_ts = util.println_ts local println_ts = util.println_ts
@ -48,11 +48,16 @@ cfv.assert_type_num(config.PKT_TIMEOUT)
cfv.assert_min(config.PKT_TIMEOUT, 2) cfv.assert_min(config.PKT_TIMEOUT, 2)
cfv.assert_type_int(config.NUM_REACTORS) cfv.assert_type_int(config.NUM_REACTORS)
cfv.assert_type_table(config.REACTOR_COOLING) cfv.assert_type_table(config.REACTOR_COOLING)
cfv.assert_type_int(config.FAC_TANK_MODE)
cfv.assert_type_table(config.FAC_TANK_LIST)
cfv.assert_type_str(config.LOG_PATH) cfv.assert_type_str(config.LOG_PATH)
cfv.assert_type_int(config.LOG_MODE) cfv.assert_type_int(config.LOG_MODE)
assert(cfv.valid(), "bad config file: missing/invalid fields") assert(cfv.valid(), "bad config file: missing/invalid fields")
assert((config.FAC_TANK_MODE ~= 0) and (config.NUM_REACTORS == #config.FAC_TANK_LIST),
"bad config file: FAC_TANK_LIST length not equal to NUM_REACTORS")
cfv.assert_eq(#config.REACTOR_COOLING, config.NUM_REACTORS) cfv.assert_eq(#config.REACTOR_COOLING, config.NUM_REACTORS)
assert(cfv.valid(), "config: number of cooling configs different than number of units") assert(cfv.valid(), "config: number of cooling configs different than number of units")
@ -61,6 +66,7 @@ for i = 1, config.NUM_REACTORS do
assert(cfv.valid(), "config: missing cooling entry for reactor " .. i) assert(cfv.valid(), "config: missing cooling entry for reactor " .. i)
cfv.assert_type_int(config.REACTOR_COOLING[i].BOILERS) cfv.assert_type_int(config.REACTOR_COOLING[i].BOILERS)
cfv.assert_type_int(config.REACTOR_COOLING[i].TURBINES) cfv.assert_type_int(config.REACTOR_COOLING[i].TURBINES)
cfv.assert_type_bool(config.REACTOR_COOLING[i].TANK)
assert(cfv.valid(), "config: missing boilers/turbines for reactor " .. i) assert(cfv.valid(), "config: missing boilers/turbines for reactor " .. i)
cfv.assert_min(config.REACTOR_COOLING[i].BOILERS, 0) cfv.assert_min(config.REACTOR_COOLING[i].BOILERS, 0)
cfv.assert_min(config.REACTOR_COOLING[i].TURBINES, 1) cfv.assert_min(config.REACTOR_COOLING[i].TURBINES, 1)

View File

@ -32,7 +32,8 @@ function supervisor.comms(_version, nic, fp_ok)
-- configuration data -- configuration data
local num_reactors = config.NUM_REACTORS local num_reactors = config.NUM_REACTORS
local cooling_conf = config.REACTOR_COOLING ---@class sv_cooling_conf
local cooling_conf = { r_cool = config.REACTOR_COOLING, fac_tank_mode = config.FAC_TANK_MODE, fac_tank_list = config.FAC_TANK_LIST }
local self = { local self = {
last_est_acks = {} last_est_acks = {}
@ -295,16 +296,10 @@ function supervisor.comms(_version, nic, fp_ok)
local s_id = svsessions.establish_crd_session(src_addr, firmware_v) local s_id = svsessions.establish_crd_session(src_addr, firmware_v)
if s_id ~= false then if s_id ~= false then
local cfg = { num_reactors }
for i = 1, #cooling_conf do
table.insert(cfg, cooling_conf[i].BOILERS)
table.insert(cfg, cooling_conf[i].TURBINES)
end
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected")) println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id)) log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, cfg) _send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { num_reactors, cooling_conf })
else else
if last_ack ~= ESTABLISH_ACK.COLLISION then if last_ack ~= ESTABLISH_ACK.COLLISION then
log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator") log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator")