From 74ae57324b43a1517312f650fb663a3a20009cec Mon Sep 17 00:00:00 2001 From: Mikayla Fischler Date: Wed, 23 Mar 2022 15:36:14 -0400 Subject: [PATCH] redstone I/O rework --- rtu/dev/redstone.lua | 33 +++++---- scada-common/rsio.lua | 152 +++++++++++++++++++++++++++++++++--------- 2 files changed, 141 insertions(+), 44 deletions(-) diff --git a/rtu/dev/redstone.lua b/rtu/dev/redstone.lua index 0ec1283..da2f7e2 100644 --- a/rtu/dev/redstone.lua +++ b/rtu/dev/redstone.lua @@ -1,6 +1,10 @@ -- #REQUIRES rtu.lua +-- #REQUIRES rsio.lua -- note: this RTU makes extensive use of the programming concept of closures +local digital_read = rsio.digital_read +local digital_is_active = rsio.digital_is_active + function redstone_rtu() local self = { rtu = rtu_init() @@ -10,56 +14,57 @@ function redstone_rtu() return self.rtu end - local link_di = function (side, color) + local link_di = function (channel, side, color) local f_read = nil if color then f_read = function () - return rs.testBundledInput(side, color) + return digital_read(rs.testBundledInput(side, color)) end else f_read = function () - return rs.getInput(side) + return digital_read(rs.getInput(side)) end end self.rtu.connect_di(f_read) end - local link_do = function (side, color) + local link_do = function (channel, side, color) local f_read = nil local f_write = nil if color then f_read = function () - return colors.test(rs.getBundledOutput(side), color) + return digital_read(colors.test(rs.getBundledOutput(side), color)) end - f_write = function (value) + f_write = function (level) local output = rs.getBundledOutput(side) + local active = digital_is_active(channel, level) - if value then - colors.combine(output, value) + if active then + colors.combine(output, color) else - colors.subtract(output, value) + colors.subtract(output, color) end rs.setBundledOutput(side, output) end else f_read = function () - return rs.getOutput(side) + return digital_read(rs.getOutput(side)) end - f_write = function (value) - rs.setOutput(side, color) + f_write = function (level) + rs.setOutput(side, digital_is_active(channel, level)) end end self.rtu.connect_coil(f_read, f_write) end - local link_ai = function (side) + local link_ai = function (channel, side) self.rtu.connect_input_reg( function () return rs.getAnalogInput(side) @@ -67,7 +72,7 @@ function redstone_rtu() ) end - local link_ao = function (side) + local link_ao = function (channel, side) self.rtu.connect_holding_reg( function () return rs.getAnalogOutput(side) diff --git a/scada-common/rsio.lua b/scada-common/rsio.lua index 468a1fa..9dbe9ef 100644 --- a/scada-common/rsio.lua +++ b/scada-common/rsio.lua @@ -3,6 +3,18 @@ IO_LVL = { HIGH = 1 } +IO_DIR = { + IN = 0, + OUT = 1 +} + +IO_MODE = { + DIGITAL_OUT = 0, + DIGITAL_IN = 1, + ANALOG_OUT = 2, + ANALOG_IN = 3 +} + RS_IO = { -- digital inputs -- @@ -41,6 +53,53 @@ RS_IO = { A_T_FLOW_RATE = 21 -- turbine flow rate percentage } +function to_string(channel) + local names = { + "F_SCRAM", + "F_AE2_LIVE", + "R_SCRAM", + "R_ENABLE", + "WASTE_PO", + "WASTE_PU", + "WASTE_AM", + "R_SCRAMMED", + "R_AUTO_SCRAM", + "R_ACTIVE", + "R_AUTO_CTRL", + "R_DMG_CRIT", + "R_HIGH_TEMP", + "R_NO_COOLANT", + "R_EXCESS_HC", + "R_EXCESS_WS", + "R_INSUFF_FUEL", + "R_PLC_TIMEOUT", + "A_R_BURN_RATE", + "A_B_BOIL_RATE", + "A_T_FLOW_RATE" + } + + if channel > 0 and channel <= #names then + return names[channel] + else + return "" + end +end + +function is_valid_channel(channel) + return channel > 0 and channel <= A_T_FLOW_RATE +end + +function is_valid_side(side) + for _, s in pairs(redstone.getSides()) do + if s == side then return true end + end + return false +end + +function is_color(color) + return (color > 0) and (bit.band(color, (color - 1)) == 0); +end + local _TRINARY = function (cond, t, f) if cond then return t else return f end end local _DI_ACTIVE_HIGH = function (level) return level == IO_LVL.HIGH end @@ -48,47 +107,80 @@ local _DI_ACTIVE_LOW = function (level) return level == IO_LVL.LOW end local _DO_ACTIVE_HIGH = function (on) return _TRINARY(on, IO_LVL.HIGH, IO_LVL.LOW) end local _DO_ACTIVE_LOW = function (on) return _TRINARY(on, IO_LVL.LOW, IO_LVL.HIGH) end +-- I/O mappings to I/O function and I/O mode local RS_DIO_MAP = { -- F_SCRAM - _DI_ACTIVE_LOW, + { _f = _DI_ACTIVE_LOW, mode = IO_DIR.IN }, -- F_AE2_LIVE - _DI_ACTIVE_HIGH, + { _f = _DI_ACTIVE_HIGH, mode = IO_DIR.IN }, -- R_SCRAM - _DI_ACTIVE_LOW, + { _f = _DI_ACTIVE_LOW, mode = IO_DIR.IN }, -- R_ENABLE - _DI_ACTIVE_HIGH, + { _f = _DI_ACTIVE_HIGH, mode = IO_DIR.IN }, -- WASTE_PO - _DO_ACTIVE_LOW, + { _f = _DO_ACTIVE_LOW, mode = IO_DIR.OUT }, -- WASTE_PU - _DO_ACTIVE_LOW, + { _f = _DO_ACTIVE_LOW, mode = IO_DIR.OUT }, -- WASTE_AM - _DO_ACTIVE_LOW, + { _f = _DO_ACTIVE_LOW, mode = IO_DIR.OUT }, -- R_SCRAMMED - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_AUTO_SCRAM - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_ACTIVE - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_AUTO_CTRL - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_DMG_CRIT - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_HIGH_TEMP - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_NO_COOLANT - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_EXCESS_HC - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_EXCESS_WS - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_INSUFF_FUEL - _DO_ACTIVE_HIGH, + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT }, -- R_PLC_TIMEOUT - _DO_ACTIVE_HIGH + { _f = _DO_ACTIVE_HIGH, mode = IO_DIR.OUT } } +function get_io_mode(channel) + local modes = { + IO_MODE.DIGITAL_IN, -- F_SCRAM + IO_MODE.DIGITAL_IN, -- F_AE2_LIVE + IO_MODE.DIGITAL_IN, -- R_SCRAM + IO_MODE.DIGITAL_IN, -- R_ENABLE + IO_MODE.DIGITAL_OUT, -- WASTE_PO + IO_MODE.DIGITAL_OUT, -- WASTE_PU + IO_MODE.DIGITAL_OUT, -- WASTE_AM + IO_MODE.DIGITAL_OUT, -- R_SCRAMMED + IO_MODE.DIGITAL_OUT, -- R_AUTO_SCRAM + IO_MODE.DIGITAL_OUT, -- R_ACTIVE + IO_MODE.DIGITAL_OUT, -- R_AUTO_CTRL + IO_MODE.DIGITAL_OUT, -- R_DMG_CRIT + IO_MODE.DIGITAL_OUT, -- R_HIGH_TEMP + IO_MODE.DIGITAL_OUT, -- R_NO_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_TIMEOUT + IO_MODE.ANALOG_OUT, -- A_R_BURN_RATE + IO_MODE.ANALOG_OUT, -- A_B_BOIL_RATE + IO_MODE.ANALOG_OUT -- A_T_FLOW_RATE + } + + if channel > 0 and channel <= #modes then + return modes[channel] + else + return IO_MODE.ANALOG_IN + end +end + -- get digital IO level reading -function digital_input_read(rs_value) +function digital_read(rs_value) if rs_value then return IO_LVL.HIGH else @@ -96,20 +188,20 @@ function digital_input_read(rs_value) end end --- returns true if the level corresponds to active -function digital_input_is_active(channel, level) - if channel > RS_IO.R_ENABLE then - return false - else - return RS_DIO_MAP[channel](level) - end -end - -- returns the level corresponding to active -function digital_output_write(channel, active) +function digital_write(channel, active) if channel < RS_IO.WASTE_PO or channel > RS_IO.R_PLC_TIMEOUT then return IO_LVL.LOW else - return RS_DIO_MAP[channel](level) + return RS_DIO_MAP[channel]._f(level) + end +end + +-- returns true if the level corresponds to active +function digital_is_active(channel, level) + if channel > RS_IO.R_ENABLE or channel > RS_IO.R_PLC_TIMEOUT then + return false + else + return RS_DIO_MAP[channel]._f(level) end end