Merge pull request #360 from MikaylaFischler/devel

2023.10.14 Release
This commit is contained in:
Mikayla 2023-10-14 19:46:33 -04:00 committed by GitHub
commit 7b522ae120
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 158 additions and 154 deletions

View File

@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {}
core.version = "2.0.0"
core.version = "2.0.2"
core.flasher = flasher
core.events = events
@ -115,7 +115,7 @@ end
-- extract the custom element assert message, dropping the path to the element file
function core.extract_assert_msg(msg)
return string.sub(msg, (string.find(msg, "@") + 1) or 1)
return string.sub(msg, (string.find(msg, "@") or 0) + 1)
end
-- Interactive Field Manager

View File

@ -217,10 +217,10 @@ function element.new(args, child_offset_x, child_offset_y)
end
-- check frame
element.assert(f.x >= 1, "frame x not >= 1", 2)
element.assert(f.y >= 1, "frame y not >= 1", 2)
element.assert(f.w >= 1, "frame width not >= 1", 2)
element.assert(f.h >= 1, "frame height not >= 1", 2)
element.assert(f.x >= 1, "frame x not >= 1", 3)
element.assert(f.y >= 1, "frame y not >= 1", 3)
element.assert(f.w >= 1, "frame width not >= 1", 3)
element.assert(f.h >= 1, "frame height not >= 1", 3)
-- create window
protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, args.hidden ~= true)

View File

