Compare commits

..

16 Commits

Author SHA1 Message Date
Mikayla
7b522ae120 Merge pull request #360 from MikaylaFischler/devel
2023.10.14 Release
2023-10-14 19:46:33 -04:00
Mikayla Fischler
43d134d9ad comment clarity 2023-10-14 18:49:51 -04:00
Mikayla Fischler
24c787f47d #353 fixed auto lock not restoring on reconnect 2023-10-14 17:57:50 -04:00
Mikayla Fischler
f95ac8be8c #359 drop packets with nil distances if using trusted range feature 2023-10-14 12:17:25 -04:00
Mikayla Fischler
41442012c2 #357 min length of auth key and some cleanup, added config change log 2023-10-14 12:02:25 -04:00
Mikayla Fischler
6f1195dded fixed element assertion scope on frame validations 2023-10-14 11:40:36 -04:00
Mikayla Fischler
86e8feaabc #358 fixed non-networked PLC operation 2023-10-14 10:07:56 -04:00
Mikayla Fischler
670ca78a5b #356 fixed extract assert msg 2023-10-14 00:38:13 -04:00
Mikayla Fischler
73ceed0f60 #354 another reversion 2023-10-13 23:53:15 -04:00
Mikayla Fischler
8412270772 #354 some reversions 2023-10-13 23:48:30 -04:00
Mikayla Fischler
686b47898c #354 optimizations/minification 2023-10-08 12:47:51 -04:00
Mikayla
e03eaf2982 #354 type check functions 2023-10-07 20:57:24 +00:00
Mikayla
8b1775b0af Merge pull request #352 from MikaylaFischler/devel
2023.10.04 Hotfix
2023-10-04 20:03:06 -04:00
Mikayla
8bbd04d133 Update feature_request.md 2023-10-04 17:32:43 -04:00
Mikayla
26dc6ff6d1 #350 handle RPS_DISABLE ack packet on supervisor 2023-10-04 21:28:14 +00:00
Mikayla
24d190921d #349 F_ALARM_ANY rsio output added 2023-10-04 21:26:07 +00:00
14 changed files with 219 additions and 200 deletions

View File

@@ -2,7 +2,7 @@
name: Feature request name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: '' title: ''
labels: enhancement labels: "enhancement,feature request"
assignees: '' assignees: ''
--- ---

View File

@@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
local core = {} local core = {}
core.version = "2.0.0" core.version = "2.0.2"
core.flasher = flasher core.flasher = flasher
core.events = events core.events = events
@@ -115,7 +115,7 @@ end
-- extract the custom element assert message, dropping the path to the element file -- extract the custom element assert message, dropping the path to the element file
function core.extract_assert_msg(msg) 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 end
-- Interactive Field Manager -- Interactive Field Manager

View File

