mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#412 updates to RSIO for induction matrix low, high, and analog charge level
This commit is contained in:
parent
a786404092
commit
d9efd5b8d2
@ -2,6 +2,7 @@
|
||||
-- Configuration GUI
|
||||
--
|
||||
|
||||
local constants = require("scada-common.constants")
|
||||
local log = require("scada-common.log")
|
||||
local ppm = require("scada-common.ppm")
|
||||
local rsio = require("scada-common.rsio")
|
||||
@ -39,39 +40,42 @@ local CENTER = core.ALIGN.CENTER
|
||||
local RIGHT = core.ALIGN.RIGHT
|
||||
|
||||
-- rsio port descriptions
|
||||
local PORT_DESC = {
|
||||
"Facility SCRAM",
|
||||
"Facility Acknowledge",
|
||||
"Reactor SCRAM",
|
||||
"Reactor RPS Reset",
|
||||
"Reactor Enable",
|
||||
"Unit Acknowledge",
|
||||
"Facility Alarm (high prio)",
|
||||
"Facility Alarm (any)",
|
||||
"Waste Plutonium Valve",
|
||||
"Waste Polonium Valve",
|
||||
"Waste Po Pellets Valve",
|
||||
"Waste Antimatter Valve",
|
||||
"Reactor Active",
|
||||
"Reactor in Auto Control",
|
||||
"RPS Tripped",
|
||||
"RPS Auto SCRAM",
|
||||
"RPS High Damage",
|
||||
"RPS High Temperature",
|
||||
"RPS Low Coolant",
|
||||
"RPS Excess Heated Coolant",
|
||||
"RPS Excess Waste",
|
||||
"RPS Insufficient Fuel",
|
||||
"RPS PLC Fault",
|
||||
"RPS Supervisor Timeout",
|
||||
"Unit Alarm",
|
||||
"Unit Emergency Cool. Valve"
|
||||
local PORT_DESC_MAP = {
|
||||
{ IO.F_SCRAM, "Facility SCRAM" },
|
||||
{ IO.F_ACK, "Facility Acknowledge" },
|
||||
{ IO.R_SCRAM, "Reactor SCRAM" },
|
||||
{ IO.R_RESET, "Reactor RPS Reset" },
|
||||
{ IO.R_ENABLE, "Reactor Enable" },
|
||||
{ IO.U_ACK, "Unit Acknowledge" },
|
||||
{ IO.F_ALARM, "Facility Alarm (high prio)" },
|
||||
{ IO.F_ALARM_ANY, "Facility Alarm (any)" },
|
||||
{ IO.F_MATRIX_LOW, "Induction Matrix < " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_LOW) .. "%" },
|
||||
{ IO.F_MATRIX_HIGH, "Induction Matrix > " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_HIGH) .. "%" },
|
||||
{ IO.F_MATRIX_CHG, "Induction Matrix Charge %" },
|
||||
{ IO.WASTE_PU, "Waste Plutonium Valve" },
|
||||
{ IO.WASTE_PO, "Waste Polonium Valve" },
|
||||
{ IO.WASTE_POPL, "Waste Po Pellets Valve" },
|
||||
{ IO.WASTE_AM, "Waste Antimatter Valve" },
|
||||
{ IO.R_ACTIVE, "Reactor Active" },
|
||||
{ IO.R_AUTO_CTRL, "Reactor in Auto Control" },
|
||||
{ IO.R_SCRAMMED, "RPS Tripped" },
|
||||
{ IO.R_AUTO_SCRAM, "RPS Auto SCRAM" },
|
||||
{ IO.R_HIGH_DMG, "RPS High Damage" },
|
||||
{ IO.R_HIGH_TEMP, "RPS High Temperature" },
|
||||
{ IO.R_LOW_COOLANT, "RPS Low Coolant" },
|
||||
{ IO.R_EXCESS_HC, "RPS Excess Heated Coolant" },
|
||||
{ IO.R_EXCESS_WS, "RPS Excess Waste" },
|
||||
{ IO.R_INSUFF_FUEL, "RPS Insufficient Fuel" },
|
||||
{ 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)
|
||||
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)
|
||||
|
||||
-- changes to the config data/format to let the user know
|
||||
@ -1167,14 +1171,17 @@ 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)}
|
||||
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)}
|
||||
|
||||
for i = 1, rsio.NUM_PORTS do
|
||||
local name = rsio.to_string(i)
|
||||
local io_dir = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, "[in]", "[out]")
|
||||
local btn_color = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
||||
local p = PORT_DESC_MAP[i][1]
|
||||
local name = rsio.to_string(p)
|
||||
local io_dir = util.trinary(rsio.get_io_dir(p) == rsio.IO_DIR.IN, "[in]", "[out]")
|
||||
local btn_color = util.trinary(rsio.get_io_dir(p) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
||||
|
||||
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=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
|
||||
|
||||
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}
|
||||
|
@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
||||
local sps_rtu = require("rtu.dev.sps_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_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||
|
@ -66,6 +66,18 @@ constants.ALARM_LIMITS = alarms
|
||||
|
||||
--#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
|
||||
|
||||
-- milliseconds until coolant flow is assumed to be stable enough to enable certain coolant checks
|
||||
|
@ -52,6 +52,8 @@ local IO_PORT = {
|
||||
-- facility
|
||||
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_MATRIX_LOW = 27, -- active high, induction matrix charge less than
|
||||
F_MATRIX_HIGH = 28, -- active high, induction matrix charge high
|
||||
|
||||
-- waste
|
||||
WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
|
||||
@ -75,17 +77,27 @@ local IO_PORT = {
|
||||
|
||||
-- unit outputs
|
||||
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_DIR = IO_DIR
|
||||
rsio.IO_MODE = IO_MODE
|
||||
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
|
||||
|
||||
assert(rsio.NUM_PORTS == (rsio.NUM_DIG_PORTS + rsio.NUM_ANA_PORTS), "port counts inconsistent")
|
||||
|
||||
local dup_chk = {}
|
||||
for _, v in pairs(IO_PORT) do
|
||||
assert(dup_chk[v] ~= true, "duplicate in port list")
|
||||
@ -96,64 +108,45 @@ assert(#dup_chk == rsio.NUM_PORTS, "port list malformed")
|
||||
|
||||
--#endregion
|
||||
|
||||
--#region Utility Functions
|
||||
--#region Utility Functions and Attribute Tables
|
||||
|
||||
local PORT_NAMES = {
|
||||
"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"
|
||||
}
|
||||
local IO = IO_PORT
|
||||
|
||||
-- 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 = {
|
||||
IO_MODE.DIGITAL_IN, -- F_SCRAM
|
||||
IO_MODE.DIGITAL_IN, -- F_ACK
|
||||
IO_MODE.DIGITAL_IN, -- R_SCRAM
|
||||
IO_MODE.DIGITAL_IN, -- R_RESET
|
||||
IO_MODE.DIGITAL_IN, -- R_ENABLE
|
||||
IO_MODE.DIGITAL_IN, -- U_ACK
|
||||
IO_MODE.DIGITAL_OUT, -- F_ALARM
|
||||
IO_MODE.DIGITAL_OUT, -- F_ALARM_ANY
|
||||
IO_MODE.DIGITAL_OUT, -- WASTE_PU
|
||||
IO_MODE.DIGITAL_OUT, -- WASTE_PO
|
||||
IO_MODE.DIGITAL_OUT, -- WASTE_POPL
|
||||
IO_MODE.DIGITAL_OUT, -- WASTE_AM
|
||||
IO_MODE.DIGITAL_OUT, -- R_ACTIVE
|
||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_CTRL
|
||||
IO_MODE.DIGITAL_OUT, -- R_SCRAMMED
|
||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_SCRAM
|
||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_DMG
|
||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_TEMP
|
||||
IO_MODE.DIGITAL_OUT, -- R_LOW_COOLANT
|
||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_HC
|
||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_WS
|
||||
IO_MODE.DIGITAL_OUT, -- R_INSUFF_FUEL
|
||||
IO_MODE.DIGITAL_OUT, -- R_PLC_FAULT
|
||||
IO_MODE.DIGITAL_OUT, -- R_PLC_TIMEOUT
|
||||
IO_MODE.DIGITAL_OUT, -- U_ALARM
|
||||
IO_MODE.DIGITAL_OUT -- U_EMER_COOL
|
||||
[IO.F_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||
[IO.F_ACK] = IO_MODE.DIGITAL_IN,
|
||||
[IO.R_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||
[IO.R_RESET] = IO_MODE.DIGITAL_IN,
|
||||
[IO.R_ENABLE] = IO_MODE.DIGITAL_IN,
|
||||
[IO.U_ACK] = IO_MODE.DIGITAL_IN,
|
||||
[IO.F_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.F_ALARM_ANY] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.F_MATRIX_LOW] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.F_MATRIX_HIGH] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.WASTE_PU] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.WASTE_PO] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.WASTE_POPL] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.WASTE_AM] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_ACTIVE] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_AUTO_CTRL] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_SCRAMMED] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_AUTO_SCRAM] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_HIGH_DMG] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_HIGH_TEMP] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_LOW_COOLANT] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_EXCESS_HC] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_EXCESS_WS] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_INSUFF_FUEL] = IO_MODE.DIGITAL_OUT,
|
||||
[IO.R_PLC_FAULT] = IO_MODE.DIGITAL_OUT,
|
||||
[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")
|
||||
@ -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
|
||||
local RS_DIO_MAP = {
|
||||
-- F_SCRAM
|
||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||
-- F_ACK
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||
[IO.F_SCRAM] = { _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 },
|
||||
|
||||
-- R_SCRAM
|
||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||
-- R_RESET
|
||||
{ _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 },
|
||||
[IO.R_SCRAM] = { _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 },
|
||||
[IO.R_ENABLE] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||
|
||||
-- U_ACK
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||
[IO.U_ACK] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||
|
||||
-- F_ALARM
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- F_ALARM_ANY
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
[IO.F_ALARM] = { _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 },
|
||||
[IO.F_MATRIX_LOW] = { _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
|
||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||
-- WASTE_PO
|
||||
{ _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 },
|
||||
[IO.WASTE_PU] = { _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 },
|
||||
[IO.WASTE_POPL] = { _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 },
|
||||
|
||||
-- R_ACTIVE
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- R_AUTO_CTRL
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- R_SCRAMMED
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- R_AUTO_SCRAM
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- R_HIGH_DMG
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- R_HIGH_TEMP
|
||||
{ _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 },
|
||||
[IO.R_ACTIVE] = { _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 },
|
||||
[IO.R_SCRAMMED] = { _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 },
|
||||
[IO.R_HIGH_DMG] = { _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 },
|
||||
[IO.R_LOW_COOLANT] = { _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 },
|
||||
[IO.R_EXCESS_WS] = { _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 },
|
||||
[IO.R_PLC_FAULT] = { _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 },
|
||||
|
||||
-- U_ALARM
|
||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
-- U_EMER_COOL
|
||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
||||
[IO.U_ALARM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||
[IO.U_EMER_COOL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
||||
}
|
||||
|
||||
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
|
||||
---@nodiscard
|
||||
---@param port IO_PORT
|
||||
---@return IO_DIR
|
||||
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
|
||||
end
|
||||
|
||||
@ -310,6 +280,13 @@ end
|
||||
|
||||
--#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
|
||||
---@nodiscard
|
||||
---@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
|
||||
---@return IO_LVL|false
|
||||
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
|
||||
else
|
||||
return RS_DIO_MAP[port]._out(active)
|
||||
@ -343,9 +320,7 @@ end
|
||||
---@param level IO_LVL logic level
|
||||
---@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)
|
||||
if not util.is_int(port) then
|
||||
return nil
|
||||
elseif level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
||||
if (not rsio.is_digital(port)) or level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
||||
return nil
|
||||
else
|
||||
return RS_DIO_MAP[port]._in(level)
|
||||
@ -356,6 +331,13 @@ end
|
||||
|
||||
--#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
|
||||
---@nodiscard
|
||||
---@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 min number minimum 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)
|
||||
local scaled_value = (value - min) / (max - min)
|
||||
return math.floor(scaled_value * 15)
|
||||
|
@ -22,7 +22,7 @@ local t_pack = table.pack
|
||||
local util = {}
|
||||
|
||||
-- scada-common version
|
||||
util.version = "1.2.2"
|
||||
util.version = "1.3.0"
|
||||
|
||||
util.TICK_TIME_S = 0.05
|
||||
util.TICK_TIME_MS = 50
|
||||
|
@ -774,6 +774,16 @@ 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_ANY, has_any_alarm)
|
||||
|
||||
-- update induction matrix related outputs
|
||||
if self.induction[1] ~= nil then
|
||||
local matrix = self.induction[1] ---@type unit_session
|
||||
local db = matrix.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
|
||||
|
||||
--#endregion
|
||||
|
@ -2,6 +2,8 @@
|
||||
-- Redstone RTU Session I/O Controller
|
||||
--
|
||||
|
||||
local rsio = require("scada-common.rsio")
|
||||
|
||||
local rsctl = {}
|
||||
|
||||
-- create a new redstone RTU I/O controller
|
||||
@ -46,6 +48,19 @@ function rsctl.new(redstone_rtus)
|
||||
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
|
||||
end
|
||||
|
||||
|
@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local svsessions = require("supervisor.session.svsessions")
|
||||
|
||||
local SUPERVISOR_VERSION = "v1.3.6"
|
||||
local SUPERVISOR_VERSION = "v1.3.7"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
@ -5,13 +5,25 @@ local util = require("scada-common.util")
|
||||
|
||||
local testutils = require("test.testutils")
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
|
||||
local IO = rsio.IO
|
||||
local IO_LVL = rsio.IO_LVL
|
||||
local IO_MODE = rsio.IO_MODE
|
||||
|
||||
local print = util.print
|
||||
local println = util.println
|
||||
|
||||
-- list of inverted digital signals<br>
|
||||
-- just using the key for a quick lookup, value need to be not nil
|
||||
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("")
|
||||
|
||||
@ -50,8 +62,8 @@ testutils.pause()
|
||||
|
||||
println(">>> checking invalid ports:")
|
||||
|
||||
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "")
|
||||
testutils.test_func_nil("rsio.to_string", rsio.to_string, "")
|
||||
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "UNKNOWN")
|
||||
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_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(...): ")
|
||||
|
||||
-- check input ports
|
||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.LOW) == true, "IO_F_SCRAM_HIGH")
|
||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.HIGH) == false, "IO_F_SCRAM_LOW")
|
||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.LOW) == true, "IO_R_SCRAM_HIGH")
|
||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.HIGH) == false, "IO_R_SCRAM_LOW")
|
||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.LOW) == false, "IO_R_ENABLE_HIGH")
|
||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.HIGH) == true, "IO_R_ENABLE_LOW")
|
||||
-- check all digital ports
|
||||
for i = 1, rsio.NUM_PORTS do
|
||||
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||
local high = DIG_INV[i] == nil
|
||||
assert(rsio.digital_is_active(i, IO_LVL.LOW) == not high, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||
assert(rsio.digital_is_active(i, IO_LVL.HIGH) == high, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||
end
|
||||
end
|
||||
|
||||
-- non-inputs should always return LOW
|
||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.LOW) == false, "IO_OUT_READ_LOW")
|
||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.HIGH) == false, "IO_OUT_READ_HIGH")
|
||||
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.LOW) == nil, "ANA_DIG_READ_LOW")
|
||||
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.HIGH) == nil, "ANA_DIG_READ_HIGH")
|
||||
|
||||
println("PASS")
|
||||
|
||||
-- check output ports
|
||||
-- check digital write
|
||||
|
||||
print("rsio.digital_write(...): ")
|
||||
print("rsio.digital_write_active(...): ")
|
||||
|
||||
-- check output ports
|
||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.LOW, "IO_F_ALARM_LOW")
|
||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.HIGH, "IO_F_ALARM_HIGH")
|
||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.HIGH, "IO_WASTE_PU_HIGH")
|
||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.LOW, "IO_WASTE_PU_LOW")
|
||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.HIGH, "IO_WASTE_PO_HIGH")
|
||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.LOW, "IO_WASTE_PO_LOW")
|
||||
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")
|
||||
-- check all digital ports
|
||||
for i = 1, rsio.NUM_PORTS do
|
||||
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||
local high = DIG_INV[i] == nil
|
||||
assert(rsio.digital_write_active(i, not high) == IO_LVL.LOW, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||
assert(rsio.digital_write_active(i, high) == IO_LVL.HIGH, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||
end
|
||||
end
|
||||
|
||||
-- non-outputs should always return false
|
||||
assert(rsio.digital_write_active(IO.F_SCRAM, false) == IO_LVL.LOW, "IO_IN_WRITE_FALSE")
|
||||
assert(rsio.digital_write_active(IO.F_SCRAM, true) == IO_LVL.LOW, "IO_IN_WRITE_TRUE")
|
||||
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, true) == false, "ANA_DIG_WRITE_TRUE")
|
||||
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, false) == false, "ANA_DIG_WRITE_FALSE")
|
||||
|
||||
println("PASS")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user