mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
commit
bfa1f4d0c6
@ -18,7 +18,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
local function println(message) print(tostring(message)) end
|
||||
local function print(message) term.write(tostring(message)) end
|
||||
|
||||
local CCMSI_VERSION = "v1.12"
|
||||
local CCMSI_VERSION = "v1.12a"
|
||||
|
||||
local install_dir = "/.install-cache"
|
||||
local manifest_path = "https://mikaylafischler.github.io/cc-mek-scada/manifests/"
|
||||
@ -345,8 +345,7 @@ elseif mode == "install" or mode == "update" then
|
||||
if mode == "install" then
|
||||
println("Installing " .. app .. " files...")
|
||||
elseif mode == "update" then
|
||||
if app == "supervisor" or app == "coordinator" or app == "pocket" then
|
||||
println("Updating " .. app .. " files... (keeping old config.lua)")
|
||||
if app == "coordinator" or app == "pocket" then println("Updating " .. app .. " files... (keeping old config.lua)")
|
||||
else println("Updating " .. app .. " files...") end
|
||||
end
|
||||
white()
|
||||
|
@ -4,12 +4,12 @@ if fs.exists("reactor-plc/configure.lua") then
|
||||
require("reactor-plc.configure").configure()
|
||||
elseif fs.exists("rtu/configure.lua") then
|
||||
require("rtu.configure").configure()
|
||||
elseif fs.exists("supervisor/startup.lua") then
|
||||
print("CONFIGURE> SUPERVISOR CONFIGURATOR NOT YET IMPLEMENTED IN BETA")
|
||||
elseif fs.exists("supervisor/configure.lua") then
|
||||
require("supervisor.configure").configure()
|
||||
elseif fs.exists("coordinator/startup.lua") then
|
||||
print("CONFIGURE> COORDINATOR CONFIGURATOR NOT YET IMPLEMENTED IN BETA")
|
||||
print("CONFIGURE> coordinator configurator not yet implemented (use 'edit coordinator/config.lua' to configure)")
|
||||
elseif fs.exists("pocket/startup.lua") then
|
||||
print("CONFIGURE> POCKET CONFIGURATOR NOT YET IMPLEMENTED IN BETA")
|
||||
print("CONFIGURE> pocket configurator not yet implemented (use 'edit pocket/config.lua' to configure)")
|
||||
else
|
||||
print("CONFIGURE> NO CONFIGURATOR FOUND")
|
||||
print("CONFIGURE> EXIT")
|
||||
|
@ -110,9 +110,9 @@ function iocontrol.init(conf, comms)
|
||||
-- determine tank information
|
||||
if io.facility.tank_mode == 0 then
|
||||
io.facility.tank_defs = {}
|
||||
-- on facility tank mode 0, setup tank defs to match unit TANK option
|
||||
-- on facility tank mode 0, setup tank defs to match unit tank option
|
||||
for i = 1, conf.num_units do
|
||||
io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TANK, 1, 0)
|
||||
io.facility.tank_defs[i] = util.trinary(conf.cooling.r_cool[i].TankConnection, 1, 0)
|
||||
end
|
||||
|
||||
io.facility.tank_list = { table.unpack(io.facility.tank_defs) }
|
||||
@ -214,7 +214,7 @@ function iocontrol.init(conf, comms)
|
||||
num_boilers = 0,
|
||||
num_turbines = 0,
|
||||
num_snas = 0,
|
||||
has_tank = conf.cooling.r_cool[i].TANK,
|
||||
has_tank = conf.cooling.r_cool[i].TankConnection,
|
||||
|
||||
control_state = false,
|
||||
burn_rate_cmd = 0.0,
|
||||
@ -295,13 +295,13 @@ function iocontrol.init(conf, comms)
|
||||
end
|
||||
|
||||
-- create boiler tables
|
||||
for _ = 1, conf.cooling.r_cool[i].BOILERS do
|
||||
for _ = 1, conf.cooling.r_cool[i].BoilerCount do
|
||||
table.insert(entry.boiler_ps_tbl, psil.create())
|
||||
table.insert(entry.boiler_data_tbl, {})
|
||||
end
|
||||
|
||||
-- create turbine tables
|
||||
for _ = 1, conf.cooling.r_cool[i].TURBINES do
|
||||
for _ = 1, conf.cooling.r_cool[i].TurbineCount do
|
||||
table.insert(entry.turbine_ps_tbl, psil.create())
|
||||
table.insert(entry.turbine_data_tbl, {})
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ local sounder = require("coordinator.sounder")
|
||||
|
||||
local apisessions = require("coordinator.session.apisessions")
|
||||
|
||||
local COORDINATOR_VERSION = "v1.0.18"
|
||||
local COORDINATOR_VERSION = "v1.1.0"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
|
||||
|
||||
local core = {}
|
||||
|
||||
core.version = "2.0.9"
|
||||
core.version = "2.1.0"
|
||||
|
||||
core.flasher = flasher
|
||||
core.events = events
|
||||
|
@ -1,5 +1,7 @@
|
||||
-- Numeric Value Entry Graphics Element
|
||||
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local core = require("graphics.core")
|
||||
local element = require("graphics.element")
|
||||
|
||||
@ -8,9 +10,11 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
|
||||
|
||||
---@class number_field_args
|
||||
---@field default? number default value, defaults to 0
|
||||
---@field min? number minimum, forced on unfocus
|
||||
---@field max? number maximum, forced on unfocus
|
||||
---@field max_digits? integer maximum number of digits, defaults to width
|
||||
---@field min? number minimum, enforced on unfocus
|
||||
---@field max? number maximum, enforced on unfocus
|
||||
---@field max_chars? integer maximum number of characters, defaults to width
|
||||
---@field max_int_digits? integer maximum number of integer digits, enforced on unfocus
|
||||
---@field max_frac_digits? integer maximum number of fractional digits, enforced on unfocus
|
||||
---@field allow_decimal? boolean true to allow decimals
|
||||
---@field allow_negative? boolean true to allow negative numbers
|
||||
---@field dis_fg_bg? cpair foreground/background colors when disabled
|
||||
@ -26,6 +30,9 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
|
||||
---@param args number_field_args
|
||||
---@return graphics_element element, element_id id
|
||||
local function number_field(args)
|
||||
element.assert(args.max_int_digits == nil or (util.is_int(args.max_int_digits) and args.max_int_digits > 0), "max_int_digits must be an integer greater than zero if supplied")
|
||||
element.assert(args.max_frac_digits == nil or (util.is_int(args.max_frac_digits) and args.max_frac_digits > 0), "max_frac_digits must be an integer greater than zero if supplied")
|
||||
|
||||
args.height = 1
|
||||
args.can_focus = true
|
||||
|
||||
@ -34,13 +41,13 @@ local function number_field(args)
|
||||
|
||||
local has_decimal = false
|
||||
|
||||
args.max_digits = args.max_digits or e.frame.w
|
||||
args.max_chars = args.max_chars or e.frame.w
|
||||
|
||||
-- set initial value
|
||||
e.value = "" .. (args.default or 0)
|
||||
|
||||
-- make an interactive field manager
|
||||
local ifield = core.new_ifield(e, args.max_digits, args.fg_bg, args.dis_fg_bg)
|
||||
local ifield = core.new_ifield(e, args.max_chars, args.fg_bg, args.dis_fg_bg)
|
||||
|
||||
-- handle mouse interaction
|
||||
---@param event mouse_interaction mouse event
|
||||
@ -62,7 +69,7 @@ local function number_field(args)
|
||||
-- handle keyboard interaction
|
||||
---@param event key_interaction key event
|
||||
function e.handle_key(event)
|
||||
if event.type == KEY_CLICK.CHAR and string.len(e.value) < args.max_digits then
|
||||
if event.type == KEY_CLICK.CHAR and string.len(e.value) < args.max_chars then
|
||||
if tonumber(event.name) then
|
||||
if e.value == 0 then e.value = "" end
|
||||
ifield.try_insert_char(event.name)
|
||||
@ -127,6 +134,37 @@ local function number_field(args)
|
||||
local min = tonumber(args.min)
|
||||
|
||||
if type(val) == "number" then
|
||||
if args.max_int_digits or args.max_frac_digits then
|
||||
local str = e.value
|
||||
local ceil = false
|
||||
|
||||
if string.find(str, "-") then str = string.sub(e.value, 2) end
|
||||
local parts = util.strtok(str, ".")
|
||||
|
||||
if parts[1] and args.max_int_digits then
|
||||
if string.len(parts[1]) > args.max_int_digits then
|
||||
parts[1] = string.rep("9", args.max_int_digits)
|
||||
ceil = true
|
||||
end
|
||||
end
|
||||
|
||||
if args.allow_decimal and args.max_frac_digits then
|
||||
if ceil then
|
||||
parts[2] = string.rep("9", args.max_frac_digits)
|
||||
elseif parts[2] and (string.len(parts[2]) > args.max_frac_digits) then
|
||||
-- add a half of the highest precision fractional value in order to round using floor
|
||||
local scaled = math.fmod(val, 1) * (10 ^ (args.max_frac_digits))
|
||||
local value = math.floor(scaled + 0.5)
|
||||
local unscaled = value * (10 ^ (-args.max_frac_digits))
|
||||
parts[2] = string.sub(tostring(unscaled), 3) -- remove starting "0."
|
||||
end
|
||||
end
|
||||
|
||||
if parts[2] then parts[2] = "." .. parts[2] else parts[2] = "" end
|
||||
|
||||
val = tonumber((parts[1] or "") .. parts[2])
|
||||
end
|
||||
|
||||
if type(args.max) == "number" and val > max then
|
||||
e.value = "" .. max
|
||||
ifield.nav_start()
|
||||
|
@ -3,9 +3,9 @@
|
||||
--
|
||||
|
||||
local log = require("scada-common.log")
|
||||
local rsio = require("scada-common.rsio")
|
||||
local tcd = require("scada-common.tcd")
|
||||
local util = require("scada-common.util")
|
||||
local rsio = require("scada-common.rsio")
|
||||
|
||||
local core = require("graphics.core")
|
||||
|
||||
@ -34,7 +34,8 @@ local RIGHT = core.ALIGN.RIGHT
|
||||
|
||||
-- changes to the config data/format to let the user know
|
||||
local changes = {
|
||||
{"v1.6.2", { "AuthKey minimum length is now 8 (if set)" } }
|
||||
{"v1.6.2", { "AuthKey minimum length is now 8 (if set)" } },
|
||||
{"v1.6.8", { "ConnTimeout can now have a fractional part" } }
|
||||
}
|
||||
|
||||
---@class plc_configurator
|
||||
@ -92,13 +93,13 @@ local tmp_cfg = {
|
||||
Networked = false,
|
||||
UnitID = 0,
|
||||
EmerCoolEnable = false,
|
||||
EmerCoolSide = nil,
|
||||
EmerCoolColor = nil,
|
||||
SVR_Channel = nil,
|
||||
PLC_Channel = nil,
|
||||
ConnTimeout = nil,
|
||||
TrustedRange = nil,
|
||||
AuthKey = nil,
|
||||
EmerCoolSide = nil, ---@type string|nil
|
||||
EmerCoolColor = nil, ---@type color|nil
|
||||
SVR_Channel = nil, ---@type integer
|
||||
PLC_Channel = nil, ---@type integer
|
||||
ConnTimeout = nil, ---@type number
|
||||
TrustedRange = nil, ---@type number
|
||||
AuthKey = nil, ---@type string|nil
|
||||
LogMode = 0,
|
||||
LogPath = "",
|
||||
LogDebug = false,
|
||||
@ -239,7 +240,7 @@ local function config_view(display)
|
||||
TextBox{parent=plc_c_2,x=1,y=3,height=3,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
TextBox{parent=plc_c_2,x=1,y=6,height=1,text="Unit #"}
|
||||
local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_digits=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg}
|
||||
local u_id = NumberField{parent=plc_c_2,x=7,y=6,width=5,max_chars=3,default=ini_cfg.UnitID,min=1,fg_bg=bw_fg_bg}
|
||||
|
||||
local u_id_err = TextBox{parent=plc_c_2,x=8,y=14,height=1,width=35,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||
|
||||
@ -332,12 +333,12 @@ local function config_view(display)
|
||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||
|
||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Connection Timeout"}
|
||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,fg_bg=bw_fg_bg}
|
||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
TextBox{parent=net_c_2,x=1,y=8,height=1,text="Trusted Range"}
|
||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_digits=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
local p2_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||
|
@ -58,7 +58,7 @@ function plc.load_config()
|
||||
if config.Networked == true then
|
||||
cfv.assert_channel(config.SVR_Channel)
|
||||
cfv.assert_channel(config.PLC_Channel)
|
||||
cfv.assert_type_int(config.ConnTimeout)
|
||||
cfv.assert_type_num(config.ConnTimeout)
|
||||
cfv.assert_min(config.ConnTimeout, 2)
|
||||
cfv.assert_type_num(config.TrustedRange)
|
||||
cfv.assert_min(config.TrustedRange, 0)
|
||||
|
@ -18,7 +18,7 @@ local plc = require("reactor-plc.plc")
|
||||
local renderer = require("reactor-plc.renderer")
|
||||
local threads = require("reactor-plc.threads")
|
||||
|
||||
local R_PLC_VERSION = "v1.6.7"
|
||||
local R_PLC_VERSION = "v1.6.9"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
@ -3,10 +3,10 @@
|
||||
--
|
||||
|
||||
local log = require("scada-common.log")
|
||||
local ppm = require("scada-common.ppm")
|
||||
local rsio = require("scada-common.rsio")
|
||||
local tcd = require("scada-common.tcd")
|
||||
local util = require("scada-common.util")
|
||||
local ppm = require("scada-common.ppm")
|
||||
|
||||
local core = require("graphics.core")
|
||||
|
||||
@ -72,7 +72,9 @@ assert(#PORT_DESC == rsio.NUM_PORTS)
|
||||
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
||||
|
||||
-- changes to the config data/format to let the user know
|
||||
local changes = {}
|
||||
local changes = {
|
||||
{"v1.7.9", { "ConnTimeout can now have a fractional part" } }
|
||||
}
|
||||
|
||||
---@class rtu_rs_definition
|
||||
---@field unit integer|nil
|
||||
@ -172,11 +174,11 @@ local tmp_cfg = {
|
||||
SpeakerVolume = 1.0,
|
||||
Peripherals = {},
|
||||
Redstone = {},
|
||||
SVR_Channel = nil,
|
||||
RTU_Channel = nil,
|
||||
ConnTimeout = nil,
|
||||
TrustedRange = nil,
|
||||
AuthKey = nil,
|
||||
SVR_Channel = nil, ---@type integer
|
||||
RTU_Channel = nil, ---@type integer
|
||||
ConnTimeout = nil, ---@type number
|
||||
TrustedRange = nil, ---@type number
|
||||
AuthKey = nil, ---@type string|nil
|
||||
LogMode = 0,
|
||||
LogPath = "",
|
||||
LogDebug = false
|
||||
@ -331,7 +333,7 @@ local function config_view(display)
|
||||
TextBox{parent=spkr_c,x=1,y=1,height=2,text="Speakers can be connected to this RTU gateway without RTU unit configuration entries."}
|
||||
TextBox{parent=spkr_c,x=1,y=4,height=3,text="You can change the speaker audio volume from the default. The range is 0.0 to 3.0, where 1.0 is standard volume."}
|
||||
|
||||
local s_vol = NumberField{parent=spkr_c,x=1,y=8,width=9,max_digits=7,allow_decimal=true,default=ini_cfg.SpeakerVolume,min=0,max=3,fg_bg=bw_fg_bg}
|
||||
local s_vol = NumberField{parent=spkr_c,x=1,y=8,width=9,max_chars=7,allow_decimal=true,default=ini_cfg.SpeakerVolume,min=0,max=3,fg_bg=bw_fg_bg}
|
||||
|
||||
TextBox{parent=spkr_c,x=1,y=10,height=3,text="Note: alarm sine waves are at half scale so that multiple will be required to reach full scale.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
@ -394,12 +396,12 @@ local function config_view(display)
|
||||
PushButton{parent=net_c_1,x=44,y=14,text="Next \x1a",callback=submit_channels,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||
|
||||
TextBox{parent=net_c_2,x=1,y=1,height=1,text="Connection Timeout"}
|
||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,fg_bg=bw_fg_bg}
|
||||
local timeout = NumberField{parent=net_c_2,x=1,y=2,width=7,default=ini_cfg.ConnTimeout,min=2,max=25,max_chars=6,max_frac_digits=2,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
TextBox{parent=net_c_2,x=9,y=2,height=2,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
|
||||
TextBox{parent=net_c_2,x=1,y=3,height=4,text="You generally do not want or need to modify this. On slow servers, you can increase this to make the system wait longer before assuming a disconnection.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
TextBox{parent=net_c_2,x=1,y=8,height=1,text="Trusted Range"}
|
||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_digits=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
local range = NumberField{parent=net_c_2,x=1,y=9,width=10,default=ini_cfg.TrustedRange,min=0,max_chars=20,allow_decimal=true,fg_bg=bw_fg_bg}
|
||||
TextBox{parent=net_c_2,x=1,y=10,height=4,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=g_lg_fg_bg}
|
||||
|
||||
local p2_err = TextBox{parent=net_c_2,x=8,y=14,height=1,width=35,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
|
||||
@ -829,11 +831,11 @@ local function config_view(display)
|
||||
|
||||
tool_ctl.p_name_msg = TextBox{parent=peri_c_4,x=1,y=1,height=2,text=""}
|
||||
tool_ctl.p_prompt = TextBox{parent=peri_c_4,x=1,y=4,height=2,text=""}
|
||||
tool_ctl.p_idx = NumberField{parent=peri_c_4,x=14,y=4,width=4,max_digits=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||
tool_ctl.p_idx = NumberField{parent=peri_c_4,x=14,y=4,width=4,max_chars=2,min=1,max=2,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||
tool_ctl.p_assign_btn = RadioButton{parent=peri_c_4,x=1,y=5,default=1,options={"the facility.","a unit. (unit #"},callback=function(v)tool_ctl.p_assign(v)end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.purple}
|
||||
tool_ctl.p_assign_end = TextBox{parent=peri_c_4,x=22,y=6,height=6,width=1,text=")"}
|
||||
|
||||
tool_ctl.p_unit = NumberField{parent=peri_c_4,x=44,y=4,width=4,max_digits=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||
tool_ctl.p_unit = NumberField{parent=peri_c_4,x=44,y=4,width=4,max_chars=2,min=1,max=4,default=1,fg_bg=bw_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||
tool_ctl.p_unit.disable()
|
||||
|
||||
function tool_ctl.p_assign(opt)
|
||||
@ -1052,7 +1054,7 @@ local function config_view(display)
|
||||
tool_ctl.rs_cfg_selection = TextBox{parent=rs_c_3,x=1,y=1,height=1,text=""}
|
||||
|
||||
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=27,y=3,width=7,height=1,text="Unit ID"}
|
||||
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=27,y=4,width=10,max_digits=2,min=1,max=4,fg_bg=bw_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"}
|
||||
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}
|
||||
@ -1142,7 +1144,7 @@ local function config_view(display)
|
||||
|
||||
tool_ctl.importing_any_dc = false
|
||||
|
||||
tmp_cfg.SpeakerVolume = config.SOUNDER_VOLUME
|
||||
tmp_cfg.SpeakerVolume = config.SOUNDER_VOLUME or 1
|
||||
tmp_cfg.SVR_Channel = config.SVR_CHANNEL
|
||||
tmp_cfg.RTU_Channel = config.RTU_CHANNEL
|
||||
tmp_cfg.ConnTimeout = config.COMMS_TIMEOUT
|
||||
|
@ -43,7 +43,7 @@ function rtu.load_config()
|
||||
cfv.assert_type_num(config.SpeakerVolume)
|
||||
cfv.assert_channel(config.SVR_Channel)
|
||||
cfv.assert_channel(config.RTU_Channel)
|
||||
cfv.assert_type_int(config.ConnTimeout)
|
||||
cfv.assert_type_num(config.ConnTimeout)
|
||||
cfv.assert_min(config.ConnTimeout, 2)
|
||||
cfv.assert_type_num(config.TrustedRange)
|
||||
cfv.assert_min(config.TrustedRange, 0)
|
||||
|
@ -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.7.8"
|
||||
local RTU_VERSION = "v1.7.11"
|
||||
|
||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||
|
@ -17,7 +17,7 @@ local max_distance = nil
|
||||
local comms = {}
|
||||
|
||||
-- protocol/data version (protocol/data independent changes tracked by util.lua version)
|
||||
comms.version = "2.4.2"
|
||||
comms.version = "2.4.3"
|
||||
|
||||
---@enum PROTOCOL
|
||||
local PROTOCOL = {
|
||||
|
@ -22,7 +22,7 @@ local t_pack = table.pack
|
||||
local util = {}
|
||||
|
||||
-- scada-common version
|
||||
util.version = "1.1.11"
|
||||
util.version = "1.1.12"
|
||||
|
||||
util.TICK_TIME_S = 0.05
|
||||
util.TICK_TIME_MS = 50
|
||||
@ -78,6 +78,17 @@ function util.strval(val)
|
||||
else return tostring(val) end
|
||||
end
|
||||
|
||||
-- tokenize a string by a separator<br>
|
||||
-- does not behave exactly like C's strtok
|
||||
---@param str string string to tokenize
|
||||
---@param sep string separator to tokenize by
|
||||
---@return table token_list
|
||||
function util.strtok(str, sep)
|
||||
local list = {}
|
||||
for part in string.gmatch(str, "([^" .. sep .. "]+)") do t_insert(list, part) end
|
||||
return list
|
||||
end
|
||||
|
||||
-- repeat a space n times
|
||||
---@nodiscard
|
||||
---@param n integer
|
||||
|
@ -2,7 +2,7 @@ local util = require("scada-common.util")
|
||||
|
||||
local println = util.println
|
||||
|
||||
local BOOTLOADER_VERSION = "0.4"
|
||||
local BOOTLOADER_VERSION = "0.5"
|
||||
|
||||
println("SCADA BOOTLOADER V" .. BOOTLOADER_VERSION)
|
||||
println("BOOT> SCANNING FOR APPLICATIONS...")
|
||||
|
@ -1,56 +0,0 @@
|
||||
local config = {}
|
||||
|
||||
-- supervisor comms channel
|
||||
config.SVR_CHANNEL = 16240
|
||||
-- PLC comms channel
|
||||
config.PLC_CHANNEL = 16241
|
||||
-- RTU/MODBUS comms channel
|
||||
config.RTU_CHANNEL = 16242
|
||||
-- coordinator comms channel
|
||||
config.CRD_CHANNEL = 16243
|
||||
-- pocket comms channel
|
||||
config.PKT_CHANNEL = 16244
|
||||
-- max trusted modem message distance
|
||||
-- (0 to disable check)
|
||||
config.TRUSTED_RANGE = 0
|
||||
-- time in seconds (>= 2) before assuming a remote
|
||||
-- device is no longer active
|
||||
config.PLC_TIMEOUT = 5
|
||||
config.RTU_TIMEOUT = 5
|
||||
config.CRD_TIMEOUT = 5
|
||||
config.PKT_TIMEOUT = 5
|
||||
-- facility authentication key
|
||||
-- (do NOT use one of your passwords)
|
||||
-- this enables verifying that messages are authentic
|
||||
-- all devices on this network must use this key
|
||||
-- config.AUTH_KEY = "SCADAfacility123"
|
||||
|
||||
-- expected number of reactors
|
||||
config.NUM_REACTORS = 4
|
||||
-- expected number of devices for each unit
|
||||
config.REACTOR_COOLING = {
|
||||
-- reactor unit 1
|
||||
{ BOILERS = 1, TURBINES = 1, TANK = false },
|
||||
-- reactor unit 2
|
||||
{ BOILERS = 1, TURBINES = 1, TANK = false },
|
||||
-- reactor unit 3
|
||||
{ BOILERS = 1, TURBINES = 1, TANK = false },
|
||||
-- reactor unit 4
|
||||
{ BOILERS = 1, TURBINES = 1, TANK = false }
|
||||
}
|
||||
-- advanced facility dynamic tank configuration
|
||||
-- (see wiki for details)
|
||||
-- by default, dynamic tanks are for each unit
|
||||
config.FAC_TANK_MODE = 0
|
||||
config.FAC_TANK_DEFS = { 0, 0, 0, 0 }
|
||||
|
||||
-- log path
|
||||
config.LOG_PATH = "/log.txt"
|
||||
-- log mode
|
||||
-- 0 = APPEND (adds to existing file on start)
|
||||
-- 1 = NEW (replaces existing file on start)
|
||||
config.LOG_MODE = 0
|
||||
-- true to log verbose debug messages
|
||||
config.LOG_DEBUG = false
|
||||
|
||||
return config
|
1088
supervisor/configure.lua
Normal file
1088
supervisor/configure.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -135,7 +135,7 @@ function facility.new(num_reactors, cooling_conf)
|
||||
|
||||
-- create units
|
||||
for i = 1, num_reactors do
|
||||
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BOILERS, cooling_conf.r_cool[i].TURBINES))
|
||||
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BoilerCount, cooling_conf.r_cool[i].TurbineCount))
|
||||
table.insert(self.group_map, 0)
|
||||
end
|
||||
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local config = require("supervisor.config")
|
||||
local databus = require("supervisor.databus")
|
||||
local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local pgi = require("supervisor.panel.pgi")
|
||||
local style = require("supervisor.panel.style")
|
||||
@ -88,7 +88,7 @@ local function init(panel)
|
||||
local plc_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||
local plc_list = Div{parent=plc_page,x=2,y=2,width=49}
|
||||
|
||||
for i = 1, config.NUM_REACTORS do
|
||||
for i = 1, supervisor.config.UnitCount do
|
||||
local ps_prefix = "plc_" .. i .. "_"
|
||||
local plc_entry = Div{parent=plc_list,height=3,fg_bg=bw_fg_bg}
|
||||
|
||||
|
@ -2,16 +2,14 @@ local log = require("scada-common.log")
|
||||
local mqueue = require("scada-common.mqueue")
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local config = require("supervisor.config")
|
||||
local databus = require("supervisor.databus")
|
||||
local facility = require("supervisor.facility")
|
||||
|
||||
local svqtypes = require("supervisor.session.svqtypes")
|
||||
|
||||
local coordinator = require("supervisor.session.coordinator")
|
||||
local plc = require("supervisor.session.plc")
|
||||
local pocket = require("supervisor.session.pocket")
|
||||
local rtu = require("supervisor.session.rtu")
|
||||
local svqtypes = require("supervisor.session.svqtypes")
|
||||
|
||||
-- Supervisor Sessions Handler
|
||||
|
||||
@ -36,7 +34,7 @@ svsessions.SESSION_TYPE = SESSION_TYPE
|
||||
local self = {
|
||||
nic = nil, ---@type nic|nil
|
||||
fp_ok = false,
|
||||
num_reactors = 0,
|
||||
config = nil, ---@type svr_config
|
||||
facility = nil, ---@type facility|nil
|
||||
sessions = { rtu = {}, plc = {}, crd = {}, pdg = {} },
|
||||
next_ids = { rtu = 0, plc = 0, crd = 0, pdg = 0 }
|
||||
@ -60,7 +58,7 @@ local function _sv_handle_outq(session)
|
||||
if msg ~= nil then
|
||||
if msg.qtype == mqueue.TYPE.PACKET then
|
||||
-- handle a packet to be sent
|
||||
self.nic.transmit(session.r_chan, config.SVR_CHANNEL, msg.message)
|
||||
self.nic.transmit(session.r_chan, self.config.SVR_Channel, msg.message)
|
||||
elseif msg.qtype == mqueue.TYPE.COMMAND then
|
||||
-- handle instruction/notification
|
||||
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||
@ -135,7 +133,7 @@ local function _shutdown(session)
|
||||
while session.out_queue.ready() do
|
||||
local msg = session.out_queue.pop()
|
||||
if msg ~= nil and msg.qtype == mqueue.TYPE.PACKET then
|
||||
self.nic.transmit(session.r_chan, config.SVR_CHANNEL, msg.message)
|
||||
self.nic.transmit(session.r_chan, self.config.SVR_Channel, msg.message)
|
||||
end
|
||||
end
|
||||
|
||||
@ -197,13 +195,13 @@ end
|
||||
-- initialize svsessions
|
||||
---@param nic nic network interface device
|
||||
---@param fp_ok boolean front panel active
|
||||
---@param num_reactors integer number of reactors
|
||||
---@param config svr_config supervisor configuration
|
||||
---@param cooling_conf sv_cooling_conf cooling configuration definition
|
||||
function svsessions.init(nic, fp_ok, num_reactors, cooling_conf)
|
||||
function svsessions.init(nic, fp_ok, config, cooling_conf)
|
||||
self.nic = nic
|
||||
self.fp_ok = fp_ok
|
||||
self.num_reactors = num_reactors
|
||||
self.facility = facility.new(num_reactors, cooling_conf)
|
||||
self.config = config
|
||||
self.facility = facility.new(config.UnitCount, cooling_conf)
|
||||
end
|
||||
|
||||
-- find an RTU session by the computer ID
|
||||
@ -280,14 +278,14 @@ end
|
||||
---@param version string
|
||||
---@return integer|false session_id
|
||||
function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
||||
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.num_reactors then
|
||||
if svsessions.get_reactor_session(for_reactor) == nil and for_reactor >= 1 and for_reactor <= self.config.UnitCount then
|
||||
---@class plc_session_struct
|
||||
local plc_s = {
|
||||
s_type = "plc",
|
||||
open = true,
|
||||
reactor = for_reactor,
|
||||
version = version,
|
||||
r_chan = config.PLC_CHANNEL,
|
||||
r_chan = self.config.PLC_Channel,
|
||||
s_addr = source_addr,
|
||||
in_queue = mqueue.new(),
|
||||
out_queue = mqueue.new(),
|
||||
@ -296,8 +294,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
||||
|
||||
local id = self.next_ids.plc
|
||||
|
||||
plc_s.instance = plc.new_session(id, source_addr, for_reactor, plc_s.in_queue, plc_s.out_queue,
|
||||
config.PLC_TIMEOUT, self.fp_ok)
|
||||
plc_s.instance = plc.new_session(id, source_addr, for_reactor, plc_s.in_queue, plc_s.out_queue, self.config.PLC_Timeout, self.fp_ok)
|
||||
table.insert(self.sessions.plc, plc_s)
|
||||
|
||||
local units = self.facility.get_units()
|
||||
@ -305,8 +302,7 @@ function svsessions.establish_plc_session(source_addr, for_reactor, version)
|
||||
|
||||
local mt = {
|
||||
---@param s plc_session_struct
|
||||
__tostring = function (s) return util.c("PLC [", s.instance.get_id(), "] for reactor #", s.reactor,
|
||||
" (@", s.s_addr, ")") end
|
||||
__tostring = function (s) return util.c("PLC [", s.instance.get_id(), "] for reactor #", s.reactor, " (@", s.s_addr, ")") end
|
||||
}
|
||||
|
||||
setmetatable(plc_s, mt)
|
||||
@ -336,7 +332,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
||||
s_type = "rtu",
|
||||
open = true,
|
||||
version = version,
|
||||
r_chan = config.RTU_CHANNEL,
|
||||
r_chan = self.config.RTU_Channel,
|
||||
s_addr = source_addr,
|
||||
in_queue = mqueue.new(),
|
||||
out_queue = mqueue.new(),
|
||||
@ -345,8 +341,7 @@ function svsessions.establish_rtu_session(source_addr, advertisement, version)
|
||||
|
||||
local id = self.next_ids.rtu
|
||||
|
||||
rtu_s.instance = rtu.new_session(id, source_addr, rtu_s.in_queue, rtu_s.out_queue, config.RTU_TIMEOUT,
|
||||
advertisement, self.facility, self.fp_ok)
|
||||
rtu_s.instance = rtu.new_session(id, source_addr, rtu_s.in_queue, rtu_s.out_queue, self.config.RTU_Timeout, advertisement, self.facility, self.fp_ok)
|
||||
table.insert(self.sessions.rtu, rtu_s)
|
||||
|
||||
local mt = {
|
||||
@ -377,7 +372,7 @@ function svsessions.establish_crd_session(source_addr, version)
|
||||
s_type = "crd",
|
||||
open = true,
|
||||
version = version,
|
||||
r_chan = config.CRD_CHANNEL,
|
||||
r_chan = self.config.CRD_Channel,
|
||||
s_addr = source_addr,
|
||||
in_queue = mqueue.new(),
|
||||
out_queue = mqueue.new(),
|
||||
@ -386,8 +381,7 @@ function svsessions.establish_crd_session(source_addr, version)
|
||||
|
||||
local id = self.next_ids.crd
|
||||
|
||||
crd_s.instance = coordinator.new_session(id, source_addr, crd_s.in_queue, crd_s.out_queue, config.CRD_TIMEOUT,
|
||||
self.facility, self.fp_ok)
|
||||
crd_s.instance = coordinator.new_session(id, source_addr, crd_s.in_queue, crd_s.out_queue, self.config.CRD_Timeout, self.facility, self.fp_ok)
|
||||
table.insert(self.sessions.crd, crd_s)
|
||||
|
||||
local mt = {
|
||||
@ -421,7 +415,7 @@ function svsessions.establish_pdg_session(source_addr, version)
|
||||
s_type = "pkt",
|
||||
open = true,
|
||||
version = version,
|
||||
r_chan = config.PKT_CHANNEL,
|
||||
r_chan = self.config.PKT_Channel,
|
||||
s_addr = source_addr,
|
||||
in_queue = mqueue.new(),
|
||||
out_queue = mqueue.new(),
|
||||
@ -430,8 +424,7 @@ function svsessions.establish_pdg_session(source_addr, version)
|
||||
|
||||
local id = self.next_ids.pdg
|
||||
|
||||
pdg_s.instance = pocket.new_session(id, source_addr, pdg_s.in_queue, pdg_s.out_queue, config.PKT_TIMEOUT, self.facility,
|
||||
self.fp_ok)
|
||||
pdg_s.instance = pocket.new_session(id, source_addr, pdg_s.in_queue, pdg_s.out_queue, self.config.PKT_Timeout, self.facility, self.fp_ok)
|
||||
table.insert(self.sessions.pdg, pdg_s)
|
||||
|
||||
local mt = {
|
||||
|
@ -14,70 +14,67 @@ local util = require("scada-common.util")
|
||||
|
||||
local core = require("graphics.core")
|
||||
|
||||
local config = require("supervisor.config")
|
||||
local configure = require("supervisor.configure")
|
||||
local databus = require("supervisor.databus")
|
||||
local renderer = require("supervisor.renderer")
|
||||
local supervisor = require("supervisor.supervisor")
|
||||
|
||||
local svsessions = require("supervisor.session.svsessions")
|
||||
|
||||
local SUPERVISOR_VERSION = "v1.1.0"
|
||||
local SUPERVISOR_VERSION = "v1.2.5"
|
||||
|
||||
local println = util.println
|
||||
local println_ts = util.println_ts
|
||||
|
||||
----------------------------------------
|
||||
-- config validation
|
||||
-- get configuration
|
||||
----------------------------------------
|
||||
|
||||
if not supervisor.load_config() then
|
||||
-- try to reconfigure (user action)
|
||||
local success, error = configure.configure(true)
|
||||
if success then
|
||||
assert(supervisor.load_config(), "failed to load valid supervisor configuration")
|
||||
else
|
||||
assert(success, "supervisor configuration error: " .. error)
|
||||
end
|
||||
end
|
||||
|
||||
local config = supervisor.config
|
||||
|
||||
local cfv = util.new_validator()
|
||||
|
||||
cfv.assert_channel(config.SVR_CHANNEL)
|
||||
cfv.assert_channel(config.PLC_CHANNEL)
|
||||
cfv.assert_channel(config.RTU_CHANNEL)
|
||||
cfv.assert_channel(config.CRD_CHANNEL)
|
||||
cfv.assert_channel(config.PKT_CHANNEL)
|
||||
cfv.assert_type_int(config.TRUSTED_RANGE)
|
||||
cfv.assert_type_num(config.PLC_TIMEOUT)
|
||||
cfv.assert_min(config.PLC_TIMEOUT, 2)
|
||||
cfv.assert_type_num(config.RTU_TIMEOUT)
|
||||
cfv.assert_min(config.RTU_TIMEOUT, 2)
|
||||
cfv.assert_type_num(config.CRD_TIMEOUT)
|
||||
cfv.assert_min(config.CRD_TIMEOUT, 2)
|
||||
cfv.assert_type_num(config.PKT_TIMEOUT)
|
||||
cfv.assert_min(config.PKT_TIMEOUT, 2)
|
||||
cfv.assert_type_int(config.NUM_REACTORS)
|
||||
cfv.assert_type_table(config.REACTOR_COOLING)
|
||||
cfv.assert_type_int(config.FAC_TANK_MODE)
|
||||
cfv.assert_type_table(config.FAC_TANK_DEFS)
|
||||
cfv.assert_type_str(config.LOG_PATH)
|
||||
cfv.assert_type_int(config.LOG_MODE)
|
||||
cfv.assert_eq(#config.CoolingConfig, config.UnitCount)
|
||||
assert(cfv.valid(), "startup> the number of reactor cooling configurations is different than the number of units")
|
||||
|
||||
assert(cfv.valid(), "bad config file: missing/invalid fields")
|
||||
for i = 1, config.UnitCount do
|
||||
cfv.assert_type_table(config.CoolingConfig[i])
|
||||
assert(cfv.valid(), "startup> missing cooling entry for reactor unit " .. i)
|
||||
cfv.assert_type_int(config.CoolingConfig[i].BoilerCount)
|
||||
cfv.assert_type_int(config.CoolingConfig[i].TurbineCount)
|
||||
cfv.assert_type_bool(config.CoolingConfig[i].TankConnection)
|
||||
assert(cfv.valid(), "startup> missing boiler/turbine/tank fields for reactor unit " .. i)
|
||||
cfv.assert_range(config.CoolingConfig[i].BoilerCount, 0, 2)
|
||||
cfv.assert_range(config.CoolingConfig[i].TurbineCount, 1, 3)
|
||||
assert(cfv.valid(), "startup> out-of-range number of boilers and/or turbines provided for reactor unit " .. i)
|
||||
end
|
||||
|
||||
assert((config.FAC_TANK_MODE == 0) or (config.NUM_REACTORS == #config.FAC_TANK_DEFS),
|
||||
"bad config file: FAC_TANK_DEFS length not equal to NUM_REACTORS")
|
||||
if config.FacilityTankMode > 0 then
|
||||
assert(config.UnitCount == #config.FacilityTankDefs, "startup> the number of facility tank definitions must be equal to the number of units in facility tank mode")
|
||||
|
||||
cfv.assert_eq(#config.REACTOR_COOLING, config.NUM_REACTORS)
|
||||
assert(cfv.valid(), "config: number of cooling configs different than number of units")
|
||||
|
||||
for i = 1, config.NUM_REACTORS do
|
||||
cfv.assert_type_table(config.REACTOR_COOLING[i])
|
||||
assert(cfv.valid(), "config: missing cooling entry for reactor " .. i)
|
||||
cfv.assert_type_int(config.REACTOR_COOLING[i].BOILERS)
|
||||
cfv.assert_type_int(config.REACTOR_COOLING[i].TURBINES)
|
||||
cfv.assert_type_bool(config.REACTOR_COOLING[i].TANK)
|
||||
assert(cfv.valid(), "config: missing boilers/turbines for reactor " .. i)
|
||||
cfv.assert_min(config.REACTOR_COOLING[i].BOILERS, 0)
|
||||
cfv.assert_min(config.REACTOR_COOLING[i].TURBINES, 1)
|
||||
assert(cfv.valid(), "config: bad number of boilers/turbines for reactor " .. i)
|
||||
for i = 1, config.UnitCount do
|
||||
local def = config.FacilityTankDefs[i]
|
||||
cfv.assert_type_int(def)
|
||||
cfv.assert_range(def, 0, 2)
|
||||
assert(cfv.valid(), "startup> invalid facility tank definition for reactor unit " .. i)
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------
|
||||
-- log init
|
||||
----------------------------------------
|
||||
|
||||
log.init(config.LOG_PATH, config.LOG_MODE, config.LOG_DEBUG == true)
|
||||
log.init(config.LogPath, config.LogMode, config.LogDebug)
|
||||
|
||||
log.info("========================================")
|
||||
log.info("BOOTING supervisor.startup " .. SUPERVISOR_VERSION)
|
||||
@ -102,8 +99,8 @@ local function main()
|
||||
ppm.mount_all()
|
||||
|
||||
-- message authentication init
|
||||
if type(config.AUTH_KEY) == "string" then
|
||||
network.init_mac(config.AUTH_KEY)
|
||||
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
|
||||
network.init_mac(config.AuthKey)
|
||||
end
|
||||
|
||||
-- get modem
|
||||
|
@ -2,8 +2,6 @@ local comms = require("scada-common.comms")
|
||||
local log = require("scada-common.log")
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local config = require("supervisor.config")
|
||||
|
||||
local svsessions = require("supervisor.session.svsessions")
|
||||
|
||||
local supervisor = {}
|
||||
@ -13,6 +11,77 @@ local DEVICE_TYPE = comms.DEVICE_TYPE
|
||||
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
||||
local MGMT_TYPE = comms.MGMT_TYPE
|
||||
|
||||
---@type svr_config
|
||||
local config = {}
|
||||
|
||||
supervisor.config = config
|
||||
|
||||
-- load the supervisor configuration
|
||||
function supervisor.load_config()
|
||||
if not settings.load("/supervisor.settings") then return false end
|
||||
|
||||
config.UnitCount = settings.get("UnitCount")
|
||||
config.CoolingConfig = settings.get("CoolingConfig")
|
||||
config.FacilityTankMode = settings.get("FacilityTankMode")
|
||||
config.FacilityTankDefs = settings.get("FacilityTankDefs")
|
||||
|
||||
config.SVR_Channel = settings.get("SVR_Channel")
|
||||
config.PLC_Channel = settings.get("PLC_Channel")
|
||||
config.RTU_Channel = settings.get("RTU_Channel")
|
||||
config.CRD_Channel = settings.get("CRD_Channel")
|
||||
config.PKT_Channel = settings.get("PKT_Channel")
|
||||
|
||||
config.PLC_Timeout = settings.get("PLC_Timeout")
|
||||
config.RTU_Timeout = settings.get("RTU_Timeout")
|
||||
config.CRD_Timeout = settings.get("CRD_Timeout")
|
||||
config.PKT_Timeout = settings.get("PKT_Timeout")
|
||||
|
||||
config.TrustedRange = settings.get("TrustedRange")
|
||||
config.AuthKey = settings.get("AuthKey")
|
||||
|
||||
config.LogMode = settings.get("LogMode")
|
||||
config.LogPath = settings.get("LogPath")
|
||||
config.LogDebug = settings.get("LogDebug")
|
||||
|
||||
local cfv = util.new_validator()
|
||||
|
||||
cfv.assert_type_int(config.UnitCount)
|
||||
cfv.assert_range(config.UnitCount, 1, 4)
|
||||
|
||||
cfv.assert_type_table(config.CoolingConfig)
|
||||
cfv.assert_type_table(config.FacilityTankDefs)
|
||||
cfv.assert_type_int(config.FacilityTankMode)
|
||||
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
||||
|
||||
cfv.assert_channel(config.SVR_Channel)
|
||||
cfv.assert_channel(config.PLC_Channel)
|
||||
cfv.assert_channel(config.RTU_Channel)
|
||||
cfv.assert_channel(config.CRD_Channel)
|
||||
cfv.assert_channel(config.PKT_Channel)
|
||||
|
||||
cfv.assert_type_num(config.PLC_Timeout)
|
||||
cfv.assert_min(config.PLC_Timeout, 2)
|
||||
cfv.assert_type_num(config.RTU_Timeout)
|
||||
cfv.assert_min(config.RTU_Timeout, 2)
|
||||
cfv.assert_type_num(config.CRD_Timeout)
|
||||
cfv.assert_min(config.CRD_Timeout, 2)
|
||||
cfv.assert_type_num(config.PKT_Timeout)
|
||||
cfv.assert_min(config.PKT_Timeout, 2)
|
||||
|
||||
cfv.assert_type_num(config.TrustedRange)
|
||||
|
||||
if type(config.AuthKey) == "string" then
|
||||
local len = string.len(config.AuthKey)
|
||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
||||
end
|
||||
|
||||
cfv.assert_type_int(config.LogMode)
|
||||
cfv.assert_type_str(config.LogPath)
|
||||
cfv.assert_type_bool(config.LogDebug)
|
||||
|
||||
return cfv.valid()
|
||||
end
|
||||
|
||||
-- supervisory controller communications
|
||||
---@nodiscard
|
||||
---@param _version string supervisor version
|
||||
@ -23,32 +92,23 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
-- print a log message to the terminal as long as the UI isn't running
|
||||
local function println(message) if not fp_ok then util.println_ts(message) end end
|
||||
|
||||
-- channel list from config
|
||||
local svr_channel = config.SVR_CHANNEL
|
||||
local plc_channel = config.PLC_CHANNEL
|
||||
local rtu_channel = config.RTU_CHANNEL
|
||||
local crd_channel = config.CRD_CHANNEL
|
||||
local pkt_channel = config.PKT_CHANNEL
|
||||
|
||||
-- configuration data
|
||||
local num_reactors = config.NUM_REACTORS
|
||||
---@class sv_cooling_conf
|
||||
local cooling_conf = { r_cool = config.REACTOR_COOLING, fac_tank_mode = config.FAC_TANK_MODE, fac_tank_defs = config.FAC_TANK_DEFS }
|
||||
local cooling_conf = { r_cool = config.CoolingConfig, fac_tank_mode = config.FacilityTankMode, fac_tank_defs = config.FacilityTankDefs }
|
||||
|
||||
local self = {
|
||||
last_est_acks = {}
|
||||
}
|
||||
|
||||
comms.set_trusted_range(config.TRUSTED_RANGE)
|
||||
comms.set_trusted_range(config.TrustedRange)
|
||||
|
||||
-- PRIVATE FUNCTIONS --
|
||||
|
||||
-- configure modem channels
|
||||
nic.closeAll()
|
||||
nic.open(svr_channel)
|
||||
nic.open(config.SVR_Channel)
|
||||
|
||||
-- pass modem, status, and config data to svsessions
|
||||
svsessions.init(nic, fp_ok, num_reactors, cooling_conf)
|
||||
svsessions.init(nic, fp_ok, config, cooling_conf)
|
||||
|
||||
-- send an establish request response
|
||||
---@param packet scada_packet
|
||||
@ -61,7 +121,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
m_pkt.make(MGMT_TYPE.ESTABLISH, { ack, data })
|
||||
s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
||||
|
||||
nic.transmit(packet.remote_channel(), svr_channel, s_pkt)
|
||||
nic.transmit(packet.remote_channel(), config.SVR_Channel, s_pkt)
|
||||
self.last_est_acks[packet.src_addr()] = ack
|
||||
end
|
||||
|
||||
@ -124,9 +184,9 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
local src_addr = packet.scada_frame.src_addr()
|
||||
local protocol = packet.scada_frame.protocol()
|
||||
|
||||
if l_chan ~= svr_channel then
|
||||
if l_chan ~= config.SVR_Channel then
|
||||
log.debug("received packet on unconfigured channel " .. l_chan, true)
|
||||
elseif r_chan == plc_channel then
|
||||
elseif r_chan == config.PLC_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_plc_session(src_addr)
|
||||
|
||||
@ -201,7 +261,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
else
|
||||
log.debug(util.c("illegal packet type ", protocol, " on PLC channel"))
|
||||
end
|
||||
elseif r_chan == rtu_channel then
|
||||
elseif r_chan == config.RTU_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_rtu_session(src_addr)
|
||||
|
||||
@ -265,7 +325,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
else
|
||||
log.debug(util.c("illegal packet type ", protocol, " on RTU channel"))
|
||||
end
|
||||
elseif r_chan == crd_channel then
|
||||
elseif r_chan == config.CRD_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_crd_session(src_addr)
|
||||
|
||||
@ -299,7 +359,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
println(util.c("CRD (", firmware_v, ") [@", src_addr, "] \xbb connected"))
|
||||
log.info(util.c("CRD_ESTABLISH: coordinator (", firmware_v, ") [@", src_addr, "] connected with session ID ", s_id))
|
||||
|
||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { num_reactors, cooling_conf })
|
||||
_send_establish(packet.scada_frame, ESTABLISH_ACK.ALLOW, { config.UnitCount, cooling_conf })
|
||||
else
|
||||
if last_ack ~= ESTABLISH_ACK.COLLISION then
|
||||
log.info("CRD_ESTABLISH: denied new coordinator [@" .. src_addr .. "] due to already being connected to another coordinator")
|
||||
@ -332,7 +392,7 @@ function supervisor.comms(_version, nic, fp_ok)
|
||||
else
|
||||
log.debug(util.c("illegal packet type ", protocol, " on coordinator channel"))
|
||||
end
|
||||
elseif r_chan == pkt_channel then
|
||||
elseif r_chan == config.PKT_Channel then
|
||||
-- look for an associated session
|
||||
local session = svsessions.find_pdg_session(src_addr)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user