mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
Merge pull request #482 from MikaylaFischler/412-additional-matrix-integrations
412 additional matrix integrations
This commit is contained in:
commit
e91fd2fcaa
@ -92,6 +92,7 @@ function iocontrol.init(conf, comms, temp_scale)
|
|||||||
---@type WASTE_PRODUCT
|
---@type WASTE_PRODUCT
|
||||||
auto_current_waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
auto_current_waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
auto_pu_fallback_active = false,
|
auto_pu_fallback_active = false,
|
||||||
|
auto_sps_disabled = false,
|
||||||
|
|
||||||
radiation = types.new_zero_radiation_reading(),
|
radiation = types.new_zero_radiation_reading(),
|
||||||
|
|
||||||
@ -593,7 +594,7 @@ function iocontrol.update_facility_status(status)
|
|||||||
|
|
||||||
local ctl_status = status[1]
|
local ctl_status = status[1]
|
||||||
|
|
||||||
if type(ctl_status) == "table" and #ctl_status == 16 then
|
if type(ctl_status) == "table" and #ctl_status == 17 then
|
||||||
fac.all_sys_ok = ctl_status[1]
|
fac.all_sys_ok = ctl_status[1]
|
||||||
fac.auto_ready = ctl_status[2]
|
fac.auto_ready = ctl_status[2]
|
||||||
|
|
||||||
@ -644,9 +645,11 @@ function iocontrol.update_facility_status(status)
|
|||||||
|
|
||||||
fac.auto_current_waste_product = ctl_status[15]
|
fac.auto_current_waste_product = ctl_status[15]
|
||||||
fac.auto_pu_fallback_active = ctl_status[16]
|
fac.auto_pu_fallback_active = ctl_status[16]
|
||||||
|
fac.auto_sps_disabled = ctl_status[17]
|
||||||
|
|
||||||
fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
|
fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
|
||||||
fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
|
fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
|
||||||
|
fac.ps.publish("sps_disabled_low_power", fac.auto_sps_disabled)
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "control status not a table or length mismatch")
|
log.debug(log_header .. "control status not a table or length mismatch")
|
||||||
valid = false
|
valid = false
|
||||||
@ -663,10 +666,26 @@ function iocontrol.update_facility_status(status)
|
|||||||
fac.rtu_count = rtu_statuses.count
|
fac.rtu_count = rtu_statuses.count
|
||||||
|
|
||||||
-- power statistics
|
-- power statistics
|
||||||
if type(rtu_statuses.power) == "table" then
|
if type(rtu_statuses.power) == "table" and #rtu_statuses.power == 4 then
|
||||||
fac.induction_ps_tbl[1].publish("avg_charge", rtu_statuses.power[1])
|
local data = fac.induction_data_tbl[1] ---@type imatrix_session_db
|
||||||
fac.induction_ps_tbl[1].publish("avg_inflow", rtu_statuses.power[2])
|
local ps = fac.induction_ps_tbl[1] ---@type psil
|
||||||
fac.induction_ps_tbl[1].publish("avg_outflow", rtu_statuses.power[3])
|
|
||||||
|
local chg = tonumber(rtu_statuses.power[1])
|
||||||
|
local in_f = tonumber(rtu_statuses.power[2])
|
||||||
|
local out_f = tonumber(rtu_statuses.power[3])
|
||||||
|
local eta = tonumber(rtu_statuses.power[4])
|
||||||
|
|
||||||
|
ps.publish("avg_charge", chg)
|
||||||
|
ps.publish("avg_inflow", in_f)
|
||||||
|
ps.publish("avg_outflow", out_f)
|
||||||
|
ps.publish("eta_ms", eta)
|
||||||
|
|
||||||
|
ps.publish("is_charging", in_f > out_f)
|
||||||
|
ps.publish("is_discharging", out_f > in_f)
|
||||||
|
|
||||||
|
if data and data.build then
|
||||||
|
ps.publish("at_max_io", in_f >= data.build.transfer_cap or out_f >= data.build.transfer_cap)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "power statistics list not a table")
|
log.debug(log_header .. "power statistics list not a table")
|
||||||
valid = false
|
valid = false
|
||||||
|
@ -29,7 +29,8 @@ local self = {
|
|||||||
gen_target = 0.0,
|
gen_target = 0.0,
|
||||||
limits = {},
|
limits = {},
|
||||||
waste_product = PRODUCT.PLUTONIUM,
|
waste_product = PRODUCT.PLUTONIUM,
|
||||||
pu_fallback = false
|
pu_fallback = false,
|
||||||
|
sps_low_power = false
|
||||||
},
|
},
|
||||||
waste_modes = {},
|
waste_modes = {},
|
||||||
priority_groups = {}
|
priority_groups = {}
|
||||||
@ -65,6 +66,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
ctl_proc.limits = config.limits
|
ctl_proc.limits = config.limits
|
||||||
ctl_proc.waste_product = config.waste_product
|
ctl_proc.waste_product = config.waste_product
|
||||||
ctl_proc.pu_fallback = config.pu_fallback
|
ctl_proc.pu_fallback = config.pu_fallback
|
||||||
|
ctl_proc.sps_low_power = config.sps_low_power
|
||||||
|
|
||||||
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
||||||
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
||||||
@ -72,6 +74,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
||||||
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
||||||
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
||||||
|
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
|
||||||
|
|
||||||
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do
|
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do
|
||||||
local unit = self.io.units[id] ---@type ioctl_unit
|
local unit = self.io.units[id] ---@type ioctl_unit
|
||||||
@ -83,6 +86,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
-- notify supervisor of auto waste config
|
-- notify supervisor of auto waste config
|
||||||
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product)
|
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product)
|
||||||
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback)
|
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback)
|
||||||
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, ctl_proc.sps_low_power)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- unit waste states
|
-- unit waste states
|
||||||
@ -259,6 +263,18 @@ function process.set_pu_fallback(enabled)
|
|||||||
_write_auto_config()
|
_write_auto_config()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set automatic process control SPS usage at low power
|
||||||
|
---@param enabled boolean whether to enable SPS usage at low power
|
||||||
|
function process.set_sps_low_power(enabled)
|
||||||
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, enabled)
|
||||||
|
|
||||||
|
log.debug(util.c("PROCESS: SET SPS LOW POWER ", enabled))
|
||||||
|
|
||||||
|
-- update config table and save
|
||||||
|
self.control_states.process.sps_low_power = enabled
|
||||||
|
_write_auto_config()
|
||||||
|
end
|
||||||
|
|
||||||
-- save process control settings
|
-- save process control settings
|
||||||
---@param mode PROCESS control mode
|
---@param mode PROCESS control mode
|
||||||
---@param burn_target number burn rate target
|
---@param burn_target number burn rate target
|
||||||
|
@ -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.4.3"
|
local COORDINATOR_VERSION = "v1.4.4"
|
||||||
|
|
||||||
local CHUNK_LOAD_DELAY_S = 30.0
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ local Rectangle = require("graphics.elements.rectangle")
|
|||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
local DataIndicator = require("graphics.elements.indicators.data")
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local IndicatorLight = require("graphics.elements.indicators.light")
|
||||||
local PowerIndicator = require("graphics.elements.indicators.power")
|
local PowerIndicator = require("graphics.elements.indicators.power")
|
||||||
local StateIndicator = require("graphics.elements.indicators.state")
|
local StateIndicator = require("graphics.elements.indicators.state")
|
||||||
local VerticalBar = require("graphics.elements.indicators.vbar")
|
local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
@ -26,9 +27,13 @@ local ALIGN = core.ALIGN
|
|||||||
---@param ps psil ps interface
|
---@param ps psil ps interface
|
||||||
---@param id number? matrix ID
|
---@param id number? matrix ID
|
||||||
local function new_view(root, x, y, data, ps, id)
|
local function new_view(root, x, y, data, ps, id)
|
||||||
|
local label_fg = style.theme.label_fg
|
||||||
local text_fg = style.theme.text_fg
|
local text_fg = style.theme.text_fg
|
||||||
local lu_col = style.lu_colors
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
|
local ind_yel = style.ind_yel
|
||||||
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
local title = "INDUCTION MATRIX"
|
local title = "INDUCTION MATRIX"
|
||||||
if type(id) == "number" then title = title .. id end
|
if type(id) == "number" then title = title .. id end
|
||||||
|
|
||||||
@ -42,45 +47,47 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
|
|
||||||
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
||||||
|
|
||||||
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
||||||
local energy = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local capacity = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local input = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local output = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
|
local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_chg = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Avg. Chg:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_in = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="Avg. In: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_out = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Avg. Out:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end)
|
|
||||||
capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end)
|
capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end)
|
||||||
input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end)
|
energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end)
|
||||||
output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end)
|
|
||||||
|
|
||||||
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
||||||
|
input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end)
|
||||||
avg_in.register(ps, "avg_inflow", avg_in.update)
|
avg_in.register(ps, "avg_inflow", avg_in.update)
|
||||||
|
output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end)
|
||||||
avg_out.register(ps, "avg_outflow", avg_out.update)
|
avg_out.register(ps, "avg_outflow", avg_out.update)
|
||||||
|
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end)
|
||||||
|
|
||||||
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill:",unit="%",format="%8.2f",value=0,width=18,fg_bg=text_fg}
|
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill: ",format="%7.2f",unit="%",value=0,width=20,fg_bg=text_fg}
|
||||||
|
local cells = DataIndicator{parent=rect,x=11,y=13,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg}
|
||||||
local cells = DataIndicator{parent=rect,x=11,y=14,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg}
|
local providers = DataIndicator{parent=rect,x=11,y=14,lu_colors=lu_col,label="Providers:",format="%7d",value=0,width=18,fg_bg=text_fg}
|
||||||
local providers = DataIndicator{parent=rect,x=11,y=15,lu_colors=lu_col,label="Providers:",format="%7d",value=0,width=18,fg_bg=text_fg}
|
|
||||||
|
|
||||||
TextBox{parent=rect,text="Transfer Capacity",x=11,y=17,height=1,width=17,fg_bg=style.theme.label_fg}
|
|
||||||
local trans_cap = PowerIndicator{parent=rect,x=19,y=18,lu_colors=lu_col,label="",format="%5.2f",rate=true,value=0,width=12,fg_bg=text_fg}
|
|
||||||
|
|
||||||
|
fill.register(ps, "energy_fill", function (val) fill.update(val * 100) end)
|
||||||
cells.register(ps, "cells", cells.update)
|
cells.register(ps, "cells", cells.update)
|
||||||
providers.register(ps, "providers", providers.update)
|
providers.register(ps, "providers", providers.update)
|
||||||
fill.register(ps, "energy_fill", function (val) fill.update(val * 100) end)
|
|
||||||
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end)
|
local chging = IndicatorLight{parent=rect,x=11,y=16,label="Charging",colors=ind_wht}
|
||||||
|
local dischg = IndicatorLight{parent=rect,x=11,y=17,label="Discharging",colors=ind_wht}
|
||||||
|
local max_io = IndicatorLight{parent=rect,x=11,y=18,label="Max I/O Rate",colors=ind_yel}
|
||||||
|
|
||||||
|
chging.register(ps, "is_charging", chging.update)
|
||||||
|
dischg.register(ps, "is_discharging", dischg.update)
|
||||||
|
max_io.register(ps, "at_max_io", max_io.update)
|
||||||
|
|
||||||
local charge = VerticalBar{parent=rect,x=2,y=2,fg_bg=cpair(colors.green,colors.gray),height=17,width=4}
|
local charge = VerticalBar{parent=rect,x=2,y=2,fg_bg=cpair(colors.green,colors.gray),height=17,width=4}
|
||||||
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
||||||
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
||||||
|
|
||||||
TextBox{parent=rect,text="FILL",x=2,y=20,height=1,width=4,fg_bg=text_fg}
|
TextBox{parent=rect,text="FILL I/O",x=2,y=20,height=1,width=8,fg_bg=label_fg}
|
||||||
TextBox{parent=rect,text="I/O",x=7,y=20,height=1,width=3,fg_bg=text_fg}
|
|
||||||
|
|
||||||
local function calc_saturation(val)
|
local function calc_saturation(val)
|
||||||
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
||||||
@ -91,6 +98,49 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
charge.register(ps, "energy_fill", charge.update)
|
charge.register(ps, "energy_fill", charge.update)
|
||||||
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
||||||
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
||||||
|
|
||||||
|
local eta = TextBox{parent=rect,x=11,y=20,width=20,height=1,text="ETA Unknown",alignment=ALIGN.CENTER,fg_bg=style.theme.field_box}
|
||||||
|
|
||||||
|
eta.register(ps, "eta_ms", function (eta_ms)
|
||||||
|
local str, pre = "", util.trinary(eta_ms >= 0, "Full in ", "Empty in ")
|
||||||
|
|
||||||
|
local seconds = math.abs(eta_ms) / 1000
|
||||||
|
local minutes = seconds / 60
|
||||||
|
local hours = minutes / 60
|
||||||
|
local days = hours / 24
|
||||||
|
|
||||||
|
if math.abs(eta_ms) < 1000 or (eta_ms ~= eta_ms) then
|
||||||
|
-- really small or NaN
|
||||||
|
str = "No ETA"
|
||||||
|
elseif days < 1000 then
|
||||||
|
days = math.floor(days)
|
||||||
|
hours = math.floor(hours % 24)
|
||||||
|
minutes = math.floor(minutes % 60)
|
||||||
|
seconds = math.floor(seconds % 60)
|
||||||
|
|
||||||
|
if days > 0 then
|
||||||
|
str = days .. "d"
|
||||||
|
elseif hours > 0 then
|
||||||
|
str = hours .. "h " .. minutes .. "m"
|
||||||
|
elseif minutes > 0 then
|
||||||
|
str = minutes .. "m " .. seconds .. "s"
|
||||||
|
elseif seconds > 0 then
|
||||||
|
str = seconds .. "s"
|
||||||
|
end
|
||||||
|
|
||||||
|
str = pre .. str
|
||||||
|
else
|
||||||
|
local years = math.floor(days / 365.25)
|
||||||
|
|
||||||
|
if years <= 99999999 then
|
||||||
|
str = pre .. years .. "y"
|
||||||
|
else
|
||||||
|
str = pre .. "eras"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
eta.set_value(str)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_view
|
return new_view
|
||||||
|
@ -341,31 +341,25 @@ local function new_view(root, x, y)
|
|||||||
status.register(facility.ps, "current_waste_product", status.update)
|
status.register(facility.ps, "current_waste_product", status.update)
|
||||||
|
|
||||||
local waste_prod = RadioButton{parent=rect,x=2,y=3,options=style.waste.options,callback=process.set_process_waste,radio_colors=cpair(style.theme.accent_dark,style.theme.accent_light),select_color=colors.brown}
|
local waste_prod = RadioButton{parent=rect,x=2,y=3,options=style.waste.options,callback=process.set_process_waste,radio_colors=cpair(style.theme.accent_dark,style.theme.accent_light),select_color=colors.brown}
|
||||||
local pu_fallback = Checkbox{parent=rect,x=2,y=7,label="Pu Fallback",callback=process.set_pu_fallback,box_fg_bg=cpair(colors.green,style.theme.checkbox_bg)}
|
|
||||||
|
|
||||||
waste_prod.register(facility.ps, "process_waste_product", waste_prod.set_value)
|
waste_prod.register(facility.ps, "process_waste_product", waste_prod.set_value)
|
||||||
pu_fallback.register(facility.ps, "process_pu_fallback", pu_fallback.set_value)
|
|
||||||
|
|
||||||
local fb_active = IndicatorLight{parent=rect,x=2,y=9,label="Fallback Active",colors=ind_wht}
|
local fb_active = IndicatorLight{parent=rect,x=2,y=7,label="Fallback Active",colors=ind_wht}
|
||||||
|
local sps_disabled = IndicatorLight{parent=rect,x=2,y=8,label="SPS Disabled LC",colors=ind_yel}
|
||||||
|
|
||||||
fb_active.register(facility.ps, "pu_fallback_active", fb_active.update)
|
fb_active.register(facility.ps, "pu_fallback_active", fb_active.update)
|
||||||
|
sps_disabled.register(facility.ps, "sps_disabled_low_power", sps_disabled.update)
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=11,text="Plutonium Rate",height=1,width=17,fg_bg=style.label}
|
local pu_fallback = Checkbox{parent=rect,x=2,y=10,label="Pu Fallback",callback=process.set_pu_fallback,box_fg_bg=cpair(colors.brown,style.theme.checkbox_bg)}
|
||||||
local pu_rate = DataIndicator{parent=rect,x=2,label="",unit="mB/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=s_field,width=17}
|
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=14,text="Polonium Rate",height=1,width=17,fg_bg=style.label}
|
TextBox{parent=rect,x=2,y=12,height=3,text="Switch to Pu when SNAs cannot keep up with waste.",fg_bg=style.label}
|
||||||
local po_rate = DataIndicator{parent=rect,x=2,label="",unit="mB/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=s_field,width=17}
|
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=17,text="Antimatter Rate",height=1,width=17,fg_bg=style.label}
|
local lc_sps = Checkbox{parent=rect,x=2,y=16,label="Low Charge SPS",callback=process.set_sps_low_power,box_fg_bg=cpair(colors.brown,style.theme.checkbox_bg)}
|
||||||
local am_rate = DataIndicator{parent=rect,x=2,label="",unit="\xb5B/t",format="%12d",value=0,lu_colors=lu_cpair,fg_bg=s_field,width=17}
|
|
||||||
|
|
||||||
pu_rate.register(facility.ps, "pu_rate", pu_rate.update)
|
TextBox{parent=rect,x=2,y=18,height=3,text="Use SPS at low charge, otherwise switches to Po.",fg_bg=style.label}
|
||||||
po_rate.register(facility.ps, "po_rate", po_rate.update)
|
|
||||||
am_rate.register(facility.ps, "am_rate", am_rate.update)
|
|
||||||
|
|
||||||
local sna_count = DataIndicator{parent=rect,x=2,y=20,label="Linked SNAs:",format="%4d",value=0,lu_colors=lu_cpair,width=17}
|
pu_fallback.register(facility.ps, "process_pu_fallback", pu_fallback.set_value)
|
||||||
|
lc_sps.register(facility.ps, "process_sps_low_power", lc_sps.set_value)
|
||||||
sna_count.register(facility.ps, "sna_count", sna_count.update)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_view
|
return new_view
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
-- Configuration GUI
|
-- Configuration GUI
|
||||||
--
|
--
|
||||||
|
|
||||||
|
local constants = require("scada-common.constants")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
@ -33,45 +34,50 @@ local tri = util.trinary
|
|||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local IO = rsio.IO
|
local IO = rsio.IO
|
||||||
|
local IO_LVL = rsio.IO_LVL
|
||||||
|
local IO_MODE = rsio.IO_MODE
|
||||||
|
|
||||||
local LEFT = core.ALIGN.LEFT
|
local LEFT = core.ALIGN.LEFT
|
||||||
local CENTER = core.ALIGN.CENTER
|
local CENTER = core.ALIGN.CENTER
|
||||||
local RIGHT = core.ALIGN.RIGHT
|
local RIGHT = core.ALIGN.RIGHT
|
||||||
|
|
||||||
-- rsio port descriptions
|
-- rsio port descriptions
|
||||||
local PORT_DESC = {
|
local PORT_DESC_MAP = {
|
||||||
"Facility SCRAM",
|
{ IO.F_SCRAM, "Facility SCRAM" },
|
||||||
"Facility Acknowledge",
|
{ IO.F_ACK, "Facility Acknowledge" },
|
||||||
"Reactor SCRAM",
|
{ IO.R_SCRAM, "Reactor SCRAM" },
|
||||||
"Reactor RPS Reset",
|
{ IO.R_RESET, "Reactor RPS Reset" },
|
||||||
"Reactor Enable",
|
{ IO.R_ENABLE, "Reactor Enable" },
|
||||||
"Unit Acknowledge",
|
{ IO.U_ACK, "Unit Acknowledge" },
|
||||||
"Facility Alarm (high prio)",
|
{ IO.F_ALARM, "Facility Alarm (high prio)" },
|
||||||
"Facility Alarm (any)",
|
{ IO.F_ALARM_ANY, "Facility Alarm (any)" },
|
||||||
"Waste Plutonium Valve",
|
{ IO.F_MATRIX_LOW, "Induction Matrix < " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_LOW) .. "%" },
|
||||||
"Waste Polonium Valve",
|
{ IO.F_MATRIX_HIGH, "Induction Matrix > " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_HIGH) .. "%" },
|
||||||
"Waste Po Pellets Valve",
|
{ IO.F_MATRIX_CHG, "Induction Matrix Charge %" },
|
||||||
"Waste Antimatter Valve",
|
{ IO.WASTE_PU, "Waste Plutonium Valve" },
|
||||||
"Reactor Active",
|
{ IO.WASTE_PO, "Waste Polonium Valve" },
|
||||||
"Reactor in Auto Control",
|
{ IO.WASTE_POPL, "Waste Po Pellets Valve" },
|
||||||
"RPS Tripped",
|
{ IO.WASTE_AM, "Waste Antimatter Valve" },
|
||||||
"RPS Auto SCRAM",
|
{ IO.R_ACTIVE, "Reactor Active" },
|
||||||
"RPS High Damage",
|
{ IO.R_AUTO_CTRL, "Reactor in Auto Control" },
|
||||||
"RPS High Temperature",
|
{ IO.R_SCRAMMED, "RPS Tripped" },
|
||||||
"RPS Low Coolant",
|
{ IO.R_AUTO_SCRAM, "RPS Auto SCRAM" },
|
||||||
"RPS Excess Heated Coolant",
|
{ IO.R_HIGH_DMG, "RPS High Damage" },
|
||||||
"RPS Excess Waste",
|
{ IO.R_HIGH_TEMP, "RPS High Temperature" },
|
||||||
"RPS Insufficient Fuel",
|
{ IO.R_LOW_COOLANT, "RPS Low Coolant" },
|
||||||
"RPS PLC Fault",
|
{ IO.R_EXCESS_HC, "RPS Excess Heated Coolant" },
|
||||||
"RPS Supervisor Timeout",
|
{ IO.R_EXCESS_WS, "RPS Excess Waste" },
|
||||||
"Unit Alarm",
|
{ IO.R_INSUFF_FUEL, "RPS Insufficient Fuel" },
|
||||||
"Unit Emergency Cool. Valve"
|
{ IO.R_PLC_FAULT, "RPS PLC Fault" },
|
||||||
|
{ IO.R_PLC_TIMEOUT, "RPS Supervisor Timeout" },
|
||||||
|
{ IO.U_ALARM, "Unit Alarm" },
|
||||||
|
{ IO.U_EMER_COOL, "Unit Emergency 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 }
|
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 }
|
||||||
|
|
||||||
assert(#PORT_DESC == rsio.NUM_PORTS)
|
assert(#PORT_DESC_MAP == rsio.NUM_PORTS)
|
||||||
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
||||||
|
|
||||||
-- changes to the config data/format to let the user know
|
-- changes to the config data/format to let the user know
|
||||||
@ -442,7 +448,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_3,x=1,y=11,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_3,x=1,y=11,height=1,text="Facility Auth Key"}
|
||||||
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
local key, _, censor = TextField{parent=net_c_3,x=1,y=12,max_len=64,value=ini_cfg.AuthKey,width=32,height=1,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function censor_key(enable) censor(util.trinary(enable, "*", nil)) end
|
local function censor_key(enable) censor(tri(enable, "*", nil)) end
|
||||||
|
|
||||||
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
||||||
|
|
||||||
@ -555,7 +561,7 @@ local function config_view(display)
|
|||||||
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
local function back_from_colors()
|
local function back_from_colors()
|
||||||
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4))
|
main_pane.set_value(tri(tool_ctl.jumped_to_color, 1, 4))
|
||||||
tool_ctl.jumped_to_color = false
|
tool_ctl.jumped_to_color = false
|
||||||
recolor(1)
|
recolor(1)
|
||||||
end
|
end
|
||||||
@ -897,7 +903,7 @@ local function config_view(display)
|
|||||||
tool_ctl.p_desc.reposition(1, 8)
|
tool_ctl.p_desc.reposition(1, 8)
|
||||||
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
||||||
elseif type == "inductionPort" or type == "spsPort" then
|
elseif type == "inductionPort" or type == "spsPort" then
|
||||||
local dev = util.trinary(type == "inductionPort", "induction matrix", "SPS")
|
local dev = tri(type == "inductionPort", "induction matrix", "SPS")
|
||||||
tool_ctl.p_idx.hide(true)
|
tool_ctl.p_idx.hide(true)
|
||||||
tool_ctl.p_unit.hide(true)
|
tool_ctl.p_unit.hide(true)
|
||||||
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
||||||
@ -923,7 +929,7 @@ local function config_view(display)
|
|||||||
tool_ctl.ppm_devs.remove_all()
|
tool_ctl.ppm_devs.remove_all()
|
||||||
for name, entry in pairs(mounts) do
|
for name, entry in pairs(mounts) do
|
||||||
if util.table_contains(RTU_DEV_TYPES, entry.type) then
|
if util.table_contains(RTU_DEV_TYPES, entry.type) then
|
||||||
local bkg = util.trinary(alternate, colors.white, colors.lightGray)
|
local bkg = tri(alternate, colors.white, colors.lightGray)
|
||||||
|
|
||||||
---@cast entry ppm_entry
|
---@cast entry ppm_entry
|
||||||
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
||||||
@ -1085,8 +1091,9 @@ local function config_view(display)
|
|||||||
local rs_c_4 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_4 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
local rs_c_5 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_5 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
local rs_c_6 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_6 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
|
local rs_c_7 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6}}
|
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6,rs_c_7}}
|
||||||
|
|
||||||
TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
||||||
|
|
||||||
@ -1143,9 +1150,23 @@ local function config_view(display)
|
|||||||
text = "You selected the ALL_WASTE shortcut."
|
text = "You selected the ALL_WASTE shortcut."
|
||||||
else
|
else
|
||||||
tool_ctl.rs_cfg_shortcut.hide(true)
|
tool_ctl.rs_cfg_shortcut.hide(true)
|
||||||
tool_ctl.rs_cfg_side_l.set_value(util.trinary(rsio.get_io_dir(port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
tool_ctl.rs_cfg_side_l.set_value(tri(rsio.get_io_dir(port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
||||||
tool_ctl.rs_cfg_color.show()
|
tool_ctl.rs_cfg_color.show()
|
||||||
text = "You selected " .. rsio.to_string(port) .. " (for "
|
|
||||||
|
local io_type = "analog input "
|
||||||
|
local io_mode = rsio.get_io_mode(port)
|
||||||
|
local inv = tri(rsio.digital_is_active(port, IO_LVL.LOW) == true, "inverted ", "")
|
||||||
|
|
||||||
|
if io_mode == IO_MODE.DIGITAL_IN then
|
||||||
|
io_type = inv .. "digital input "
|
||||||
|
elseif io_mode == IO_MODE.DIGITAL_OUT then
|
||||||
|
io_type = inv .. "digital output "
|
||||||
|
elseif io_mode == IO_MODE.ANALOG_OUT then
|
||||||
|
io_type = "analog output "
|
||||||
|
end
|
||||||
|
|
||||||
|
text = "You selected the " .. io_type .. rsio.to_string(port) .. " (for "
|
||||||
|
|
||||||
if PORT_DSGN[port] == 1 then
|
if PORT_DSGN[port] == 1 then
|
||||||
text = text .. "a unit)."
|
text = text .. "a unit)."
|
||||||
tool_ctl.rs_cfg_unit_l.show()
|
tool_ctl.rs_cfg_unit_l.show()
|
||||||
@ -1167,25 +1188,35 @@ local function config_view(display)
|
|||||||
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=all_w_macro,x=16,y=1,width=5,height=1,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=all_w_macro,x=16,y=1,width=5,height=1,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=all_w_macro,x=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=all_w_macro,x=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
for i = 1, rsio.NUM_PORTS do
|
for i = 1, rsio.NUM_PORTS do
|
||||||
local name = rsio.to_string(i)
|
local p = PORT_DESC_MAP[i][1]
|
||||||
local io_dir = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, "[in]", "[out]")
|
local name = rsio.to_string(p)
|
||||||
local btn_color = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
local io_dir = tri(rsio.get_io_dir(p) == rsio.IO_DIR.IN, "[in]", "[out]")
|
||||||
|
local btn_color = tri(rsio.get_io_dir(p) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
||||||
|
|
||||||
local entry = Div{parent=rs_ports,height=1}
|
local entry = Div{parent=rs_ports,height=1}
|
||||||
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(i)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(p)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=entry,x=16,y=1,width=5,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=entry,x=16,y=1,width=5,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=entry,x=22,y=1,height=1,text=PORT_DESC[i],fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=22,y=1,height=1,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_selection = TextBox{parent=rs_c_3,x=1,y=1,height=1,text=""}
|
tool_ctl.rs_cfg_selection = TextBox{parent=rs_c_3,x=1,y=1,height=2,text=""}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=27,y=3,width=7,height=1,text="Unit ID"}
|
PushButton{parent=rs_c_3,x=36,y=3,text="What's that?",min_width=14,callback=function()rs_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=27,y=4,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
|
||||||
|
|
||||||
tool_ctl.rs_cfg_side_l = TextBox{parent=rs_c_3,x=1,y=3,width=11,height=1,text="Output Side"}
|
TextBox{parent=rs_c_7,x=1,y=1,height=4,text="(Normal) Digital Input: On if there is a redstone signal, off otherwise\nInverted Digital Input: On without a redstone signal, off otherwise"}
|
||||||
local side = Radio2D{parent=rs_c_3,x=1,y=4,rows=2,columns=3,default=1,options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.red}
|
TextBox{parent=rs_c_7,x=1,y=6,height=4,text="(Normal) Digital Output: Redstone signal to 'turn it on', none to 'turn it off'\nInverted Digital Output: No redstone signal to 'turn it on', redstone signal to 'turn it off'"}
|
||||||
|
TextBox{parent=rs_c_7,x=1,y=11,height=2,text="Analog Input: 0-15 redstone power level input\nAnalog Output: 0-15 scaled redstone power level output"}
|
||||||
|
PushButton{parent=rs_c_7,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.rs_cfg_side_l = TextBox{parent=rs_c_3,x=1,y=4,width=11,height=1,text="Output Side"}
|
||||||
|
local side = Radio2D{parent=rs_c_3,x=1,y=5,rows=1,columns=6,default=1,options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.red}
|
||||||
|
|
||||||
|
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=25,y=7,width=7,height=1,text="Unit ID"}
|
||||||
|
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=33,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function set_bundled(bundled)
|
local function set_bundled(bundled)
|
||||||
if bundled then tool_ctl.rs_cfg_color.enable() else tool_ctl.rs_cfg_color.disable() end
|
if bundled then tool_ctl.rs_cfg_color.enable() else tool_ctl.rs_cfg_color.disable() end
|
||||||
@ -1216,10 +1247,10 @@ local function config_view(display)
|
|||||||
if port >= 0 then
|
if port >= 0 then
|
||||||
---@type rtu_rs_definition
|
---@type rtu_rs_definition
|
||||||
local def = {
|
local def = {
|
||||||
unit = util.trinary(PORT_DSGN[port] == 1, u, nil),
|
unit = tri(PORT_DSGN[port] == 1, u, nil),
|
||||||
port = port,
|
port = port,
|
||||||
side = side_options_map[side.get_value()],
|
side = side_options_map[side.get_value()],
|
||||||
color = util.trinary(bundled.get_value(), color_options_map[tool_ctl.rs_cfg_color.get_value()], nil)
|
color = tri(bundled.get_value(), color_options_map[tool_ctl.rs_cfg_color.get_value()], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tool_ctl.rs_cfg_editing == false then
|
if tool_ctl.rs_cfg_editing == false then
|
||||||
@ -1233,10 +1264,10 @@ local function config_view(display)
|
|||||||
local default_colors = { colors.red, colors.orange, colors.yellow, colors.lime }
|
local default_colors = { colors.red, colors.orange, colors.yellow, colors.lime }
|
||||||
for i = 0, 3 do
|
for i = 0, 3 do
|
||||||
table.insert(tmp_cfg.Redstone, {
|
table.insert(tmp_cfg.Redstone, {
|
||||||
unit = util.trinary(PORT_DSGN[IO.WASTE_PU + i] == 1, u, nil),
|
unit = tri(PORT_DSGN[IO.WASTE_PU + i] == 1, u, nil),
|
||||||
port = IO.WASTE_PU + i,
|
port = IO.WASTE_PU + i,
|
||||||
side = util.trinary(bundled.get_value(), side_options_map[side.get_value()], default_sides[i + 1]),
|
side = tri(bundled.get_value(), side_options_map[side.get_value()], default_sides[i + 1]),
|
||||||
color = util.trinary(bundled.get_value(), default_colors[i + 1], nil)
|
color = tri(bundled.get_value(), default_colors[i + 1], nil)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1289,7 +1320,7 @@ local function config_view(display)
|
|||||||
peri_import_list.remove_all()
|
peri_import_list.remove_all()
|
||||||
for _, entry in ipairs(config.RTU_DEVICES) do
|
for _, entry in ipairs(config.RTU_DEVICES) do
|
||||||
local for_facility = entry.for_reactor == 0
|
local for_facility = entry.for_reactor == 0
|
||||||
local ini_unit = util.trinary(for_facility, nil, entry.for_reactor)
|
local ini_unit = tri(for_facility, nil, entry.for_reactor)
|
||||||
|
|
||||||
local def = { name = entry.name, unit = ini_unit, index = entry.index }
|
local def = { name = entry.name, unit = ini_unit, index = entry.index }
|
||||||
local mount = mounts[def.name] ---@type ppm_entry|nil
|
local mount = mounts[def.name] ---@type ppm_entry|nil
|
||||||
@ -1368,7 +1399,7 @@ local function config_view(display)
|
|||||||
table.insert(tmp_cfg.Redstone, def)
|
table.insert(tmp_cfg.Redstone, def)
|
||||||
|
|
||||||
local name = rsio.to_string(def.port)
|
local name = rsio.to_string(def.port)
|
||||||
local io_dir = util.trinary(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
local io_dir = tri(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
||||||
local conn = def.side
|
local conn = def.side
|
||||||
local unit = "facility"
|
local unit = "facility"
|
||||||
|
|
||||||
@ -1431,7 +1462,7 @@ local function config_view(display)
|
|||||||
local val = util.strval(raw)
|
local val = util.strval(raw)
|
||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
elseif f[1] == "LogMode" then val = tri(raw == log.MODE.APPEND, "append", "replace")
|
||||||
elseif f[1] == "FrontPanelTheme" then
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
val = util.strval(themes.fp_theme_name(raw))
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
elseif f[1] == "ColorMode" then
|
elseif f[1] == "ColorMode" then
|
||||||
@ -1440,7 +1471,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
if val == "nil" then val = "<not set>" end
|
if val == "nil" then val = "<not set>" end
|
||||||
|
|
||||||
local c = util.trinary(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 then
|
||||||
@ -1554,7 +1585,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.rs_cfg_selection.set_value(text)
|
tool_ctl.rs_cfg_selection.set_value(text)
|
||||||
tool_ctl.rs_cfg_side_l.set_value(util.trinary(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
tool_ctl.rs_cfg_side_l.set_value(tri(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
||||||
side.set_value(side_to_idx(def.side))
|
side.set_value(side_to_idx(def.side))
|
||||||
bundled.set_value(def.color ~= nil)
|
bundled.set_value(def.color ~= nil)
|
||||||
tool_ctl.rs_cfg_color.set_value(value)
|
tool_ctl.rs_cfg_color.set_value(value)
|
||||||
@ -1575,7 +1606,7 @@ local function config_view(display)
|
|||||||
local def = cfg.Redstone[i] ---@type rtu_rs_definition
|
local def = cfg.Redstone[i] ---@type rtu_rs_definition
|
||||||
|
|
||||||
local name = rsio.to_string(def.port)
|
local name = rsio.to_string(def.port)
|
||||||
local io_dir = util.trinary(rsio.get_io_mode(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
local io_dir = tri(rsio.get_io_mode(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
||||||
local conn = def.side
|
local conn = def.side
|
||||||
local unit = util.strval(def.unit or "F")
|
local unit = util.strval(def.unit or "F")
|
||||||
|
|
||||||
|
@ -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.9.4"
|
local RTU_VERSION = "v1.9.5"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_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 = "2.5.0"
|
comms.version = "2.5.1"
|
||||||
comms.api_version = "0.0.1"
|
comms.api_version = "0.0.1"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
@ -97,7 +97,8 @@ local FAC_COMMAND = {
|
|||||||
START = 2, -- start automatic process control
|
START = 2, -- start automatic process control
|
||||||
ACK_ALL_ALARMS = 3, -- acknowledge all alarms on all units
|
ACK_ALL_ALARMS = 3, -- acknowledge all alarms on all units
|
||||||
SET_WASTE_MODE = 4, -- set automatic waste processing mode
|
SET_WASTE_MODE = 4, -- set automatic waste processing mode
|
||||||
SET_PU_FB = 5 -- set plutonium fallback mode
|
SET_PU_FB = 5, -- set plutonium fallback mode
|
||||||
|
SET_SPS_LP = 6 -- set SPS at low power mode
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum UNIT_COMMAND
|
---@enum UNIT_COMMAND
|
||||||
|
@ -66,6 +66,18 @@ constants.ALARM_LIMITS = alarms
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Supervisor Redstone Activation Thresholds
|
||||||
|
|
||||||
|
---@class _rs_threshold_constants
|
||||||
|
local rs = {}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
constants.RS_THRESHOLDS = rs
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Supervisor Constants
|
--#region Supervisor Constants
|
||||||
|
|
||||||
-- milliseconds until coolant flow is assumed to be stable enough to enable certain coolant checks
|
-- milliseconds until coolant flow is assumed to be stable enough to enable certain coolant checks
|
||||||
|
@ -52,6 +52,8 @@ local IO_PORT = {
|
|||||||
-- facility
|
-- facility
|
||||||
F_ALARM = 7, -- active high, facility-wide alarm (any high priority unit alarm)
|
F_ALARM = 7, -- active high, facility-wide alarm (any high priority unit alarm)
|
||||||
F_ALARM_ANY = 8, -- active high, any alarm regardless of priority
|
F_ALARM_ANY = 8, -- active high, any alarm regardless of priority
|
||||||
|
F_MATRIX_LOW = 27, -- active high, induction matrix charge low
|
||||||
|
F_MATRIX_HIGH = 28, -- active high, induction matrix charge high
|
||||||
|
|
||||||
-- waste
|
-- waste
|
||||||
WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
|
WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
|
||||||
@ -75,17 +77,27 @@ 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
|
||||||
|
|
||||||
|
-- analog outputs --
|
||||||
|
|
||||||
|
-- facility
|
||||||
|
F_MATRIX_CHG = 29 -- analog charge level of the induction matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
rsio.IO_LVL = IO_LVL
|
rsio.IO_LVL = IO_LVL
|
||||||
rsio.IO_DIR = IO_DIR
|
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 = IO_PORT.U_EMER_COOL
|
|
||||||
|
rsio.NUM_PORTS = 29
|
||||||
|
rsio.NUM_DIG_PORTS = 28
|
||||||
|
rsio.NUM_ANA_PORTS = 1
|
||||||
|
|
||||||
-- self checks
|
-- self checks
|
||||||
|
|
||||||
|
assert(rsio.NUM_PORTS == (rsio.NUM_DIG_PORTS + rsio.NUM_ANA_PORTS), "port counts inconsistent")
|
||||||
|
|
||||||
local dup_chk = {}
|
local dup_chk = {}
|
||||||
for _, v in pairs(IO_PORT) do
|
for _, v in pairs(IO_PORT) do
|
||||||
assert(dup_chk[v] ~= true, "duplicate in port list")
|
assert(dup_chk[v] ~= true, "duplicate in port list")
|
||||||
@ -96,64 +108,45 @@ assert(#dup_chk == rsio.NUM_PORTS, "port list malformed")
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Utility Functions
|
--#region Utility Functions and Attribute Tables
|
||||||
|
|
||||||
local PORT_NAMES = {
|
local IO = IO_PORT
|
||||||
"F_SCRAM",
|
|
||||||
"F_ACK",
|
|
||||||
"R_SCRAM",
|
|
||||||
"R_RESET",
|
|
||||||
"R_ENABLE",
|
|
||||||
"U_ACK",
|
|
||||||
"F_ALARM",
|
|
||||||
"F_ALARM_ANY",
|
|
||||||
"WASTE_PU",
|
|
||||||
"WASTE_PO",
|
|
||||||
"WASTE_POPL",
|
|
||||||
"WASTE_AM",
|
|
||||||
"R_ACTIVE",
|
|
||||||
"R_AUTO_CTRL",
|
|
||||||
"R_SCRAMMED",
|
|
||||||
"R_AUTO_SCRAM",
|
|
||||||
"R_HIGH_DMG",
|
|
||||||
"R_HIGH_TEMP",
|
|
||||||
"R_LOW_COOLANT",
|
|
||||||
"R_EXCESS_HC",
|
|
||||||
"R_EXCESS_WS",
|
|
||||||
"R_INSUFF_FUEL",
|
|
||||||
"R_PLC_FAULT",
|
|
||||||
"R_PLC_TIMEOUT",
|
|
||||||
"U_ALARM",
|
|
||||||
"U_EMER_COOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
-- list of all port names
|
||||||
|
local PORT_NAMES = {}
|
||||||
|
for k, v in pairs(IO) do PORT_NAMES[v] = k end
|
||||||
|
|
||||||
|
-- list of all port I/O modes
|
||||||
local MODES = {
|
local MODES = {
|
||||||
IO_MODE.DIGITAL_IN, -- F_SCRAM
|
[IO.F_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- F_ACK
|
[IO.F_ACK] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_SCRAM
|
[IO.R_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_RESET
|
[IO.R_RESET] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_ENABLE
|
[IO.R_ENABLE] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- U_ACK
|
[IO.U_ACK] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_OUT, -- F_ALARM
|
[IO.F_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- F_ALARM_ANY
|
[IO.F_ALARM_ANY] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_PU
|
[IO.F_MATRIX_LOW] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_PO
|
[IO.F_MATRIX_HIGH] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_POPL
|
[IO.WASTE_PU] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_AM
|
[IO.WASTE_PO] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_ACTIVE
|
[IO.WASTE_POPL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_CTRL
|
[IO.WASTE_AM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_SCRAMMED
|
[IO.R_ACTIVE] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_SCRAM
|
[IO.R_AUTO_CTRL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_DMG
|
[IO.R_SCRAMMED] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_TEMP
|
[IO.R_AUTO_SCRAM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_LOW_COOLANT
|
[IO.R_HIGH_DMG] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_HC
|
[IO.R_HIGH_TEMP] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_WS
|
[IO.R_LOW_COOLANT] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_INSUFF_FUEL
|
[IO.R_EXCESS_HC] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_PLC_FAULT
|
[IO.R_EXCESS_WS] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_PLC_TIMEOUT
|
[IO.R_INSUFF_FUEL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- U_ALARM
|
[IO.R_PLC_FAULT] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT -- U_EMER_COOL
|
[IO.R_PLC_TIMEOUT] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.U_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.U_EMER_COOL] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.F_MATRIX_CHG] = IO_MODE.ANALOG_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rsio.NUM_PORTS == #PORT_NAMES, "port names length incorrect")
|
assert(rsio.NUM_PORTS == #PORT_NAMES, "port names length incorrect")
|
||||||
@ -179,74 +172,51 @@ local function _O_ACTIVE_LOW(active) if active then return IO_LVL.LOW else retur
|
|||||||
|
|
||||||
-- I/O mappings to I/O function and I/O mode
|
-- I/O mappings to I/O function and I/O mode
|
||||||
local RS_DIO_MAP = {
|
local RS_DIO_MAP = {
|
||||||
-- F_SCRAM
|
[IO.F_SCRAM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
[IO.F_ACK] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
-- F_ACK
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- R_SCRAM
|
[IO.R_SCRAM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
[IO.R_RESET] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
-- R_RESET
|
[IO.R_ENABLE] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
-- R_ENABLE
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- U_ACK
|
[IO.U_ACK] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- F_ALARM
|
[IO.F_ALARM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.F_ALARM_ANY] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- F_ALARM_ANY
|
[IO.F_MATRIX_LOW] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.F_MATRIX_HIGH] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
|
|
||||||
-- WASTE_PU
|
[IO.WASTE_PU] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
[IO.WASTE_PO] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
-- WASTE_PO
|
[IO.WASTE_POPL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
[IO.WASTE_AM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
-- WASTE_POPL
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
|
||||||
-- WASTE_AM
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
|
||||||
|
|
||||||
-- R_ACTIVE
|
[IO.R_ACTIVE] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_AUTO_CTRL] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_AUTO_CTRL
|
[IO.R_SCRAMMED] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_AUTO_SCRAM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_SCRAMMED
|
[IO.R_HIGH_DMG] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_HIGH_TEMP] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_AUTO_SCRAM
|
[IO.R_LOW_COOLANT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_EXCESS_HC] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_HIGH_DMG
|
[IO.R_EXCESS_WS] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_INSUFF_FUEL] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_HIGH_TEMP
|
[IO.R_PLC_FAULT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _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 },
|
||||||
-- R_LOW_COOLANT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_EXCESS_HC
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_EXCESS_WS
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_INSUFF_FUEL
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_PLC_FAULT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_PLC_TIMEOUT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
|
|
||||||
-- U_ALARM
|
[IO.U_ALARM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _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 }
|
||||||
-- U_EMER_COOL
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rsio.NUM_PORTS == #RS_DIO_MAP, "RS_DIO_MAP length incorrect")
|
assert(rsio.NUM_DIG_PORTS == #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
|
||||||
---@param port IO_PORT
|
---@param port IO_PORT
|
||||||
---@return IO_DIR
|
---@return IO_DIR
|
||||||
function rsio.get_io_dir(port)
|
function rsio.get_io_dir(port)
|
||||||
if rsio.is_valid_port(port) then return RS_DIO_MAP[port].mode
|
if rsio.is_valid_port(port) then
|
||||||
|
return util.trinary(MODES[port] == IO_MODE.DIGITAL_OUT or MODES[port] == IO_MODE.ANALOG_OUT, IO_DIR.OUT, IO_DIR.IN)
|
||||||
else return IO_DIR.IN end
|
else return IO_DIR.IN end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -310,6 +280,13 @@ end
|
|||||||
|
|
||||||
--#region Digital I/O
|
--#region Digital I/O
|
||||||
|
|
||||||
|
-- check if a port is digital
|
||||||
|
---@nodiscard
|
||||||
|
---@param port IO_PORT
|
||||||
|
function rsio.is_digital(port)
|
||||||
|
return rsio.is_valid_port(port) and (MODES[port] == IO_MODE.DIGITAL_IN or MODES[port] == IO_MODE.DIGITAL_OUT)
|
||||||
|
end
|
||||||
|
|
||||||
-- get digital I/O level reading from a redstone boolean input value
|
-- get digital I/O level reading from a redstone boolean input value
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param rs_value boolean raw value from redstone
|
---@param rs_value boolean raw value from redstone
|
||||||
@ -330,7 +307,7 @@ function rsio.digital_write(level) return level == IO_LVL.HIGH end
|
|||||||
---@param active boolean state to convert to logic level
|
---@param active boolean state to convert to logic level
|
||||||
---@return IO_LVL|false
|
---@return IO_LVL|false
|
||||||
function rsio.digital_write_active(port, active)
|
function rsio.digital_write_active(port, active)
|
||||||
if (not util.is_int(port)) or (port < IO_PORT.F_ALARM) or (port > IO_PORT.U_EMER_COOL) then
|
if not rsio.is_digital(port) then
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
return RS_DIO_MAP[port]._out(active)
|
return RS_DIO_MAP[port]._out(active)
|
||||||
@ -343,9 +320,7 @@ end
|
|||||||
---@param level IO_LVL logic level
|
---@param level IO_LVL logic level
|
||||||
---@return boolean|nil state true for active, false for inactive, or nil if invalid port or level provided
|
---@return boolean|nil state true for active, false for inactive, or nil if invalid port or level provided
|
||||||
function rsio.digital_is_active(port, level)
|
function rsio.digital_is_active(port, level)
|
||||||
if not util.is_int(port) then
|
if (not rsio.is_digital(port)) or level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
||||||
return nil
|
|
||||||
elseif level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return RS_DIO_MAP[port]._in(level)
|
return RS_DIO_MAP[port]._in(level)
|
||||||
@ -356,6 +331,13 @@ end
|
|||||||
|
|
||||||
--#region Analog I/O
|
--#region Analog I/O
|
||||||
|
|
||||||
|
-- check if a port is analog
|
||||||
|
---@nodiscard
|
||||||
|
---@param port IO_PORT
|
||||||
|
function rsio.is_analog(port)
|
||||||
|
return rsio.is_valid_port(port) and (MODES[port] == IO_MODE.ANALOG_IN or MODES[port] == IO_MODE.ANALOG_OUT)
|
||||||
|
end
|
||||||
|
|
||||||
-- read an analog value scaled from min to max
|
-- read an analog value scaled from min to max
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param rs_value number redstone reading (0 to 15)
|
---@param rs_value number redstone reading (0 to 15)
|
||||||
@ -372,7 +354,7 @@ end
|
|||||||
---@param value number value to write (from min to max range)
|
---@param value number value to write (from min to max range)
|
||||||
---@param min number minimum of range
|
---@param min number minimum of range
|
||||||
---@param max number maximum of range
|
---@param max number maximum of range
|
||||||
---@return number rs_value scaled redstone reading (0 to 15)
|
---@return integer rs_value scaled redstone reading (0 to 15)
|
||||||
function rsio.analog_write(value, min, max)
|
function rsio.analog_write(value, min, max)
|
||||||
local scaled_value = (value - min) / (max - min)
|
local scaled_value = (value - min) / (max - min)
|
||||||
return math.floor(scaled_value * 15)
|
return math.floor(scaled_value * 15)
|
||||||
|
@ -22,7 +22,7 @@ local t_pack = table.pack
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
-- scada-common version
|
-- scada-common version
|
||||||
util.version = "1.2.2"
|
util.version = "1.3.0"
|
||||||
|
|
||||||
util.TICK_TIME_S = 0.05
|
util.TICK_TIME_S = 0.05
|
||||||
util.TICK_TIME_MS = 50
|
util.TICK_TIME_MS = 50
|
||||||
@ -181,8 +181,7 @@ function util.round(x) return math.floor(x + 0.5) end
|
|||||||
-- get a new moving average object
|
-- get a new moving average object
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param length integer history length
|
---@param length integer history length
|
||||||
---@param default number value to fill history with for first call to compute()
|
function util.mov_avg(length)
|
||||||
function util.mov_avg(length, default)
|
|
||||||
local data = {}
|
local data = {}
|
||||||
local index = 1
|
local index = 1
|
||||||
local last_t = 0 ---@type number|nil
|
local last_t = 0 ---@type number|nil
|
||||||
@ -190,11 +189,15 @@ function util.mov_avg(length, default)
|
|||||||
---@class moving_average
|
---@class moving_average
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- reset all to a given value
|
-- reset all to a given value, or clear all data if no value is given
|
||||||
---@param x number value
|
---@param x number? value
|
||||||
function public.reset(x)
|
function public.reset(x)
|
||||||
|
index = 1
|
||||||
data = {}
|
data = {}
|
||||||
for _ = 1, length do t_insert(data, x) end
|
|
||||||
|
if x then
|
||||||
|
for _ = 1, length do t_insert(data, x) end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- record a new value
|
-- record a new value
|
||||||
@ -214,12 +217,15 @@ function util.mov_avg(length, default)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@return number average
|
---@return number average
|
||||||
function public.compute()
|
function public.compute()
|
||||||
local sum = 0
|
if #data == 0 then return 0 end
|
||||||
for i = 1, length do sum = sum + data[i] end
|
|
||||||
return sum / length
|
|
||||||
end
|
|
||||||
|
|
||||||
public.reset(default)
|
local sum = 0
|
||||||
|
for i = 1, #data do
|
||||||
|
sum = sum + data[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
return sum / #data
|
||||||
|
end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
@ -120,6 +120,8 @@ function facility.new(config, cooling_conf)
|
|||||||
waste_product = WASTE.PLUTONIUM,
|
waste_product = WASTE.PLUTONIUM,
|
||||||
current_waste_product = WASTE.PLUTONIUM,
|
current_waste_product = WASTE.PLUTONIUM,
|
||||||
pu_fallback = false,
|
pu_fallback = false,
|
||||||
|
sps_low_power = false,
|
||||||
|
disabled_sps = false,
|
||||||
-- alarm tones
|
-- alarm tones
|
||||||
tone_states = {},
|
tone_states = {},
|
||||||
test_tone_set = false,
|
test_tone_set = false,
|
||||||
@ -128,9 +130,14 @@ function facility.new(config, cooling_conf)
|
|||||||
test_alarm_states = {},
|
test_alarm_states = {},
|
||||||
-- statistics
|
-- statistics
|
||||||
im_stat_init = false,
|
im_stat_init = false,
|
||||||
avg_charge = util.mov_avg(3, 0.0),
|
avg_charge = util.mov_avg(3), -- 3 seconds
|
||||||
avg_inflow = util.mov_avg(6, 0.0),
|
avg_inflow = util.mov_avg(6), -- 3 seconds
|
||||||
avg_outflow = util.mov_avg(6, 0.0)
|
avg_outflow = util.mov_avg(6), -- 3 seconds
|
||||||
|
-- induction matrix charge delta stats
|
||||||
|
avg_net = util.mov_avg(60), -- 60 seconds
|
||||||
|
imtx_last_capacity = 0,
|
||||||
|
imtx_last_charge = 0,
|
||||||
|
imtx_last_charge_t = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
-- create units
|
-- create units
|
||||||
@ -300,22 +307,45 @@ function facility.new(config, cooling_conf)
|
|||||||
|
|
||||||
-- calculate moving averages for induction matrix
|
-- calculate moving averages for induction matrix
|
||||||
if self.induction[1] ~= nil then
|
if self.induction[1] ~= nil then
|
||||||
local matrix = self.induction[1] ---@type unit_session
|
local matrix = self.induction[1] ---@type unit_session
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
local db = matrix.get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
charge_update = db.tanks.last_update
|
charge_update = db.tanks.last_update
|
||||||
rate_update = db.state.last_update
|
rate_update = db.state.last_update
|
||||||
|
|
||||||
if (charge_update > 0) and (rate_update > 0) then
|
if (charge_update > 0) and (rate_update > 0) then
|
||||||
|
local energy = util.joules_to_fe(db.tanks.energy)
|
||||||
|
local input = util.joules_to_fe(db.state.last_input)
|
||||||
|
local output = util.joules_to_fe(db.state.last_output)
|
||||||
|
|
||||||
if self.im_stat_init then
|
if self.im_stat_init then
|
||||||
self.avg_charge.record(util.joules_to_fe(db.tanks.energy), charge_update)
|
self.avg_charge.record(energy, charge_update)
|
||||||
self.avg_inflow.record(util.joules_to_fe(db.state.last_input), rate_update)
|
self.avg_inflow.record(input, rate_update)
|
||||||
self.avg_outflow.record(util.joules_to_fe(db.state.last_output), rate_update)
|
self.avg_outflow.record(output, rate_update)
|
||||||
|
|
||||||
|
if charge_update ~= self.imtx_last_charge_t then
|
||||||
|
local delta = (energy - self.imtx_last_charge) / (charge_update - self.imtx_last_charge_t)
|
||||||
|
|
||||||
|
self.imtx_last_charge = energy
|
||||||
|
self.imtx_last_charge_t = charge_update
|
||||||
|
|
||||||
|
-- if the capacity changed, toss out existing data
|
||||||
|
if db.build.max_energy ~= self.imtx_last_capacity then
|
||||||
|
self.imtx_last_capacity = db.build.max_energy
|
||||||
|
self.avg_net.reset()
|
||||||
|
else
|
||||||
|
self.avg_net.record(delta, charge_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self.im_stat_init = true
|
self.im_stat_init = true
|
||||||
self.avg_charge.reset(util.joules_to_fe(db.tanks.energy))
|
|
||||||
self.avg_inflow.reset(util.joules_to_fe(db.state.last_input))
|
self.avg_charge.reset(energy)
|
||||||
self.avg_outflow.reset(util.joules_to_fe(db.state.last_output))
|
self.avg_inflow.reset(input)
|
||||||
|
self.avg_outflow.reset(output)
|
||||||
|
|
||||||
|
self.imtx_last_charge = energy
|
||||||
|
self.imtx_last_charge_t = charge_update
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -620,8 +650,7 @@ function facility.new(config, cooling_conf)
|
|||||||
local astatus = self.ascram_status
|
local astatus = self.ascram_status
|
||||||
|
|
||||||
if self.induction[1] ~= nil then
|
if self.induction[1] ~= nil then
|
||||||
local matrix = self.induction[1] ---@type unit_session
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
|
||||||
|
|
||||||
-- clear matrix disconnected
|
-- clear matrix disconnected
|
||||||
if astatus.matrix_dc then
|
if astatus.matrix_dc then
|
||||||
@ -774,6 +803,15 @@ function facility.new(config, cooling_conf)
|
|||||||
|
|
||||||
self.io_ctl.digital_write(IO.F_ALARM, has_prio_alarm)
|
self.io_ctl.digital_write(IO.F_ALARM, has_prio_alarm)
|
||||||
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
|
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
|
||||||
|
|
||||||
|
-- update induction matrix related outputs
|
||||||
|
if self.induction[1] ~= nil then
|
||||||
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
|
self.io_ctl.digital_write(IO.F_MATRIX_LOW, db.tanks.energy_fill < const.RS_THRESHOLDS.IMATRIX_CHARGE_LOW)
|
||||||
|
self.io_ctl.digital_write(IO.F_MATRIX_HIGH, db.tanks.energy_fill > const.RS_THRESHOLDS.IMATRIX_CHARGE_HIGH)
|
||||||
|
self.io_ctl.analog_write(IO.F_MATRIX_CHG, db.tanks.energy_fill, 0, 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
@ -804,9 +842,25 @@ function facility.new(config, cooling_conf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- update waste product
|
-- update waste product
|
||||||
if self.waste_product == WASTE.PLUTONIUM or (self.pu_fallback and insufficent_po_rate) then
|
|
||||||
|
self.current_waste_product = self.waste_product
|
||||||
|
|
||||||
|
if (not self.sps_low_power) and (self.waste_product == WASTE.ANTI_MATTER) and (self.induction[1] ~= nil) then
|
||||||
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
|
if db.tanks.energy_fill >= 0.15 then
|
||||||
|
self.disabled_sps = false
|
||||||
|
elseif self.disabled_sps or ((db.tanks.last_update > 0) and (db.tanks.energy_fill < 0.1)) then
|
||||||
|
self.disabled_sps = true
|
||||||
|
self.current_waste_product = WASTE.POLONIUM
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.disabled_sps = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.pu_fallback and insufficent_po_rate then
|
||||||
self.current_waste_product = WASTE.PLUTONIUM
|
self.current_waste_product = WASTE.PLUTONIUM
|
||||||
else self.current_waste_product = self.waste_product end
|
end
|
||||||
|
|
||||||
-- make sure dynamic tanks are allowing outflow if required
|
-- make sure dynamic tanks are allowing outflow if required
|
||||||
-- set all, rather than trying to determine which is for which (simpler & safer)
|
-- set all, rather than trying to determine which is for which (simpler & safer)
|
||||||
@ -1063,6 +1117,14 @@ function facility.new(config, cooling_conf)
|
|||||||
return self.pu_fallback
|
return self.pu_fallback
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- enable/disable SPS at low power
|
||||||
|
---@param enabled boolean requested state
|
||||||
|
---@return boolean enabled newly set value
|
||||||
|
function public.set_sps_low_power(enabled)
|
||||||
|
self.sps_low_power = enabled == true
|
||||||
|
return self.sps_low_power
|
||||||
|
end
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Diagnostic Testing
|
--#region Diagnostic Testing
|
||||||
@ -1167,7 +1229,8 @@ function facility.new(config, cooling_conf)
|
|||||||
self.status_text[2],
|
self.status_text[2],
|
||||||
self.group_map,
|
self.group_map,
|
||||||
self.current_waste_product,
|
self.current_waste_product,
|
||||||
(self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM)
|
self.pu_fallback and (self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM),
|
||||||
|
self.disabled_sps
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1183,15 +1246,21 @@ function facility.new(config, cooling_conf)
|
|||||||
status.power = {
|
status.power = {
|
||||||
self.avg_charge.compute(),
|
self.avg_charge.compute(),
|
||||||
self.avg_inflow.compute(),
|
self.avg_inflow.compute(),
|
||||||
self.avg_outflow.compute()
|
self.avg_outflow.compute(),
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
-- status of induction matricies (including tanks)
|
-- status of induction matricies (including tanks)
|
||||||
status.induction = {}
|
status.induction = {}
|
||||||
for i = 1, #self.induction do
|
for i = 1, #self.induction do
|
||||||
local matrix = self.induction[i] ---@type unit_session
|
local matrix = self.induction[i] ---@type unit_session
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
local db = matrix.get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
||||||
|
|
||||||
|
local fe_per_ms = self.avg_net.compute()
|
||||||
|
local remaining = util.joules_to_fe(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
||||||
|
status.power[4] = remaining / fe_per_ms
|
||||||
end
|
end
|
||||||
|
|
||||||
-- status of sps
|
-- status of sps
|
||||||
|
@ -270,6 +270,12 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
else
|
else
|
||||||
log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
|
log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
|
||||||
end
|
end
|
||||||
|
elseif cmd == FAC_COMMAND.SET_SPS_LP then
|
||||||
|
if pkt.length == 2 then
|
||||||
|
_send(CRDN_TYPE.FAC_CMD, { cmd, facility.set_sps_low_power(pkt.data[2]) })
|
||||||
|
else
|
||||||
|
log.debug(log_header .. "CRDN set sps low power packet length mismatch")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "CRDN facility command unknown")
|
log.debug(log_header .. "CRDN facility command unknown")
|
||||||
end
|
end
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
-- Redstone RTU Session I/O Controller
|
-- Redstone RTU Session I/O Controller
|
||||||
--
|
--
|
||||||
|
|
||||||
|
local rsio = require("scada-common.rsio")
|
||||||
|
|
||||||
local rsctl = {}
|
local rsctl = {}
|
||||||
|
|
||||||
-- create a new redstone RTU I/O controller
|
-- create a new redstone RTU I/O controller
|
||||||
@ -16,7 +18,7 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
function public.is_connected(port)
|
function public.is_connected(port)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
if db.io[port] ~= nil then return true end
|
if db.io[port] ~= nil then return true end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -28,8 +30,8 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@param value boolean
|
---@param value boolean
|
||||||
function public.digital_write(port, value)
|
function public.digital_write(port, value)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
local io = db.io[port] ---@type rs_db_dig_io|nil
|
local io = db.io[port] ---@type rs_db_dig_io|nil
|
||||||
if io ~= nil then io.write(value) end
|
if io ~= nil then io.write(value) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -40,12 +42,25 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@return boolean|nil
|
---@return boolean|nil
|
||||||
function public.digital_read(port)
|
function public.digital_read(port)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
local io = db.io[port] ---@type rs_db_dig_io|nil
|
local io = db.io[port] ---@type rs_db_dig_io|nil
|
||||||
if io ~= nil then return io.read() end
|
if io ~= nil then return io.read() end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- write to an analog redstone port (applies to all RTUs)
|
||||||
|
---@param port IO_PORT
|
||||||
|
---@param value number value
|
||||||
|
---@param min number minimum value for scaling 0 to 15
|
||||||
|
---@param max number maximum value for scaling 0 to 15
|
||||||
|
function public.analog_write(port, value, min, max)
|
||||||
|
for i = 1, #redstone_rtus do
|
||||||
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
|
local io = db.io[port] ---@type rs_db_ana_io|nil
|
||||||
|
if io ~= nil then io.write(rsio.analog_write(value, min, max)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
|||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local SUPERVISOR_VERSION = "v1.3.6"
|
local SUPERVISOR_VERSION = "v1.3.7"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
@ -71,8 +71,8 @@ function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
|||||||
---@class _unit_self
|
---@class _unit_self
|
||||||
local self = {
|
local self = {
|
||||||
r_id = reactor_id,
|
r_id = reactor_id,
|
||||||
plc_s = nil, ---@class plc_session_struct
|
plc_s = nil, ---@type plc_session_struct
|
||||||
plc_i = nil, ---@class plc_session
|
plc_i = nil, ---@type plc_session
|
||||||
num_boilers = num_boilers,
|
num_boilers = num_boilers,
|
||||||
num_turbines = num_turbines,
|
num_turbines = num_turbines,
|
||||||
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
||||||
|
@ -1,16 +1,28 @@
|
|||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local testutils = require("test.testutils")
|
local testutils = require("test.testutils")
|
||||||
|
|
||||||
|
local IO = rsio.IO
|
||||||
|
local IO_LVL = rsio.IO_LVL
|
||||||
|
local IO_MODE = rsio.IO_MODE
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
|
|
||||||
local IO = rsio.IO
|
-- list of inverted digital signals<br>
|
||||||
local IO_LVL = rsio.IO_LVL
|
-- only using the key for a quick lookup, value just can't be nil
|
||||||
local IO_MODE = rsio.IO_MODE
|
local DIG_INV = {
|
||||||
|
[IO.F_SCRAM] = 0,
|
||||||
|
[IO.R_SCRAM] = 0,
|
||||||
|
[IO.WASTE_PU] = 0,
|
||||||
|
[IO.WASTE_PO] = 0,
|
||||||
|
[IO.WASTE_POPL] = 0,
|
||||||
|
[IO.WASTE_AM] = 0,
|
||||||
|
[IO.U_EMER_COOL] = 0
|
||||||
|
}
|
||||||
|
|
||||||
println("starting RSIO tester")
|
println("starting RSIO tester")
|
||||||
println("")
|
println("")
|
||||||
@ -50,8 +62,8 @@ testutils.pause()
|
|||||||
|
|
||||||
println(">>> checking invalid ports:")
|
println(">>> checking invalid ports:")
|
||||||
|
|
||||||
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "")
|
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "UNKNOWN")
|
||||||
testutils.test_func_nil("rsio.to_string", rsio.to_string, "")
|
testutils.test_func_nil("rsio.to_string", rsio.to_string, "UNKNOWN")
|
||||||
testutils.test_func("rsio.get_io_mode", rsio.get_io_mode, { -1, 100, false }, IO_MODE.ANALOG_IN)
|
testutils.test_func("rsio.get_io_mode", rsio.get_io_mode, { -1, 100, false }, IO_MODE.ANALOG_IN)
|
||||||
testutils.test_func_nil("rsio.get_io_mode", rsio.get_io_mode, IO_MODE.ANALOG_IN)
|
testutils.test_func_nil("rsio.get_io_mode", rsio.get_io_mode, IO_MODE.ANALOG_IN)
|
||||||
|
|
||||||
@ -100,46 +112,35 @@ println(">>> checking port I/O:")
|
|||||||
|
|
||||||
print("rsio.digital_is_active(...): ")
|
print("rsio.digital_is_active(...): ")
|
||||||
|
|
||||||
-- check input ports
|
-- check all digital ports
|
||||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.LOW) == true, "IO_F_SCRAM_HIGH")
|
for i = 1, rsio.NUM_PORTS do
|
||||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.HIGH) == false, "IO_F_SCRAM_LOW")
|
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.LOW) == true, "IO_R_SCRAM_HIGH")
|
local high = DIG_INV[i] == nil
|
||||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.HIGH) == false, "IO_R_SCRAM_LOW")
|
assert(rsio.digital_is_active(i, IO_LVL.LOW) == not high, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.LOW) == false, "IO_R_ENABLE_HIGH")
|
assert(rsio.digital_is_active(i, IO_LVL.HIGH) == high, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.HIGH) == true, "IO_R_ENABLE_LOW")
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- non-inputs should always return LOW
|
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.LOW) == nil, "ANA_DIG_READ_LOW")
|
||||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.LOW) == false, "IO_OUT_READ_LOW")
|
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.HIGH) == nil, "ANA_DIG_READ_HIGH")
|
||||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.HIGH) == false, "IO_OUT_READ_HIGH")
|
|
||||||
|
|
||||||
println("PASS")
|
println("PASS")
|
||||||
|
|
||||||
-- check output ports
|
-- check digital write
|
||||||
|
|
||||||
print("rsio.digital_write(...): ")
|
print("rsio.digital_write_active(...): ")
|
||||||
|
|
||||||
-- check output ports
|
-- check all digital ports
|
||||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.LOW, "IO_F_ALARM_LOW")
|
for i = 1, rsio.NUM_PORTS do
|
||||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.HIGH, "IO_F_ALARM_HIGH")
|
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.HIGH, "IO_WASTE_PU_HIGH")
|
local high = DIG_INV[i] == nil
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.LOW, "IO_WASTE_PU_LOW")
|
assert(rsio.digital_write_active(i, not high) == IO_LVL.LOW, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.HIGH, "IO_WASTE_PO_HIGH")
|
assert(rsio.digital_write_active(i, high) == IO_LVL.HIGH, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.LOW, "IO_WASTE_PO_LOW")
|
end
|
||||||
assert(rsio.digital_write_active(IO.WASTE_POPL, true) == IO_LVL.HIGH, "IO_WASTE_POPL_HIGH")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_POPL, true) == IO_LVL.LOW, "IO_WASTE_POPL_LOW")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_AM, true) == IO_LVL.HIGH, "IO_WASTE_AM_HIGH")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_AM, true) == IO_LVL.LOW, "IO_WASTE_AM_LOW")
|
|
||||||
|
|
||||||
-- check all reactor output ports (all are active high)
|
|
||||||
for i = IO.R_ALARM, (IO.R_PLC_TIMEOUT - IO.R_ALARM + 1) do
|
|
||||||
assert(rsio.to_string(i) ~= "", "REACTOR_IO_BAD_PORT")
|
|
||||||
assert(rsio.digital_write_active(i, false) == IO_LVL.LOW, "IO_" .. rsio.to_string(i) .. "_LOW")
|
|
||||||
assert(rsio.digital_write_active(i, true) == IO_LVL.HIGH, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- non-outputs should always return false
|
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, true) == false, "ANA_DIG_WRITE_TRUE")
|
||||||
assert(rsio.digital_write_active(IO.F_SCRAM, false) == IO_LVL.LOW, "IO_IN_WRITE_FALSE")
|
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, false) == false, "ANA_DIG_WRITE_FALSE")
|
||||||
assert(rsio.digital_write_active(IO.F_SCRAM, true) == IO_LVL.LOW, "IO_IN_WRITE_TRUE")
|
|
||||||
|
|
||||||
println("PASS")
|
println("PASS")
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user