@@ -217,10 +217,10 @@ function element.new(args, child_offset_x, child_offset_y)
end end
-- check frame -- check frame
element.assert(f.x >= 1, "frame x not >= 1", 2) element.assert(f.x >= 1, "frame x not >= 1", 3)
element.assert(f.y >= 1, "frame y not >= 1", 2) element.assert(f.y >= 1, "frame y not >= 1", 3)
element.assert(f.w >= 1, "frame width not >= 1", 2) element.assert(f.w >= 1, "frame width not >= 1", 3)
element.assert(f.h >= 1, "frame height not >= 1", 2) element.assert(f.h >= 1, "frame height not >= 1", 3)
-- create window -- create window
protected.window = window.create(self.p_window, f.x, f.y, f.w, f.h, args.hidden ~= true) 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 CENTER = core.ALIGN.CENTER
local RIGHT = core.ALIGN.RIGHT 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 ---@class plc_configurator
local configurator = {} local configurator = {}
@@ -37,7 +42,6 @@ local style = {}
style.root = cpair(colors.black, colors.lightGray) style.root = cpair(colors.black, colors.lightGray)
style.header = cpair(colors.white, colors.gray) style.header = cpair(colors.white, colors.gray)
style.label = cpair(colors.gray, colors.lightGray)
style.colors = { style.colors = {
{ c = colors.red, hex = 0xdf4949 }, { c = colors.red, hex = 0xdf4949 },
@@ -55,6 +59,11 @@ style.colors = {
{ c = colors.gray, hex = 0x575757 } { 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 = { local tool_ctl = {
ask_config = false, ask_config = false,
has_config = false, has_config = false,
@@ -173,8 +182,6 @@ end
-- create the config view -- create the config view
---@param display graphics_element ---@param display graphics_element
local function config_view(display) 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 ---@diagnostic disable-next-line: undefined-field
local function exit() os.queueEvent("terminate") end 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 net_cfg = Div{parent=root_pane_div,x=1,y=1}
local log_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 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 -- 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."} 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 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)} 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 + 3 y_start = y_start + 5
end end
local function view_config() 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 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=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 -- 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_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=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)} 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} 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=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 #"} 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} 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} 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=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)} 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"} 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 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 if ini_cfg.EmerCoolColor == nil then color.disable() end
local function submit_emcool() 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_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=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"} 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)} 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=cpair(colors.gray,colors.lightGray)} 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"} 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)} 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=cpair(colors.gray,colors.lightGray)} 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} 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} 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"} 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)} 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=cpair(colors.gray,colors.lightGray)} 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=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=g_lg_fg_bg}
TextBox{parent=net_c_2,x=1,y=8,height=1,text_align=CENTER,text="Trusted Range"} 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)} 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=cpair(colors.gray,colors.lightGray)} 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} 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} 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=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"} 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 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) hide_key.set_value(true)
censor_key(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() local function submit_auth()
tmp_cfg.AuthKey = key.get_value() local v = key.get_value()
main_pane.set_value(4) 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 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} 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} 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"} 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)} 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} 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)} 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() local function back_from_settings()
if tool_ctl.viewing_config or tool_ctl.importing_legacy then 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=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)} 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 -- set tool functions now that we have the elements
function tool_ctl.set_networked(enable) 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 f[1] == "EmerCoolColor" and raw ~= nil then val = color_name_map[raw] end
if val == "nil" then val = "n/a" 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 alternate = not alternate
if string.len(val) > val_max_w then 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_bool(config.Networked)
cfv.assert_type_int(config.UnitID) cfv.assert_type_int(config.UnitID)
cfv.assert_type_bool(config.EmerCoolEnable) cfv.assert_type_bool(config.EmerCoolEnable)
cfv.assert_channel(config.SVR_Channel)
cfv.assert_channel(config.PLC_Channel) if config.Networked == true then
cfv.assert_type_int(config.ConnTimeout) cfv.assert_channel(config.SVR_Channel)
cfv.assert_min(config.ConnTimeout, 2) cfv.assert_channel(config.PLC_Channel)
cfv.assert_type_num(config.TrustedRange) cfv.assert_type_int(config.ConnTimeout)
cfv.assert_min(config.TrustedRange, 0) cfv.assert_min(config.ConnTimeout, 2)
cfv.assert_type_str(config.AuthKey) 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_int(config.LogMode)
cfv.assert_type_str(config.LogPath) cfv.assert_type_str(config.LogPath)
cfv.assert_type_bool(config.LogDebug) 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 renderer = require("reactor-plc.renderer")
local threads = require("reactor-plc.threads") 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 = util.println
local println_ts = util.println_ts local println_ts = util.println_ts
@@ -50,7 +50,7 @@ log.info("BOOTING reactor-plc.startup " .. R_PLC_VERSION)
log.info("========================================") log.info("========================================")
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<") println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
crash.set_env("plc", R_PLC_VERSION) crash.set_env("reactor-plc", R_PLC_VERSION)
---------------------------------------- ----------------------------------------
-- main application -- main application
@@ -69,7 +69,7 @@ local function main()
ppm.mount_all() ppm.mount_all()
-- message authentication init -- 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) network.init_mac(config.AuthKey)
end end

View File