@ -30,6 +30,11 @@ local LEFT = core.ALIGN.LEFT
local CENTER = core.ALIGN.CENTER
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)" } }
}
---@class plc_configurator
local configurator = {}
@ -37,7 +42,6 @@ local style = {}
style.root = cpair(colors.black, colors.lightGray)
style.header = cpair(colors.white, colors.gray)
style.label = cpair(colors.gray, colors.lightGray)
style.colors = {
{ c = colors.red, hex = 0xdf4949 },
@ -55,6 +59,11 @@ style.colors = {
{ c = colors.gray, hex = 0x575757 }
}
local bw_fg_bg = cpair(colors.black, colors.white)
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
local nav_fg_bg = bw_fg_bg
local btn_act_fg_bg = cpair(colors.white, colors.gray)
local tool_ctl = {
ask_config = false,
has_config = false,
@ -173,8 +182,6 @@ end
-- create the config view
---@param display graphics_element
local function config_view(display)
local nav_fg_bg = cpair(colors.black,colors.white)
local btn_act_fg_bg = cpair(colors.white,colors.gray)
---@diagnostic disable-next-line: undefined-field
local function exit() os.queueEvent("terminate") end
@ -188,8 +195,9 @@ local function config_view(display)
local net_cfg = Div{parent=root_pane_div,x=1,y=1}
local log_cfg = Div{parent=root_pane_div,x=1,y=1}
local summary = Div{parent=root_pane_div,x=1,y=1}
local changelog = Div{parent=root_pane_div,x=1,y=1}
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,summary}}
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,summary,changelog}}
-- MAIN PAGE
@ -198,8 +206,8 @@ local function config_view(display)
TextBox{parent=main_page,x=2,y=2,height=2,text_align=CENTER,text="Welcome to the Reactor PLC configurator! Please select one of the following options."}
if tool_ctl.ask_config then
TextBox{parent=main_page,x=2,y=y_start,height=2,text_align=CENTER,text="Notice: This device has no valid config. The configurator has been automatically started.",fg_bg=cpair(colors.red,colors.lightGray)}
y_start = y_start + 3
TextBox{parent=main_page,x=2,y=y_start,height=4,width=49,text_align=CENTER,text="Notice: This device has no valid config so the configurator has been automatically started. If you previously had a valid config, you may want to check the Change Log to see what changed.",fg_bg=cpair(colors.red,colors.lightGray)}
y_start = y_start + 5
end
local function view_config()
@ -220,6 +228,7 @@ local function config_view(display)
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
PushButton{parent=main_page,x=2,y=17,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg}
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(6)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
-- PLC CONFIG
@ -233,7 +242,7 @@ local function config_view(display)
TextBox{parent=plc_cfg,x=1,y=2,height=1,text_align=CENTER,text=" PLC Configuration",fg_bg=cpair(colors.black,colors.orange)}
TextBox{parent=plc_c_1,x=1,y=1,height=1,text_align=CENTER,text="Would you like to set this PLC as networked?"}
TextBox{parent=plc_c_1,x=1,y=3,height=4,text_align=CENTER,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=plc_c_1,x=1,y=3,height=4,text_align=CENTER,text="If you have a supervisor, select the box. You will later be prompted to select the network configuration. If you instead want to use this as a standalone safety system, don't select the box.",fg_bg=g_lg_fg_bg}
local networked = CheckBox{parent=plc_c_1,x=1,y=8,label="Networked",default=ini_cfg.Networked,box_fg_bg=cpair(colors.orange,colors.black)}
@ -246,10 +255,10 @@ local function config_view(display)
PushButton{parent=plc_c_1,x=44,y=14,min_width=6,text="Next \x1a",callback=submit_networked,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
TextBox{parent=plc_c_2,x=1,y=1,height=1,text_align=CENTER,text="Please enter the reactor unit ID for this PLC."}
TextBox{parent=plc_c_2,x=1,y=3,height=3,text_align=CENTER,text="If this is a networked PLC, currently only IDs 1 through 4 are acceptable.",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=plc_c_2,x=1,y=3,height=3,text_align=CENTER,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_align=CENTER,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=cpair(colors.black,colors.white)}
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_err = TextBox{parent=plc_c_2,x=8,y=14,height=1,width=35,text_align=LEFT,text="Please set a unit ID.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
@ -266,7 +275,7 @@ local function config_view(display)
PushButton{parent=plc_c_2,x=44,y=14,min_width=6,text="Next \x1a",callback=submit_id,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
TextBox{parent=plc_c_3,x=1,y=1,height=4,text_align=CENTER,text="When networked, the supervisor takes care of emergency coolant via RTUs. However, you can configure independent emergency coolant via the PLC. "}
TextBox{parent=plc_c_3,x=1,y=6,height=5,text_align=CENTER,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=plc_c_3,x=1,y=6,height=5,text_align=CENTER,text="This independent control can be used with or without a supervisor. To configure, you would next select the interface of the redstone output connected to one or more mekanism pipes.",fg_bg=g_lg_fg_bg}
local en_em_cool = CheckBox{parent=plc_c_3,x=1,y=11,label="Enable PLC Emergency Coolant Control",default=ini_cfg.EmerCoolEnable,box_fg_bg=cpair(colors.orange,colors.black)}
@ -287,7 +296,7 @@ local function config_view(display)
TextBox{parent=plc_c_4,x=1,y=5,height=1,text_align=CENTER,text="Bundled Redstone Configuration"}
local bundled = CheckBox{parent=plc_c_4,x=1,y=6,label="Is Bundled?",default=ini_cfg.EmerCoolColor~=nil,box_fg_bg=cpair(colors.orange,colors.black),callback=function(v)tool_ctl.bundled_emcool(v)end}
local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=cpair(colors.gray,colors.lightGray)}
local color = Radio2D{parent=plc_c_4,x=1,y=8,rows=4,columns=4,default=color_to_idx(ini_cfg.EmerCoolColor),options=color_options,radio_colors=cpair(colors.lightGray,colors.black),color_map=color_options_map,disable_color=colors.gray,disable_fg_bg=g_lg_fg_bg}
if ini_cfg.EmerCoolColor == nil then color.disable() end
local function submit_emcool()
@ -310,14 +319,14 @@ local function config_view(display)
TextBox{parent=net_cfg,x=1,y=2,height=1,text_align=CENTER,text=" Network Configuration",fg_bg=cpair(colors.black,colors.lightBlue)}
TextBox{parent=net_c_1,x=1,y=1,height=1,text_align=CENTER,text="Please set the network channels below."}
TextBox{parent=net_c_1,x=1,y=3,height=4,text_align=CENTER,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=net_c_1,x=1,y=3,height=4,text_align=CENTER,text="Each of the 5 uniquely named channels, including the 2 below, must be the same for each device in this SCADA network. For multiplayer servers, it is recommended to not use the default channels.",fg_bg=g_lg_fg_bg}
TextBox{parent=net_c_1,x=1,y=8,height=1,text_align=CENTER,text="Supervisor Channel"}
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=cpair(colors.black,colors.white)}
TextBox{parent=net_c_1,x=9,y=9,height=4,text_align=CENTER,text="[SVR_CHANNEL]",fg_bg=cpair(colors.gray,colors.lightGray)}
local svr_chan = NumberField{parent=net_c_1,x=1,y=9,width=7,default=ini_cfg.SVR_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
TextBox{parent=net_c_1,x=9,y=9,height=4,text_align=CENTER,text="[SVR_CHANNEL]",fg_bg=g_lg_fg_bg}
TextBox{parent=net_c_1,x=1,y=11,height=1,text_align=CENTER,text="PLC Channel"}
local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=cpair(colors.black,colors.white)}
TextBox{parent=net_c_1,x=9,y=12,height=4,text_align=CENTER,text="[PLC_CHANNEL]",fg_bg=cpair(colors.gray,colors.lightGray)}
local plc_chan = NumberField{parent=net_c_1,x=1,y=12,width=7,default=ini_cfg.PLC_Channel,min=1,max=65535,fg_bg=bw_fg_bg}
TextBox{parent=net_c_1,x=9,y=12,height=4,text_align=CENTER,text="[PLC_CHANNEL]",fg_bg=g_lg_fg_bg}
local chan_err = TextBox{parent=net_c_1,x=8,y=14,height=1,width=35,text_align=LEFT,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
@ -342,13 +351,13 @@ local function config_view(display)
PushButton{parent=net_c_1,x=44,y=14,min_width=6,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_align=CENTER,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=cpair(colors.black,colors.white)}
TextBox{parent=net_c_2,x=9,y=2,height=2,text_align=CENTER,text="seconds (default 5)",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=net_c_2,x=1,y=3,height=4,text_align=CENTER,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=cpair(colors.gray,colors.lightGray)}
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}
TextBox{parent=net_c_2,x=9,y=2,height=2,text_align=CENTER,text="seconds (default 5)",fg_bg=g_lg_fg_bg}
TextBox{parent=net_c_2,x=1,y=3,height=4,text_align=CENTER,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_align=CENTER,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=cpair(colors.black,colors.white)}
TextBox{parent=net_c_2,x=1,y=10,height=4,text_align=CENTER,text="Setting this to a value larger than 0 prevents connections with devices that many meters (blocks) away in any direction.",fg_bg=cpair(colors.gray,colors.lightGray)}
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}
TextBox{parent=net_c_2,x=1,y=10,height=4,text_align=CENTER,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_align=LEFT,text="",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
@ -373,10 +382,10 @@ local function config_view(display)
PushButton{parent=net_c_2,x=44,y=14,min_width=6,text="Next \x1a",callback=submit_ct_tr,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
TextBox{parent=net_c_3,x=1,y=1,height=2,text_align=CENTER,text="Optionally, set the facility authentication key below. Do NOT use one of your passwords."}
TextBox{parent=net_c_3,x=1,y=4,height=6,text_align=CENTER,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=net_c_3,x=1,y=4,height=6,text_align=CENTER,text="This enables verifying that messages are authentic, so it is intended for security on multiplayer servers. All devices on the same network MUST use the same key if any device has a key. This does result in some extra compution (can slow things down).",fg_bg=g_lg_fg_bg}
TextBox{parent=net_c_3,x=1,y=11,height=1,text_align=CENTER,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=cpair(colors.black,colors.white)}
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
@ -385,9 +394,15 @@ local function config_view(display)
hide_key.set_value(true)
censor_key(true)
local key_err = TextBox{parent=net_c_3,x=8,y=14,height=1,width=35,text_align=LEFT,text="Key must be at least 8 characters.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
local function submit_auth()
tmp_cfg.AuthKey = key.get_value()
main_pane.set_value(4)
local v = key.get_value()
if string.len(v) == 0 or string.len(v) >= 8 then
tmp_cfg.AuthKey = key.get_value()
main_pane.set_value(4)
key_err.hide(true)
else key_err.show() end
end
PushButton{parent=net_c_3,x=1,y=14,min_width=6,text="\x1b Back",callback=function()net_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
@ -405,10 +420,10 @@ local function config_view(display)
local mode = RadioButton{parent=log_c_1,x=1,y=4,default=ini_cfg.LogMode+1,options={"Append on Startup","Replace on Startup"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.pink}
TextBox{parent=log_c_1,x=1,y=7,height=1,text_align=CENTER,text="Log File Path"}
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=cpair(colors.black,colors.white)}
local path = TextField{parent=log_c_1,x=1,y=8,width=49,height=1,value=ini_cfg.LogPath,max_len=128,fg_bg=bw_fg_bg}
local en_dbg = CheckBox{parent=log_c_1,x=1,y=10,default=ini_cfg.LogDebug,label="Enable Logging Debug Messages",box_fg_bg=cpair(colors.pink,colors.black)}
TextBox{parent=log_c_1,x=3,y=11,height=2,text_align=CENTER,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=cpair(colors.gray,colors.lightGray)}
TextBox{parent=log_c_1,x=3,y=11,height=2,text_align=CENTER,text="This results in much larger log files. It is best to only use this when there is a problem.",fg_bg=g_lg_fg_bg}
local path_err = TextBox{parent=log_c_1,x=8,y=14,height=1,width=35,text_align=LEFT,text="Please provide a log file path.",fg_bg=cpair(colors.red,colors.lightGray),hidden=true}
@ -444,7 +459,7 @@ local function config_view(display)
TextBox{parent=summary,x=1,y=2,height=1,text_align=CENTER,text=" Summary",fg_bg=cpair(colors.black,colors.green)}
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=cpair(colors.black,colors.white),nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
local setting_list = ListBox{parent=sum_c_1,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
local function back_from_settings()
if tool_ctl.viewing_config or tool_ctl.importing_legacy then
@ -526,6 +541,25 @@ local function config_view(display)
PushButton{parent=sum_c_4,x=1,y=14,min_width=6,text="Home",callback=go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
PushButton{parent=sum_c_4,x=44,y=14,min_width=6,text="Exit",callback=exit,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
-- CONFIG CHANGE LOG
local cl = Div{parent=changelog,x=2,y=4,width=49}
TextBox{parent=changelog,x=1,y=2,height=1,text_align=CENTER,text=" Config Change Log",fg_bg=bw_fg_bg}
local c_log = ListBox{parent=cl,x=1,y=1,height=12,width=51,scroll_height=100,fg_bg=bw_fg_bg,nav_fg_bg=g_lg_fg_bg,nav_active=cpair(colors.black,colors.gray)}
for _, change in ipairs(changes) do
TextBox{parent=c_log,text=change[1],height=1,fg_bg=bw_fg_bg}
for _, v in ipairs(change[2]) do
local e = Div{parent=c_log,height=#util.strwrap(v,46)}
TextBox{parent=e,y=1,x=1,text="- ",height=1,fg_bg=cpair(colors.gray,colors.white)}
TextBox{parent=e,y=1,x=3,text=v,height=e.get_height(),fg_bg=cpair(colors.gray,colors.white)}
end
end
PushButton{parent=cl,x=1,y=14,min_width=6,text="\x1b Back",callback=function()main_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
-- set tool functions now that we have the elements
function tool_ctl.set_networked(enable)
@ -596,7 +630,7 @@ local function config_view(display)
if f[1] == "EmerCoolColor" and raw ~= nil then val = color_name_map[raw] end
if val == "nil" then val = "n/a" end
local c = util.trinary(alternate, cpair(colors.gray,colors.lightGray), cpair(colors.gray,colors.white))
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
alternate = not alternate
if string.len(val) > val_max_w then

View File

@ -54,13 +54,22 @@ function plc.load_config()
cfv.assert_type_bool(config.Networked)
cfv.assert_type_int(config.UnitID)
cfv.assert_type_bool(config.EmerCoolEnable)
cfv.assert_channel(config.SVR_Channel)
cfv.assert_channel(config.PLC_Channel)
cfv.assert_type_int(config.ConnTimeout)
cfv.assert_min(config.ConnTimeout, 2)
cfv.assert_type_num(config.TrustedRange)
cfv.assert_min(config.TrustedRange, 0)
cfv.assert_type_str(config.AuthKey)
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_min(config.ConnTimeout, 2)
cfv.assert_type_num(config.TrustedRange)
cfv.assert_min(config.TrustedRange, 0)
cfv.assert_type_str(config.AuthKey)
if type(config.AuthKey) == "string" then
local len = string.len(config.AuthKey)
cfv.assert_eq(len == 0 or len >= 8, true)
end
end
cfv.assert_type_int(config.LogMode)
cfv.assert_type_str(config.LogPath)
cfv.assert_type_bool(config.LogDebug)

View File

@ -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.0"
local R_PLC_VERSION = "v1.6.2"
local println = util.println
local println_ts = util.println_ts
@ -50,7 +50,7 @@ log.info("BOOTING reactor-plc.startup " .. R_PLC_VERSION)
log.info("========================================")
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
crash.set_env("plc", R_PLC_VERSION)
crash.set_env("reactor-plc", R_PLC_VERSION)
----------------------------------------
-- main application
@ -69,7 +69,7 @@ local function main()
ppm.mount_all()
-- message authentication init
if string.len(config.AuthKey) > 0 then
if type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0 then
network.init_mac(config.AuthKey)
end

View File

@ -311,7 +311,7 @@ function comms.authd_packet()
self.valid = false
self.raw = self.modem_msg_in.msg
if (type(max_distance) == "number") and (type(distance) == "number") and (distance > max_distance) then
if (type(max_distance) == "number") and ((type(distance) ~= "number") or (distance > max_distance)) then
-- outside of maximum allowable transmission distance
-- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
else

View File

@ -4,19 +4,26 @@
local cc_strings = require("cc.strings")
local math = math
local string = string
local table = table
local os = os
local getmetatable = getmetatable
local print = print
local tostring = tostring
local type = type
---@class util
local util = {}
-- scada-common version
util.version = "1.1.3"
-- ENVIRONMENT CONSTANTS --
util.version = "1.1.5"
util.TICK_TIME_S = 0.05
util.TICK_TIME_MS = 50
-- OPERATORS --
--#region
--#region OPERATORS
-- trinary operator
---@nodiscard
@ -30,37 +37,29 @@ end
--#endregion
-- PRINT --
--#region
--#region PRINT
local p_time = "[%H:%M:%S] "
-- print
---@param message any
function util.print(message)
term.write(tostring(message))
end
function util.print(message) term.write(tostring(message)) end
-- print line
---@param message any
function util.println(message)
print(tostring(message))
end
function util.println(message) print(tostring(message)) end
-- timestamped print
---@param message any
function util.print_ts(message)
term.write(os.date("[%H:%M:%S] ") .. tostring(message))
end
function util.print_ts(message) term.write(os.date(p_time) .. tostring(message)) end
-- timestamped print line
---@param message any
function util.println_ts(message)
print(os.date("[%H:%M:%S] ") .. tostring(message))
end
function util.println_ts(message) print(os.date(p_time) .. tostring(message)) end
--#endregion
-- STRING TOOLS --
--#region
--#region STRING TOOLS
-- get a value as a string
---@nodiscard
@ -68,21 +67,18 @@ end
---@return string
function util.strval(val)
local t = type(val)
if t == "string" then return val end
-- this depends on Lua short-circuiting the or check for metatables (note: metatables won't have metatables)
if (t == "table" and (getmetatable(val) == nil or getmetatable(val).__tostring == nil)) or t == "function" then
return "[" .. tostring(val) .. "]"
else
return tostring(val)
end
return table.concat{"[", tostring(val), "]"}
else return tostring(val) end
end
-- repeat a space n times
---@nodiscard
---@param n integer
---@return string
function util.spaces(n)
return string.rep(" ", n)
end
function util.spaces(n) return string.rep(" ", n) end
-- pad text to a minimum width
---@nodiscard
@ -94,7 +90,7 @@ function util.pad(str, n)
local lpad = math.floor((n - len) / 2)
local rpad = (n - len) - lpad
return util.spaces(lpad) .. str .. util.spaces(rpad)
return table.concat{util.spaces(lpad), str, util.spaces(rpad)}
end
-- wrap a string into a table of lines
@ -112,11 +108,9 @@ function util.strwrap(str, limit) return cc_strings.wrap(str, limit) end
---@return string
---@diagnostic disable-next-line: unused-vararg
function util.concat(...)
local str = ""
for _, v in ipairs(arg) do str = str .. util.strval(v) end
return str
local strings = {}
for i = 1, #arg do strings[i] = util.strval(arg[i]) end
return table.concat(strings)
end
-- alias
@ -127,9 +121,7 @@ util.c = util.concat
---@param format string
---@vararg any
---@diagnostic disable-next-line: unused-vararg
function util.sprintf(format, ...)
return string.format(format, table.unpack(arg))
end
function util.sprintf(format, ...) return string.format(format, table.unpack(arg)) end
-- luacheck: unused args
@ -144,7 +136,7 @@ function util.comma_format(num)
local i = 1
while i > 0 do
formatted, i = formatted:gsub("^(%s-%d+)(%d%d%d)", '%1,%2')
formatted, i = formatted:gsub("^(%s-%d+)(%d%d%d)", "%1,%2")
if i > 0 then commas = commas + 1 end
end
@ -158,31 +150,24 @@ end
--#endregion
-- MATH --
--#region
--#region MATH
-- is a value an integer
---@nodiscard
---@param x any value
---@return boolean is_integer if the number is an integer
function util.is_int(x)
return type(x) == "number" and x == math.floor(x)
end
---@return boolean is_integer
function util.is_int(x) return type(x) == "number" and x == math.floor(x) end
-- get the sign of a number
---@nodiscard
---@param x number value
---@return integer sign (-1 for < 0, 1 otherwise)
function util.sign(x)
return util.trinary(x < 0, -1, 1)
end
function util.sign(x) return util.trinary(x < 0, -1, 1) end
-- round a number to an integer
---@nodiscard
---@return integer rounded
function util.round(x)
return math.floor(x + 0.5)
end
function util.round(x) return math.floor(x + 0.5) end
-- get a new moving average object
---@nodiscard
@ -191,7 +176,7 @@ end
function util.mov_avg(length, default)
local data = {}
local index = 1
local last_t = 0 ---@type number|nil
local last_t = 0 ---@type number|nil
---@class moving_average
local public = {}
@ -207,9 +192,7 @@ function util.mov_avg(length, default)
---@param x number new value
---@param t number? optional last update time to prevent duplicated entries
function public.record(x, t)
if type(t) == "number" and last_t == t then
return
end
if type(t) == "number" and last_t == t then return end
data[index] = x
last_t = t
@ -232,23 +215,21 @@ function util.mov_avg(length, default)
return public
end
-- TIME --
--#endregion
--#region TIME
-- current time
---@nodiscard
---@return integer milliseconds
function util.time_ms()
---@diagnostic disable-next-line: undefined-field
return os.epoch('local')
end
function util.time_ms() return os.epoch("local") end
-- current time
---@nodiscard
---@return number seconds
function util.time_s()
---@diagnostic disable-next-line: undefined-field
return os.epoch('local') / 1000.0
end
function util.time_s() return os.epoch("local") / 1000.0 end
-- current time
---@nodiscard
@ -257,17 +238,14 @@ function util.time() return util.time_ms() end
--#endregion
-- OS --
--#region
--#region OS
-- OS pull event raw wrapper with types
---@nodiscard
---@param target_event? string event to wait for
---@return os_event event, any param1, any param2, any param3, any param4, any param5
function util.pull_event(target_event)
---@diagnostic disable-next-line: undefined-field
return os.pullEventRaw(target_event)
end
function util.pull_event(target_event) return os.pullEventRaw(target_event) end
-- OS queue event raw wrapper with types
---@param event os_event
@ -285,30 +263,23 @@ end
---@nodiscard
---@param t number timer duration in seconds
---@return integer timer ID
function util.start_timer(t)
---@diagnostic disable-next-line: undefined-field
return os.startTimer(t)
end
function util.start_timer(t) return os.startTimer(t) end
-- cancel an OS timer
---@param timer integer timer ID
function util.cancel_timer(timer)
---@diagnostic disable-next-line: undefined-field
os.cancelTimer(timer)
end
function util.cancel_timer(timer) os.cancelTimer(timer) end
--#endregion
-- PARALLELIZATION --
--#region
--#region PARALLELIZATION
-- protected sleep call so we still are in charge of catching termination
---@param t integer seconds
--- EVENT_CONSUMER: this function consumes events
function util.psleep(t)
---@diagnostic disable-next-line: undefined-field
pcall(os.sleep, t)
end
function util.psleep(t) pcall(os.sleep, t) end
-- no-op to provide a brief pause (1 tick) to yield<br>
--- EVENT_CONSUMER: this function consumes events
@ -330,8 +301,7 @@ end
--#endregion
-- TABLE UTILITIES --
--#region
--#region TABLE UTILITIES
-- delete elements from a table if the passed function returns false when passed a table element<br>
-- put briefly: deletes elements that return false, keeps elements that return true
@ -371,8 +341,7 @@ end
--#endregion
-- MEKANISM POWER --
--#region
--#region MEKANISM POWER
-- convert Joules to FE
---@nodiscard
@ -401,8 +370,7 @@ local function ZFE(fe) return fe / 1000000000000000000000.0 end -- how & why did
---@param format? string format override
---@return string str, string? unit
function util.power_format(fe, combine_label, format)
local unit
local value
local unit, value
if type(format) ~= "string" then format = "%.2f" end
@ -441,8 +409,7 @@ end
--#endregion
-- UTILITY CLASSES --
--#region
--#region UTILITY CLASSES
-- WATCHDOG --
@ -451,32 +418,25 @@ end
---@nodiscard
---@param timeout number timeout duration
function util.new_watchdog(timeout)
local self = {
timeout = timeout,
wd_timer = util.start_timer(timeout)
}
local self = { timeout = timeout, wd_timer = util.start_timer(timeout) }
---@class watchdog
local public = {}
-- check if a timer is this watchdog
---@nodiscard
---@param timer number timer event timer ID
---@param timer number event timer ID
function public.is_timer(timer) return self.wd_timer == timer end
-- satiate the beast
function public.feed()
if self.wd_timer ~= nil then
util.cancel_timer(self.wd_timer)
end
public.cancel()
self.wd_timer = util.start_timer(self.timeout)
end
-- cancel the watchdog
function public.cancel()
if self.wd_timer ~= nil then
util.cancel_timer(self.wd_timer)
end
if self.wd_timer ~= nil then util.cancel_timer(self.wd_timer) end
end
return public
@ -489,10 +449,7 @@ end
---@nodiscard
---@param period number clock period
function util.new_clock(period)
local self = {
period = period,
timer = nil
}
local self = { period = period, timer = nil }
---@class clock
local public = {}
@ -533,7 +490,7 @@ function util.new_validator()
function public.assert_range(check, min, max) valid = valid and check >= min and check <= max end
function public.assert_range_ex(check, min, max) valid = valid and check > min and check < max end
function public.assert_channel(channel) valid = valid and type(channel) == "number" and channel >= 0 and channel <= 65535 end
function public.assert_channel(channel) valid = valid and util.is_int(channel) and channel >= 0 and channel <= 65535 end
-- check if all assertions passed successfully
---@nodiscard

View File

@ -590,6 +590,9 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
end
end
-- check if the manual lockout for automatic control is active
function public.is_auto_locked() return self.auto_lock end
-- set the burn rate on behalf of automatic control
---@param rate number burn rate
---@param ramp boolean true to ramp, false to not

View File

@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.0.8"
local SUPERVISOR_VERSION = "v1.0.9"
local println = util.println
local println_ts = util.println_ts

View File

@ -498,7 +498,6 @@ function unit.new(reactor_id, num_boilers, num_turbines)
self.plc_s = nil
self.plc_i = nil
self.db.control.br100 = 0
self.db.control.lim_br100 = 0
end
-- unlink RTU unit sessions if they are closed
@ -525,12 +524,14 @@ function unit.new(reactor_id, num_boilers, num_turbines)
end
end
-- check plc formed/faulted
-- plc instance checks
if self.plc_i ~= nil then
-- check if degraded
local rps = self.plc_i.get_rps()
if rps.fault or rps.sys_fail then
self.db.control.degraded = true
end
if rps.fault or rps.sys_fail then self.db.control.degraded = true end
-- re-engage auto lock if it reconnected without it
if self.auto_engaged and not self.plc_i.is_auto_locked() then self.plc_i.auto_lock(true) end
end
-- update deltas

View File

@ -75,7 +75,7 @@ function logic.update_annunciator(self)
(next(self.plc_i.get_status()) ~= nil) and (next(self.plc_i.get_struct()) ~= nil)
-- update auto control limit
if (self.db.control.lim_br100 == 0) or ((self.db.control.lim_br100 / 100) > plc_db.mek_struct.max_burn) then
if (plc_db.mek_struct.max_burn > 0) and ((self.db.control.lim_br100 / 100) > plc_db.mek_struct.max_burn) then
self.db.control.lim_br100 = math.floor(plc_db.mek_struct.max_burn * 100)
end