@@ -16,8 +16,8 @@ local max_distance = nil
---@class comms ---@class comms
local comms = {} local comms = {}
-- protocol version (non-protocol changes tracked by util.lua version) -- protocol/data version (protocol/data independent changes tracked by util.lua version)
comms.version = "2.4.0" comms.version = "2.4.1"
---@enum PROTOCOL ---@enum PROTOCOL
local PROTOCOL = { local PROTOCOL = {
@@ -311,7 +311,7 @@ function comms.authd_packet()
self.valid = false self.valid = false
self.raw = self.modem_msg_in.msg 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 -- outside of maximum allowable transmission distance
-- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)") -- log.debug("comms.authd_packet.receive(): discarding packet with distance " .. distance .. " (outside trusted range)")
else else

View File

@@ -7,9 +7,7 @@ local util = require("scada-common.util")
---@class rsio ---@class rsio
local rsio = {} local rsio = {}
---------------------- --#region RS I/O Constants
-- RS I/O CONSTANTS --
----------------------
---@enum IO_LVL I/O logic level ---@enum IO_LVL I/O logic level
local IO_LVL = { local IO_LVL = {
@@ -53,30 +51,31 @@ local IO_PORT = {
-- facility -- facility
F_ALARM = 7, -- active high, facility-wide alarm (any high priority unit alarm) F_ALARM = 7, -- active high, facility-wide alarm (any high priority unit alarm)
F_ALARM_ANY = 8, -- active high, any alarm regardless of priority
-- waste -- waste
WASTE_PU = 8, -- active low, waste -> plutonium -> pellets route WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
WASTE_PO = 9, -- active low, waste -> polonium route WASTE_PO = 10, -- active low, waste -> polonium route
WASTE_POPL = 10, -- active low, polonium -> pellets route WASTE_POPL = 11, -- active low, polonium -> pellets route
WASTE_AM = 11, -- active low, polonium -> anti-matter route WASTE_AM = 12, -- active low, polonium -> anti-matter route
-- reactor -- reactor
R_ACTIVE = 12, -- active high, if the reactor is active R_ACTIVE = 13, -- active high, reactor is active
R_AUTO_CTRL = 13, -- active high, if the reactor burn rate is automatic R_AUTO_CTRL = 14, -- active high, reactor burn rate is automatic
R_SCRAMMED = 14, -- active high, if the reactor is scrammed R_SCRAMMED = 15, -- active high, reactor is scrammed
R_AUTO_SCRAM = 15, -- active high, if the reactor was automatically scrammed R_AUTO_SCRAM = 16, -- active high, reactor was automatically scrammed
R_HIGH_DMG = 16, -- active high, if the reactor damage is high R_HIGH_DMG = 17, -- active high, reactor damage is high
R_HIGH_TEMP = 17, -- active high, if the reactor is at a high temperature R_HIGH_TEMP = 18, -- active high, reactor is at a high temperature
R_LOW_COOLANT = 18, -- active high, if the reactor has very low coolant R_LOW_COOLANT = 19, -- active high, reactor has very low coolant
R_EXCESS_HC = 19, -- active high, if the reactor has excess heated coolant R_EXCESS_HC = 20, -- active high, reactor has excess heated coolant
R_EXCESS_WS = 20, -- active high, if the reactor has excess waste R_EXCESS_WS = 21, -- active high, reactor has excess waste
R_INSUFF_FUEL = 21, -- active high, if the reactor has insufficent fuel R_INSUFF_FUEL = 22, -- active high, reactor has insufficent fuel
R_PLC_FAULT = 22, -- active high, if the reactor PLC reports a device access fault R_PLC_FAULT = 23, -- active high, reactor PLC reports a device access fault
R_PLC_TIMEOUT = 23, -- active high, if the reactor PLC has not been heard from R_PLC_TIMEOUT = 24, -- active high, reactor PLC has not been heard from
-- unit outputs -- unit outputs
U_ALARM = 24, -- active high, unit alarm U_ALARM = 25, -- active high, unit alarm
U_EMER_COOL = 25 -- active low, emergency coolant control U_EMER_COOL = 26 -- active low, emergency coolant control
} }
rsio.IO_LVL = IO_LVL rsio.IO_LVL = IO_LVL
@@ -84,9 +83,9 @@ rsio.IO_DIR = IO_DIR
rsio.IO_MODE = IO_MODE rsio.IO_MODE = IO_MODE
rsio.IO = IO_PORT rsio.IO = IO_PORT
----------------------- --#endregion
-- UTILITY FUNCTIONS --
----------------------- --#region Utility Functions
-- port to string -- port to string
---@nodiscard ---@nodiscard
@@ -100,6 +99,7 @@ function rsio.to_string(port)
"R_ENABLE", "R_ENABLE",
"U_ACK", "U_ACK",
"F_ALARM", "F_ALARM",
"F_ALARM_ANY",
"WASTE_PU", "WASTE_PU",
"WASTE_PO", "WASTE_PO",
"WASTE_POPL", "WASTE_POPL",
@@ -153,6 +153,8 @@ local RS_DIO_MAP = {
-- F_ALARM -- F_ALARM
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT }, { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
-- F_ALARM_ANY
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
-- WASTE_PU -- WASTE_PU
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }, { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
@@ -207,6 +209,7 @@ function rsio.get_io_mode(port)
IO_MODE.DIGITAL_IN, -- R_ENABLE IO_MODE.DIGITAL_IN, -- R_ENABLE
IO_MODE.DIGITAL_IN, -- U_ACK IO_MODE.DIGITAL_IN, -- U_ACK
IO_MODE.DIGITAL_OUT, -- F_ALARM IO_MODE.DIGITAL_OUT, -- F_ALARM
IO_MODE.DIGITAL_OUT, -- F_ALARM_ANY
IO_MODE.DIGITAL_OUT, -- WASTE_PU IO_MODE.DIGITAL_OUT, -- WASTE_PU
IO_MODE.DIGITAL_OUT, -- WASTE_PO IO_MODE.DIGITAL_OUT, -- WASTE_PO
IO_MODE.DIGITAL_OUT, -- WASTE_POPL IO_MODE.DIGITAL_OUT, -- WASTE_POPL
@@ -234,9 +237,9 @@ function rsio.get_io_mode(port)
end end
end end
-------------------- --#endregion
-- GENERIC CHECKS --
-------------------- --#region Generic Checks
local RS_SIDES = rs.getSides() local RS_SIDES = rs.getSides()
@@ -269,9 +272,9 @@ function rsio.is_color(color)
return util.is_int(color) and (color > 0) and (_B_AND(color, (color - 1)) == 0) return util.is_int(color) and (color > 0) and (_B_AND(color, (color - 1)) == 0)
end end
----------------- --#endregion
-- DIGITAL I/O --
----------------- --#region Digital I/O
-- get digital I/O level reading from a redstone boolean input value -- get digital I/O level reading from a redstone boolean input value
---@nodiscard ---@nodiscard
@@ -285,9 +288,7 @@ end
---@nodiscard ---@nodiscard
---@param level IO_LVL logic level ---@param level IO_LVL logic level
---@return boolean ---@return boolean
function rsio.digital_write(level) function rsio.digital_write(level) return level == IO_LVL.HIGH end
return level == IO_LVL.HIGH
end
-- returns the level corresponding to active -- returns the level corresponding to active
---@nodiscard ---@nodiscard
@@ -317,9 +318,9 @@ function rsio.digital_is_active(port, level)
end end
end end
---------------- --#endregion
-- ANALOG I/O --
---------------- --#region Analog I/O
-- read an analog value scaled from min to max -- read an analog value scaled from min to max
---@nodiscard ---@nodiscard
@@ -343,4 +344,6 @@ function rsio.analog_write(value, min, max)
return math.floor(scaled_value * 15) return math.floor(scaled_value * 15)
end end
--#endregion
return rsio return rsio

View File

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

View File

@@ -746,18 +746,21 @@ function facility.new(num_reactors, cooling_conf)
-- handle facility ack -- handle facility ack
if self.io_ctl.digital_read(IO.F_ACK) then public.ack_all() end if self.io_ctl.digital_read(IO.F_ACK) then public.ack_all() end
-- update facility alarm output (check if emergency+ alarms are active) -- update facility alarm outputs
local has_alarm = false local has_prio_alarm, has_any_alarm = false, false
for i = 1, #self.units do for i = 1, #self.units do
local u = self.units[i] ---@type reactor_unit local u = self.units[i] ---@type reactor_unit
if u.has_alarm_min_prio(PRIO.EMERGENCY) then if u.has_alarm_min_prio(PRIO.EMERGENCY) then
has_alarm = true has_prio_alarm, has_any_alarm = true, true
break break
elseif u.has_alarm_min_prio(PRIO.TIMELY) then
has_any_alarm = true
end end
end end
self.io_ctl.digital_write(IO.F_ALARM, has_alarm) self.io_ctl.digital_write(IO.F_ALARM, has_prio_alarm)
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
end end
---------------- ----------------

View File

@@ -390,6 +390,15 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
cmd = UNIT_COMMAND.START, cmd = UNIT_COMMAND.START,
ack = ack ack = ack
}) })
elseif pkt.type == RPLC_TYPE.RPS_DISABLE then
-- disable acknowledgement
local ack = _get_ack(pkt)
if ack then
self.acks.disable = true
self.sDB.control_state = false
elseif ack == false then
log.debug(log_header .. "disable failed!")
end
elseif pkt.type == RPLC_TYPE.RPS_SCRAM then elseif pkt.type == RPLC_TYPE.RPS_SCRAM then
-- manual SCRAM acknowledgement -- manual SCRAM acknowledgement
local ack = _get_ack(pkt) local ack = _get_ack(pkt)
@@ -581,6 +590,9 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
end end
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 -- set the burn rate on behalf of automatic control
---@param rate number burn rate ---@param rate number burn rate
---@param ramp boolean true to ramp, false to not ---@param ramp boolean true to ramp, false to not
@@ -791,7 +803,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
-- reactor disable request retry -- reactor disable request retry
if not self.acks.disable then if not self.acks.disable then
if rtimes.disable_req - util.time() <= 0 then if rtimes.disable_req - util.time() <= 0 then
_send(RPLC_TYPE.RPS_DISABLE, {}) _send(RPLC_TYPE.RPS_DISABLE, {})
rtimes.disable_req = util.time() + RETRY_PERIOD rtimes.disable_req = util.time() + RETRY_PERIOD
end end
@@ -800,7 +812,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
-- SCRAM request retry -- SCRAM request retry
if not self.acks.scram then if not self.acks.scram then
if rtimes.scram_req - util.time() <= 0 then if rtimes.scram_req - util.time() <= 0 then
_send(RPLC_TYPE.RPS_SCRAM, {}) _send(RPLC_TYPE.RPS_SCRAM, {})
rtimes.scram_req = util.time() + RETRY_PERIOD rtimes.scram_req = util.time() + RETRY_PERIOD
end end

View File

@@ -21,7 +21,7 @@ local supervisor = require("supervisor.supervisor")
local svsessions = require("supervisor.session.svsessions") local svsessions = require("supervisor.session.svsessions")
local SUPERVISOR_VERSION = "v1.0.6" local SUPERVISOR_VERSION = "v1.0.9"
local println = util.println local println = util.println
local println_ts = util.println_ts 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_s = nil
self.plc_i = nil self.plc_i = nil
self.db.control.br100 = 0 self.db.control.br100 = 0
self.db.control.lim_br100 = 0
end end
-- unlink RTU unit sessions if they are closed -- unlink RTU unit sessions if they are closed
@@ -525,12 +524,14 @@ function unit.new(reactor_id, num_boilers, num_turbines)
end end
end end
-- check plc formed/faulted -- plc instance checks
if self.plc_i ~= nil then if self.plc_i ~= nil then
-- check if degraded
local rps = self.plc_i.get_rps() local rps = self.plc_i.get_rps()
if rps.fault or rps.sys_fail then if rps.fault or rps.sys_fail then self.db.control.degraded = true end
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 end
-- update deltas -- 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) (next(self.plc_i.get_status()) ~= nil) and (next(self.plc_i.get_struct()) ~= nil)
-- update auto control limit -- 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) self.db.control.lim_br100 = math.floor(plc_db.mek_struct.max_burn * 100)
end end