Compare commits
162 Commits
v1.8.8-bet
...
v1.8.15-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afc38e7e7a | ||
|
|
41b7a68f3e | ||
|
|
8968ebede3 | ||
|
|
50a9168b06 | ||
|
|
76e85da9d5 | ||
|
|
be560cd532 | ||
|
|
2b0a536292 | ||
|
|
afed6f514d | ||
|
|
3e6a0a8869 | ||
|
|
b99cf19be0 | ||
|
|
7f009f9c86 | ||
|
|
6a8ed311f3 | ||
|
|
c181142f75 | ||
|
|
0cb964a177 | ||
|
|
3cd832ca20 | ||
|
|
0c6c7bf9a5 | ||
|
|
3bfcb1d83c | ||
|
|
4fe6792804 | ||
|
|
25dc47d520 | ||
|
|
f958b0e3b7 | ||
|
|
f621ff2482 | ||
|
|
eb45ff899b | ||
|
|
e91fd2fcaa | ||
|
|
d35b824458 | ||
|
|
165d1497f8 | ||
|
|
50bf057ca6 | ||
|
|
6f768ef6b3 | ||
|
|
826086951e | ||
|
|
35bf56663f | ||
|
|
7b8cea4a5c | ||
|
|
51d4a22532 | ||
|
|
fb85c2f05b | ||
|
|
712d018806 | ||
|
|
00a8d64a88 | ||
|
|
d9efd5b8d2 | ||
|
|
a786404092 | ||
|
|
1bd03c0b1a | ||
|
|
0d8d7aeb15 | ||
|
|
0b60dc9fa4 | ||
|
|
48b91ac808 | ||
|
|
4c9efc78b1 | ||
|
|
3c5857cd68 | ||
|
|
473b0dd10d | ||
|
|
d2df7db18b | ||
|
|
0ca002316f | ||
|
|
9fe34648c2 | ||
|
|
57dc20692b | ||
|
|
f22d699baf | ||
|
|
23b31e0049 | ||
|
|
2b4309afa7 | ||
|
|
89cd5a07f2 | ||
|
|
99213da760 | ||
|
|
f23f69d441 | ||
|
|
a99b7912f8 | ||
|
|
23ca5fb69e | ||
|
|
c243d064ef | ||
|
|
878c3b92e1 | ||
|
|
2aa52d2e2c | ||
|
|
dfc1ee6497 | ||
|
|
a6a1a61954 | ||
|
|
d0b50c834c | ||
|
|
612a06ba98 | ||
|
|
65d43d55c7 | ||
|
|
4fcd375ee2 | ||
|
|
a22f5562cf | ||
|
|
0365ea5e8a | ||
|
|
da300607f1 | ||
|
|
cc3174ee76 | ||
|
|
98c37caecd | ||
|
|
cc50e4a020 | ||
|
|
f734c4307b | ||
|
|
45573be8c7 | ||
|
|
eab1ffbe03 | ||
|
|
1504247b33 | ||
|
|
92a4277f73 | ||
|
|
31a663e86b | ||
|
|
5c0e2c32ee | ||
|
|
3537c59365 | ||
|
|
1d7104ae74 | ||
|
|
4629e1ba2a | ||
|
|
659644865a | ||
|
|
03d2b3f087 | ||
|
|
bea7b91ff1 | ||
|
|
b94c89f4ec | ||
|
|
55e4e5a68b | ||
|
|
e1ad76a00d | ||
|
|
93e4590947 | ||
|
|
2442e7f972 | ||
|
|
bb2c07963b | ||
|
|
44c6352a8c | ||
|
|
968b0a9122 | ||
|
|
19869416af | ||
|
|
598f6f08af | ||
|
|
ca2d8ab7da | ||
|
|
1c0f61b3e0 | ||
|
|
ecd3575643 | ||
|
|
9dc3a09f4d | ||
|
|
7a5d14d67f | ||
|
|
d6175e5cec | ||
|
|
a00a824a7f | ||
|
|
9393632428 | ||
|
|
886bd0d5d5 | ||
|
|
9c1d83fdfc | ||
|
|
bd88244681 | ||
|
|
8dae632b25 | ||
|
|
89d56d3101 | ||
|
|
1f451ff92a | ||
|
|
9d08b51f84 | ||
|
|
047ff5c203 | ||
|
|
895f768e58 | ||
|
|
b3f29566ea | ||
|
|
c0a5c8d504 | ||
|
|
bbe7b52662 | ||
|
|
d5b166dcc6 | ||
|
|
b0aa6d54ac | ||
|
|
ad240ae44c | ||
|
|
79c93f1562 | ||
|
|
5d760a0524 | ||
|
|
6c89b3134c | ||
|
|
3c7fff28c9 | ||
|
|
814043bf04 | ||
|
|
fc7896ebd3 | ||
|
|
48a8eadc55 | ||
|
|
f0f2aadf53 | ||
|
|
ce37a672a3 | ||
|
|
510995b04f | ||
|
|
560061d4ad | ||
|
|
d87e3893f0 | ||
|
|
fc198cd9d2 | ||
|
|
c714e49ad8 | ||
|
|
d1e4ea586e | ||
|
|
4e789ab92d | ||
|
|
0892a57d35 | ||
|
|
1bc4828010 | ||
|
|
fb5a9d5d9e | ||
|
|
adbf1f2f78 | ||
|
|
2f99aaeedb | ||
|
|
a318ffb283 | ||
|
|
a677e994d6 | ||
|
|
f9917b786c | ||
|
|
dbc1f41c5d | ||
|
|
d6185e0183 | ||
|
|
6ef049baa1 | ||
|
|
a4214e8a4f | ||
|
|
45881067df | ||
|
|
c40aa229bf | ||
|
|
51f2bba4d1 | ||
|
|
628a50e1bd | ||
|
|
cdd31508d9 | ||
|
|
83d62991f8 | ||
|
|
f207a950e4 | ||
|
|
0b0051dc2f | ||
|
|
f152c37ea9 | ||
|
|
372fd426d8 | ||
|
|
526a54903e | ||
|
|
e1a632dcc7 | ||
|
|
d5d818e625 | ||
|
|
03de90c3d8 | ||
|
|
99096e0fc9 | ||
|
|
0395aa95b6 | ||
|
|
c624baed2c | ||
|
|
1a40321c0f |
12
README.md
12
README.md
@@ -4,12 +4,11 @@ Configurable ComputerCraft SCADA system for multi-reactor control of Mekanism fi
|
|||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||

|

|
||||||
|
|
||||||
### [Join](https://discord.gg/R9NSCkhcwt) the Discord!
|
### Join [the Discord](https://discord.gg/R9NSCkhcwt)!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Released Component Versions
|
## Released Component Versions
|
||||||
|
|
||||||
@@ -17,6 +16,7 @@ Configurable ComputerCraft SCADA system for multi-reactor control of Mekanism fi
|
|||||||
|
|
||||||
|
|
||||||

|

|
||||||
|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
@@ -46,6 +46,12 @@ You can install this on a ComputerCraft computer using either:
|
|||||||
* `wget https://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/main/ccmsi.lua`
|
* `wget https://raw.githubusercontent.com/MikaylaFischler/cc-mek-scada/main/ccmsi.lua`
|
||||||
* `pastebin get sqUN6VUb ccmsi.lua`
|
* `pastebin get sqUN6VUb ccmsi.lua`
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please reach out to me via Discord or email (or GitHub in some way) if you are thinking of making any contributions at this time. I started this project as a challenge for myself and have been enjoying having something I can work on in my own way.
|
||||||
|
|
||||||
|
Once this is out of beta I will be more open to contributions, but for now I am hoping to keep them to a minimum as the remaining challenges are ones I am looking forward to solving.
|
||||||
|
|
||||||
## [SCADA](https://en.wikipedia.org/wiki/SCADA)
|
## [SCADA](https://en.wikipedia.org/wiki/SCADA)
|
||||||
> Supervisory control and data acquisition (SCADA) is a control system architecture comprising computers, networked data communications and graphical user interfaces for high-level supervision of machines and processes. It also covers sensors and other devices, such as programmable logic controllers, which interface with process plant or machinery.
|
> Supervisory control and data acquisition (SCADA) is a control system architecture comprising computers, networked data communications and graphical user interfaces for high-level supervision of machines and processes. It also covers sensors and other devices, such as programmable logic controllers, which interface with process plant or machinery.
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local network = require("scada-common.network")
|
|||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
local tcd = require("scada-common.tcd")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
@@ -24,6 +25,8 @@ local RadioButton = require("graphics.elements.controls.radio_button")
|
|||||||
local NumberField = require("graphics.elements.form.number_field")
|
local NumberField = require("graphics.elements.form.number_field")
|
||||||
local TextField = require("graphics.elements.form.text_field")
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local IndLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local tri = util.trinary
|
local tri = util.trinary
|
||||||
|
|
||||||
@@ -39,7 +42,11 @@ 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
|
-- changes to the config data/format to let the user know
|
||||||
local changes = {}
|
local changes = {
|
||||||
|
{ "v1.2.4", { "Added temperature scale options" } },
|
||||||
|
{ "v1.2.12", { "Added main UI theme", "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
|
{ "v1.3.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
||||||
|
}
|
||||||
|
|
||||||
---@class crd_configurator
|
---@class crd_configurator
|
||||||
local configurator = {}
|
local configurator = {}
|
||||||
@@ -49,21 +56,7 @@ 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.colors = {
|
style.colors = themes.smooth_stone.colors
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
@@ -71,6 +64,7 @@ local nav_fg_bg = bw_fg_bg
|
|||||||
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
||||||
local dis_fg_bg = cpair(colors.lightGray,colors.white)
|
local dis_fg_bg = cpair(colors.lightGray,colors.white)
|
||||||
|
|
||||||
|
---@class _crd_cfg_tool_ctl
|
||||||
local tool_ctl = {
|
local tool_ctl = {
|
||||||
nic = nil, ---@type nic
|
nic = nil, ---@type nic
|
||||||
net_listen = false,
|
net_listen = false,
|
||||||
@@ -84,8 +78,12 @@ local tool_ctl = {
|
|||||||
has_config = false,
|
has_config = false,
|
||||||
viewing_config = false,
|
viewing_config = false,
|
||||||
importing_legacy = false,
|
importing_legacy = false,
|
||||||
|
jumped_to_color = false,
|
||||||
|
|
||||||
view_cfg = nil, ---@type graphics_element
|
view_cfg = nil, ---@type graphics_element
|
||||||
|
color_cfg = nil, ---@type graphics_element
|
||||||
|
color_next = nil, ---@type graphics_element
|
||||||
|
color_apply = nil, ---@type graphics_element
|
||||||
settings_apply = nil, ---@type graphics_element
|
settings_apply = nil, ---@type graphics_element
|
||||||
|
|
||||||
gen_summary = nil, ---@type function
|
gen_summary = nil, ---@type function
|
||||||
@@ -119,6 +117,7 @@ local tmp_cfg = {
|
|||||||
UnitCount = 1,
|
UnitCount = 1,
|
||||||
SpeakerVolume = 1.0,
|
SpeakerVolume = 1.0,
|
||||||
Time24Hour = true,
|
Time24Hour = true,
|
||||||
|
TempScale = 1,
|
||||||
DisableFlowView = false,
|
DisableFlowView = false,
|
||||||
MainDisplay = nil, ---@type string
|
MainDisplay = nil, ---@type string
|
||||||
FlowDisplay = nil, ---@type string
|
FlowDisplay = nil, ---@type string
|
||||||
@@ -133,6 +132,9 @@ local tmp_cfg = {
|
|||||||
LogMode = 0,
|
LogMode = 0,
|
||||||
LogPath = "",
|
LogPath = "",
|
||||||
LogDebug = false,
|
LogDebug = false,
|
||||||
|
MainTheme = 1,
|
||||||
|
FrontPanelTheme = 1,
|
||||||
|
ColorMode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class crd_config
|
---@class crd_config
|
||||||
@@ -148,6 +150,7 @@ local fields = {
|
|||||||
{ "UnitDisplays", "Unit Monitors", {} },
|
{ "UnitDisplays", "Unit Monitors", {} },
|
||||||
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
{ "SpeakerVolume", "Speaker Volume", 1.0 },
|
||||||
{ "Time24Hour", "Use 24-hour Time Format", true },
|
{ "Time24Hour", "Use 24-hour Time Format", true },
|
||||||
|
{ "TempScale", "Temperature Scale", 1 },
|
||||||
{ "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false },
|
{ "DisableFlowView", "Disable Flow Monitor (legacy, discouraged)", false },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "CRD_Channel", "CRD Channel", 16243 },
|
{ "CRD_Channel", "CRD Channel", 16243 },
|
||||||
@@ -158,7 +161,10 @@ local fields = {
|
|||||||
{ "AuthKey", "Facility Auth Key" , ""},
|
{ "AuthKey", "Facility Auth Key" , ""},
|
||||||
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
||||||
{ "LogPath", "Log Path", "/log.txt" },
|
{ "LogPath", "Log Path", "/log.txt" },
|
||||||
{ "LogDebug","Log Debug Messages", false }
|
{ "LogDebug", "Log Debug Messages", false },
|
||||||
|
{ "MainTheme", "Main UI Theme", themes.UI_THEME.SMOOTH_STONE },
|
||||||
|
{ "FrontPanelTheme", "Front Panel Theme", themes.FP_THEME.SANDSTONE },
|
||||||
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- check if a value is an integer within a range (inclusive)
|
-- check if a value is an integer within a range (inclusive)
|
||||||
@@ -309,10 +315,11 @@ local function config_view(display)
|
|||||||
local spkr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local spkr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local crd_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local crd_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 clr_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 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,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,summary,changelog}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,net_cfg,fac_cfg,mon_cfg,spkr_cfg,crd_cfg,log_cfg,clr_cfg,summary,changelog}}
|
||||||
|
|
||||||
-- Main Page
|
-- Main Page
|
||||||
|
|
||||||
@@ -333,7 +340,7 @@ local function config_view(display)
|
|||||||
tool_ctl.viewing_config = true
|
tool_ctl.viewing_config = true
|
||||||
tool_ctl.gen_summary(settings_cfg)
|
tool_ctl.gen_summary(settings_cfg)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
main_pane.set_value(8)
|
main_pane.set_value(9)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists("/coordinator/config.lua") then
|
if fs.exists("/coordinator/config.lua") then
|
||||||
@@ -344,10 +351,21 @@ local function config_view(display)
|
|||||||
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=dis_fg_bg}
|
||||||
|
|
||||||
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
local function jump_color()
|
||||||
|
tool_ctl.jumped_to_color = true
|
||||||
|
tool_ctl.color_next.hide(true)
|
||||||
|
tool_ctl.color_apply.show()
|
||||||
|
main_pane.set_value(8)
|
||||||
|
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(9)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(10)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
if not tool_ctl.has_config then
|
||||||
|
tool_ctl.view_cfg.disable()
|
||||||
|
tool_ctl.color_cfg.disable()
|
||||||
|
end
|
||||||
|
|
||||||
--#region Network
|
--#region Network
|
||||||
|
|
||||||
@@ -465,7 +483,7 @@ local function config_view(display)
|
|||||||
key_err.hide(true)
|
key_err.hide(true)
|
||||||
|
|
||||||
-- init mac for supervisor connection
|
-- init mac for supervisor connection
|
||||||
if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) end
|
if string.len(v) >= 8 then network.init_mac(tmp_cfg.AuthKey) else network.deinit_mac() end
|
||||||
|
|
||||||
main_pane.set_value(3)
|
main_pane.set_value(3)
|
||||||
|
|
||||||
@@ -693,7 +711,7 @@ local function config_view(display)
|
|||||||
mon_pane.set_value(1)
|
mon_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=mon_c_4,x=1,y=14,text="\x1b Back",callback=back_from_legacy,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=mon_c_4,x=44,y=14,min_width=6,text="Done",callback=back_from_legacy,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
@@ -730,8 +748,6 @@ local function config_view(display)
|
|||||||
|
|
||||||
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
local crd_c_1 = Div{parent=crd_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local crd_pane = MultiPane{parent=crd_cfg,x=1,y=4,panes={crd_c_1}}
|
|
||||||
|
|
||||||
TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
TextBox{parent=crd_cfg,x=1,y=2,height=1,text=" Coordinator UI Configuration",fg_bg=cpair(colors.black,colors.lime)}
|
||||||
|
|
||||||
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."}
|
TextBox{parent=crd_c_1,x=1,y=1,height=3,text="Configure the UI interface options below if you wish to customize formats."}
|
||||||
@@ -739,8 +755,12 @@ local function config_view(display)
|
|||||||
TextBox{parent=crd_c_1,x=1,y=4,height=1,text="Clock Time Format"}
|
TextBox{parent=crd_c_1,x=1,y=4,height=1,text="Clock Time Format"}
|
||||||
local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
local clock_fmt = RadioButton{parent=crd_c_1,x=1,y=5,default=util.trinary(ini_cfg.Time24Hour,1,2),options={"24-Hour","12-Hour"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
|
TextBox{parent=crd_c_1,x=1,y=8,height=1,text="Temperature Scale"}
|
||||||
|
local temp_scale = RadioButton{parent=crd_c_1,x=1,y=9,default=ini_cfg.TempScale,options={"Kelvin","Celsius","Fahrenheit","Rankine"},callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.lime}
|
||||||
|
|
||||||
local function submit_ui_opts()
|
local function submit_ui_opts()
|
||||||
tmp_cfg.Time24Hour = clock_fmt.get_value() == 1
|
tmp_cfg.Time24Hour = clock_fmt.get_value() == 1
|
||||||
|
tmp_cfg.TempScale = temp_scale.get_value()
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(7)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -774,10 +794,8 @@ local function config_view(display)
|
|||||||
tmp_cfg.LogMode = mode.get_value() - 1
|
tmp_cfg.LogMode = mode.get_value() - 1
|
||||||
tmp_cfg.LogPath = path.get_value()
|
tmp_cfg.LogPath = path.get_value()
|
||||||
tmp_cfg.LogDebug = en_dbg.get_value()
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.color_apply.hide(true)
|
||||||
tool_ctl.viewing_config = false
|
tool_ctl.color_next.show()
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
main_pane.set_value(8)
|
main_pane.set_value(8)
|
||||||
else path_err.show() end
|
else path_err.show() end
|
||||||
end
|
end
|
||||||
@@ -787,6 +805,121 @@ local function config_view(display)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Color Options
|
||||||
|
|
||||||
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color themes for the different UI displays."}
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Main UI Theme"}
|
||||||
|
local main_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.MainTheme,options=themes.UI_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=18,y=7,height=1,text="Front Panel Theme"}
|
||||||
|
local fp_theme = RadioButton{parent=clr_c_1,x=18,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
||||||
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
||||||
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
||||||
|
|
||||||
|
local function recolor(value)
|
||||||
|
local c = themes.smooth_stone.color_modes[value]
|
||||||
|
|
||||||
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
b_off.hide()
|
||||||
|
g_off.show()
|
||||||
|
else
|
||||||
|
g_off.hide()
|
||||||
|
b_off.show()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #c == 0 then
|
||||||
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
||||||
|
else
|
||||||
|
term.setPaletteColor(colors.green, c[1].hex)
|
||||||
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
||||||
|
term.setPaletteColor(colors.red, c[3].hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
||||||
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function back_from_colors()
|
||||||
|
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 7))
|
||||||
|
tool_ctl.jumped_to_color = false
|
||||||
|
recolor(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_access()
|
||||||
|
clr_pane.set_value(2)
|
||||||
|
recolor(c_mode.get_value())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_colors()
|
||||||
|
tmp_cfg.MainTheme = main_theme.get_value()
|
||||||
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
||||||
|
tmp_cfg.ColorMode = c_mode.get_value()
|
||||||
|
|
||||||
|
if tool_ctl.jumped_to_color then
|
||||||
|
settings.set("MainTheme", tmp_cfg.MainTheme)
|
||||||
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
||||||
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
||||||
|
|
||||||
|
if settings.save("/coordinator.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
clr_pane.set_value(3)
|
||||||
|
else
|
||||||
|
clr_pane.set_value(4)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
tool_ctl.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
main_pane.set_value(9)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
|
local function c_go_home()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
||||||
|
PushButton{parent=clr_c_3,x=1,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=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=clr_c_4,x=1,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=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Summary and Saving
|
--#region Summary and Saving
|
||||||
|
|
||||||
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
@@ -807,7 +940,7 @@ local function config_view(display)
|
|||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
tool_ctl.settings_apply.show()
|
tool_ctl.settings_apply.show()
|
||||||
else
|
else
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -838,12 +971,16 @@ local function config_view(display)
|
|||||||
try_set(mode, ini_cfg.LogMode)
|
try_set(mode, ini_cfg.LogMode)
|
||||||
try_set(path, ini_cfg.LogPath)
|
try_set(path, ini_cfg.LogPath)
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
|
try_set(main_theme, ini_cfg.MainTheme)
|
||||||
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
||||||
|
try_set(c_mode, ini_cfg.ColorMode)
|
||||||
|
|
||||||
preset_monitor_fields()
|
preset_monitor_fields()
|
||||||
|
|
||||||
tool_ctl.gen_mon_list()
|
tool_ctl.gen_mon_list()
|
||||||
|
|
||||||
tool_ctl.view_cfg.enable()
|
tool_ctl.view_cfg.enable()
|
||||||
|
tool_ctl.color_cfg.enable()
|
||||||
|
|
||||||
if tool_ctl.importing_legacy then
|
if tool_ctl.importing_legacy then
|
||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
@@ -867,7 +1004,7 @@ local function config_view(display)
|
|||||||
net_pane.set_value(1)
|
net_pane.set_value(1)
|
||||||
fac_pane.set_value(1)
|
fac_pane.set_value(1)
|
||||||
mon_pane.set_value(1)
|
mon_pane.set_value(1)
|
||||||
crd_pane.set_value(1)
|
clr_pane.set_value(1)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -964,7 +1101,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
main_pane.set_value(8)
|
main_pane.set_value(9)
|
||||||
tool_ctl.importing_legacy = true
|
tool_ctl.importing_legacy = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1109,6 +1246,10 @@ local function config_view(display)
|
|||||||
function tool_ctl.gen_mon_list()
|
function tool_ctl.gen_mon_list()
|
||||||
mon_list.remove_all()
|
mon_list.remove_all()
|
||||||
|
|
||||||
|
local missing = { main = tmp_cfg.MainDisplay ~= nil, flow = tmp_cfg.FlowDisplay ~= nil, unit = {} }
|
||||||
|
for i = 1, tmp_cfg.UnitCount do missing.unit[i] = tmp_cfg.UnitDisplays[i] ~= nil end
|
||||||
|
|
||||||
|
-- list connected monitors
|
||||||
local monitors = ppm.get_monitor_list()
|
local monitors = ppm.get_monitor_list()
|
||||||
for iface, device in pairs(monitors) do
|
for iface, device in pairs(monitors) do
|
||||||
local dev = device.dev
|
local dev = device.dev
|
||||||
@@ -1128,11 +1269,14 @@ local function config_view(display)
|
|||||||
|
|
||||||
if tmp_cfg.MainDisplay == iface then
|
if tmp_cfg.MainDisplay == iface then
|
||||||
assignment = "Main"
|
assignment = "Main"
|
||||||
|
missing.main = false
|
||||||
elseif tmp_cfg.FlowDisplay == iface then
|
elseif tmp_cfg.FlowDisplay == iface then
|
||||||
assignment = "Flow"
|
assignment = "Flow"
|
||||||
|
missing.flow = false
|
||||||
else
|
else
|
||||||
for i = 1, tmp_cfg.UnitCount do
|
for i = 1, tmp_cfg.UnitCount do
|
||||||
if tmp_cfg.UnitDisplays[i] == iface then
|
if tmp_cfg.UnitDisplays[i] == iface then
|
||||||
|
missing.unit[i] = false
|
||||||
assignment = "Unit " .. i
|
assignment = "Unit " .. i
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -1157,6 +1301,31 @@ local function config_view(display)
|
|||||||
|
|
||||||
if assignment == "Unused" then unset.disable() end
|
if assignment == "Unused" then unset.disable() end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local dc_list = {} -- disconnected monitor list
|
||||||
|
|
||||||
|
if missing.main then table.insert(dc_list, { "Main", tmp_cfg.MainDisplay }) end
|
||||||
|
if missing.flow then table.insert(dc_list, { "Flow", tmp_cfg.FlowDisplay }) end
|
||||||
|
for i = 1, tmp_cfg.UnitCount do
|
||||||
|
if missing.unit[i] then table.insert(dc_list, { "Unit " .. i, tmp_cfg.UnitDisplays[i] }) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- add monitors that are assigned but not connected
|
||||||
|
for i = 1, #dc_list do
|
||||||
|
local line = Div{parent=mon_list,x=1,y=1,height=1}
|
||||||
|
|
||||||
|
TextBox{parent=line,x=1,y=1,width=6,height=1,text=dc_list[i][1],fg_bg=cpair(colors.blue,colors.white)}
|
||||||
|
TextBox{parent=line,x=8,y=1,height=1,text="disconnected",fg_bg=cpair(colors.red,colors.white)}
|
||||||
|
|
||||||
|
local function unset_mon()
|
||||||
|
purge_assignments(dc_list[i][2])
|
||||||
|
tool_ctl.gen_mon_list()
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=line,x=33,y=1,width=4,height=1,text="?x?",fg_bg=cpair(colors.black,colors.white)}
|
||||||
|
PushButton{parent=line,x=37,y=1,min_width=5,height=1,text="SET",callback=function()end,dis_fg_bg=cpair(colors.black,colors.gray)}.disable()
|
||||||
|
PushButton{parent=line,x=42,y=1,min_width=7,height=1,text="UNSET",callback=unset_mon,fg_bg=cpair(colors.black,colors.red),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.black,colors.gray)}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- expose the auth key on the summary page
|
-- expose the auth key on the summary page
|
||||||
@@ -1186,6 +1355,14 @@ local function config_view(display)
|
|||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
|
elseif f[1] == "TempScale" then
|
||||||
|
if raw == 1 then val = "Kelvin" elseif raw == 2 then val = "Celsius" elseif raw == 3 then val = "Fahrenheit" elseif raw == 4 then val = "Rankine" end
|
||||||
|
elseif f[1] == "MainTheme" then
|
||||||
|
val = util.strval(themes.ui_theme_name(raw))
|
||||||
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
|
elseif f[1] == "ColorMode" then
|
||||||
|
val = util.strval(themes.color_mode_name(raw))
|
||||||
elseif f[1] == "UnitDisplays" and type(cfg.UnitDisplays) == "table" then
|
elseif f[1] == "UnitDisplays" and type(cfg.UnitDisplays) == "table" then
|
||||||
val = ""
|
val = ""
|
||||||
for idx = 1, #cfg.UnitDisplays do
|
for idx = 1, #cfg.UnitDisplays do
|
||||||
@@ -1272,9 +1449,11 @@ function configurator.configure(start_code, message)
|
|||||||
elseif event == "paste" then
|
elseif event == "paste" then
|
||||||
display.handle_paste(param1)
|
display.handle_paste(param1)
|
||||||
elseif event == "peripheral_detach" then
|
elseif event == "peripheral_detach" then
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
ppm.handle_unmount(param1)
|
ppm.handle_unmount(param1)
|
||||||
tool_ctl.gen_mon_list()
|
tool_ctl.gen_mon_list()
|
||||||
elseif event == "peripheral" then
|
elseif event == "peripheral" then
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
ppm.mount(param1)
|
ppm.mount(param1)
|
||||||
tool_ctl.gen_mon_list()
|
tool_ctl.gen_mon_list()
|
||||||
elseif event == "monitor_resize" then
|
elseif event == "monitor_resize" then
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ local ppm = require("scada-common.ppm")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
|
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
local process = require("coordinator.process")
|
local process = require("coordinator.process")
|
||||||
|
|
||||||
@@ -35,6 +37,7 @@ function coordinator.load_config()
|
|||||||
config.UnitCount = settings.get("UnitCount")
|
config.UnitCount = settings.get("UnitCount")
|
||||||
config.SpeakerVolume = settings.get("SpeakerVolume")
|
config.SpeakerVolume = settings.get("SpeakerVolume")
|
||||||
config.Time24Hour = settings.get("Time24Hour")
|
config.Time24Hour = settings.get("Time24Hour")
|
||||||
|
config.TempScale = settings.get("TempScale")
|
||||||
|
|
||||||
config.DisableFlowView = settings.get("DisableFlowView")
|
config.DisableFlowView = settings.get("DisableFlowView")
|
||||||
config.MainDisplay = settings.get("MainDisplay")
|
config.MainDisplay = settings.get("MainDisplay")
|
||||||
@@ -53,11 +56,17 @@ function coordinator.load_config()
|
|||||||
config.LogPath = settings.get("LogPath")
|
config.LogPath = settings.get("LogPath")
|
||||||
config.LogDebug = settings.get("LogDebug")
|
config.LogDebug = settings.get("LogDebug")
|
||||||
|
|
||||||
|
config.MainTheme = settings.get("MainTheme")
|
||||||
|
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
||||||
|
config.ColorMode = settings.get("ColorMode")
|
||||||
|
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
cfv.assert_type_int(config.UnitCount)
|
cfv.assert_type_int(config.UnitCount)
|
||||||
cfv.assert_range(config.UnitCount, 1, 4)
|
cfv.assert_range(config.UnitCount, 1, 4)
|
||||||
cfv.assert_type_bool(config.Time24Hour)
|
cfv.assert_type_bool(config.Time24Hour)
|
||||||
|
cfv.assert_type_int(config.TempScale)
|
||||||
|
cfv.assert_range(config.TempScale, 1, 4)
|
||||||
|
|
||||||
cfv.assert_type_bool(config.DisableFlowView)
|
cfv.assert_type_bool(config.DisableFlowView)
|
||||||
cfv.assert_type_table(config.UnitDisplays)
|
cfv.assert_type_table(config.UnitDisplays)
|
||||||
@@ -80,7 +89,7 @@ function coordinator.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
@@ -88,13 +97,20 @@ function coordinator.load_config()
|
|||||||
cfv.assert_type_str(config.LogPath)
|
cfv.assert_type_str(config.LogPath)
|
||||||
cfv.assert_type_bool(config.LogDebug)
|
cfv.assert_type_bool(config.LogDebug)
|
||||||
|
|
||||||
|
cfv.assert_type_int(config.MainTheme)
|
||||||
|
cfv.assert_range(config.MainTheme, 1, 2)
|
||||||
|
cfv.assert_type_int(config.FrontPanelTheme)
|
||||||
|
cfv.assert_range(config.FrontPanelTheme, 1, 2)
|
||||||
|
cfv.assert_type_int(config.ColorMode)
|
||||||
|
cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
||||||
|
|
||||||
-- Monitor Setup
|
-- Monitor Setup
|
||||||
|
|
||||||
---@class monitors_struct
|
---@class monitors_struct
|
||||||
local monitors = {
|
local monitors = {
|
||||||
primary = nil, ---@type table|nil
|
main = nil, ---@type table|nil
|
||||||
primary_name = "",
|
main_name = "",
|
||||||
flow = nil, ---@type table|nil
|
flow = nil, ---@type table|nil
|
||||||
flow_name = "",
|
flow_name = "",
|
||||||
unit_displays = {},
|
unit_displays = {},
|
||||||
unit_name_map = {}
|
unit_name_map = {}
|
||||||
@@ -118,11 +134,11 @@ function coordinator.load_config()
|
|||||||
return 2, "Main monitor is not connected."
|
return 2, "Main monitor is not connected."
|
||||||
end
|
end
|
||||||
|
|
||||||
monitors.primary = ppm.get_periph(config.MainDisplay)
|
monitors.main = ppm.get_periph(config.MainDisplay)
|
||||||
monitors.primary_name = config.MainDisplay
|
monitors.main_name = config.MainDisplay
|
||||||
|
|
||||||
monitors.primary.setTextScale(0.5)
|
monitors.main.setTextScale(0.5)
|
||||||
w, _ = ppm.monitor_block_size(monitors.primary.getSize())
|
w, _ = ppm.monitor_block_size(monitors.main.getSize())
|
||||||
if w ~= 8 then
|
if w ~= 8 then
|
||||||
return 2, util.c("Main monitor width is incorrect (was ", w, ", must be 8).")
|
return 2, util.c("Main monitor width is incorrect (was ", w, ", must be 8).")
|
||||||
end
|
end
|
||||||
@@ -176,7 +192,7 @@ end
|
|||||||
---@return function? update, function? done
|
---@return function? update, function? done
|
||||||
local function log_dmesg(message, dmesg_tag, working)
|
local function log_dmesg(message, dmesg_tag, working)
|
||||||
local colors = {
|
local colors = {
|
||||||
GRAPHICS = colors.green,
|
RENDER = colors.green,
|
||||||
SYSTEM = colors.cyan,
|
SYSTEM = colors.cyan,
|
||||||
BOOT = colors.blue,
|
BOOT = colors.blue,
|
||||||
COMMS = colors.purple,
|
COMMS = colors.purple,
|
||||||
@@ -190,7 +206,7 @@ local function log_dmesg(message, dmesg_tag, working)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function coordinator.log_graphics(message) log_dmesg(message, "GRAPHICS") end
|
function coordinator.log_render(message) log_dmesg(message, "RENDER") end
|
||||||
function coordinator.log_sys(message) log_dmesg(message, "SYSTEM") end
|
function coordinator.log_sys(message) log_dmesg(message, "SYSTEM") end
|
||||||
function coordinator.log_boot(message) log_dmesg(message, "BOOT") end
|
function coordinator.log_boot(message) log_dmesg(message, "BOOT") end
|
||||||
function coordinator.log_comms(message) log_dmesg(message, "COMMS") end
|
function coordinator.log_comms(message) log_dmesg(message, "COMMS") end
|
||||||
@@ -263,11 +279,12 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
-- send an API establish request response
|
-- send an API establish request response
|
||||||
---@param packet scada_packet
|
---@param packet scada_packet
|
||||||
---@param ack ESTABLISH_ACK
|
---@param ack ESTABLISH_ACK
|
||||||
local function _send_api_establish_ack(packet, ack)
|
---@param data any?
|
||||||
|
local function _send_api_establish_ack(packet, ack, data)
|
||||||
local s_pkt = comms.scada_packet()
|
local s_pkt = comms.scada_packet()
|
||||||
local m_pkt = comms.mgmt_packet()
|
local m_pkt = comms.mgmt_packet()
|
||||||
|
|
||||||
m_pkt.make(MGMT_TYPE.ESTABLISH, { ack })
|
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())
|
s_pkt.make(packet.src_addr(), packet.seq_num() + 1, PROTOCOL.SCADA_MGMT, m_pkt.raw_sendable())
|
||||||
|
|
||||||
nic.transmit(config.PKT_Channel, config.CRD_Channel, s_pkt)
|
nic.transmit(config.PKT_Channel, config.CRD_Channel, s_pkt)
|
||||||
@@ -299,7 +316,7 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
if not self.sv_linked then
|
if not self.sv_linked then
|
||||||
if self.est_tick_waiting == nil then
|
if self.est_tick_waiting == nil then
|
||||||
self.est_start = util.time_s()
|
self.est_start = os.clock()
|
||||||
self.est_last = self.est_start
|
self.est_last = self.est_start
|
||||||
|
|
||||||
self.est_tick_waiting, self.est_task_done =
|
self.est_tick_waiting, self.est_task_done =
|
||||||
@@ -307,10 +324,10 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
_send_establish()
|
_send_establish()
|
||||||
else
|
else
|
||||||
self.est_tick_waiting(math.max(0, LINK_TIMEOUT - (util.time_s() - self.est_start)))
|
self.est_tick_waiting(math.max(0, LINK_TIMEOUT - (os.clock() - self.est_start)))
|
||||||
end
|
end
|
||||||
|
|
||||||
if abort or (util.time_s() - self.est_start) >= LINK_TIMEOUT then
|
if abort or (os.clock() - self.est_start) >= LINK_TIMEOUT then
|
||||||
self.est_task_done(false)
|
self.est_task_done(false)
|
||||||
|
|
||||||
if abort then
|
if abort then
|
||||||
@@ -331,11 +348,12 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
ok = false
|
ok = false
|
||||||
elseif self.sv_config_err then
|
elseif self.sv_config_err then
|
||||||
|
self.est_task_done(false)
|
||||||
coordinator.log_comms("supervisor unit count does not match coordinator unit count, check configs")
|
coordinator.log_comms("supervisor unit count does not match coordinator unit count, check configs")
|
||||||
ok = false
|
ok = false
|
||||||
elseif (util.time_s() - self.est_last) > 1.0 then
|
elseif (os.clock() - self.est_last) > 1.0 then
|
||||||
_send_establish()
|
_send_establish()
|
||||||
self.est_last = util.time_s()
|
self.est_last = os.clock()
|
||||||
end
|
end
|
||||||
elseif self.est_tick_waiting ~= nil then
|
elseif self.est_tick_waiting ~= nil then
|
||||||
self.est_task_done(true)
|
self.est_task_done(true)
|
||||||
@@ -454,10 +472,11 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
-- establish a new session
|
-- establish a new session
|
||||||
-- validate packet and continue
|
-- validate packet and continue
|
||||||
if packet.length == 3 and type(packet.data[1]) == "string" and type(packet.data[2]) == "string" then
|
if packet.length == 4 then
|
||||||
local comms_v = packet.data[1]
|
local comms_v = util.strval(packet.data[1])
|
||||||
local firmware_v = packet.data[2]
|
local firmware_v = util.strval(packet.data[2])
|
||||||
local dev_type = packet.data[3]
|
local dev_type = packet.data[3]
|
||||||
|
local api_v = util.strval(packet.data[4])
|
||||||
|
|
||||||
if comms_v ~= comms.version then
|
if comms_v ~= comms.version then
|
||||||
if self.last_api_est_acks[src_addr] ~= ESTABLISH_ACK.BAD_VERSION then
|
if self.last_api_est_acks[src_addr] ~= ESTABLISH_ACK.BAD_VERSION then
|
||||||
@@ -465,12 +484,19 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
end
|
end
|
||||||
|
|
||||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_VERSION)
|
||||||
|
elseif api_v ~= comms.api_version then
|
||||||
|
if self.last_api_est_acks[src_addr] ~= ESTABLISH_ACK.BAD_API_VERSION then
|
||||||
|
log.info(util.c("dropping API establish packet with incorrect api version v", api_v, " (expected v", comms.api_version, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.BAD_API_VERSION)
|
||||||
elseif dev_type == DEVICE_TYPE.PKT then
|
elseif dev_type == DEVICE_TYPE.PKT then
|
||||||
-- pocket linking request
|
-- pocket linking request
|
||||||
local id = apisessions.establish_session(src_addr, firmware_v)
|
local id = apisessions.establish_session(src_addr, firmware_v)
|
||||||
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
coordinator.log_comms(util.c("API_ESTABLISH: pocket (", firmware_v, ") [@", src_addr, "] connected with session ID ", id))
|
||||||
|
|
||||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.ALLOW)
|
local conf = iocontrol.get_db().facility.conf
|
||||||
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.ALLOW, { conf.num_units, conf.cooling })
|
||||||
else
|
else
|
||||||
log.debug(util.c("API_ESTABLISH: illegal establish packet for device ", dev_type, " on pocket channel"))
|
log.debug(util.c("API_ESTABLISH: illegal establish packet for device ", dev_type, " on pocket channel"))
|
||||||
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.DENY)
|
_send_api_establish_ack(packet.scada_frame, ESTABLISH_ACK.DENY)
|
||||||
@@ -676,7 +702,7 @@ function coordinator.comms(version, nic, sv_watchdog)
|
|||||||
|
|
||||||
if conf.num_units == config.UnitCount then
|
if conf.num_units == config.UnitCount then
|
||||||
-- init io controller
|
-- init io controller
|
||||||
iocontrol.init(conf, public)
|
iocontrol.init(conf, public, config.TempScale)
|
||||||
|
|
||||||
self.sv_addr = src_addr
|
self.sv_addr = src_addr
|
||||||
self.sv_linked = true
|
self.sv_linked = true
|
||||||
|
|||||||
@@ -47,10 +47,27 @@ end
|
|||||||
-- initialize the coordinator IO controller
|
-- initialize the coordinator IO controller
|
||||||
---@param conf facility_conf configuration
|
---@param conf facility_conf configuration
|
||||||
---@param comms coord_comms comms reference
|
---@param comms coord_comms comms reference
|
||||||
function iocontrol.init(conf, comms)
|
---@param temp_scale integer temperature unit (1 = K, 2 = C, 3 = F, 4 = R)
|
||||||
|
function iocontrol.init(conf, comms, temp_scale)
|
||||||
|
-- temperature unit label and conversion function (from Kelvin)
|
||||||
|
if temp_scale == 2 then
|
||||||
|
io.temp_label = "\xb0C"
|
||||||
|
io.temp_convert = function (t) return t - 273.15 end
|
||||||
|
elseif temp_scale == 3 then
|
||||||
|
io.temp_label = "\xb0F"
|
||||||
|
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
||||||
|
elseif temp_scale == 4 then
|
||||||
|
io.temp_label = "\xb0R"
|
||||||
|
io.temp_convert = function (t) return 1.8 * t end
|
||||||
|
else
|
||||||
|
io.temp_label = "K"
|
||||||
|
io.temp_convert = function (t) return t end
|
||||||
|
end
|
||||||
|
|
||||||
-- facility data structure
|
-- facility data structure
|
||||||
---@class ioctl_facility
|
---@class ioctl_facility
|
||||||
io.facility = {
|
io.facility = {
|
||||||
|
conf = conf,
|
||||||
num_units = conf.num_units,
|
num_units = conf.num_units,
|
||||||
tank_mode = conf.cooling.fac_tank_mode,
|
tank_mode = conf.cooling.fac_tank_mode,
|
||||||
tank_defs = conf.cooling.fac_tank_defs,
|
tank_defs = conf.cooling.fac_tank_defs,
|
||||||
@@ -75,6 +92,7 @@ function iocontrol.init(conf, comms)
|
|||||||
---@type WASTE_PRODUCT
|
---@type WASTE_PRODUCT
|
||||||
auto_current_waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
auto_current_waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
auto_pu_fallback_active = false,
|
auto_pu_fallback_active = false,
|
||||||
|
auto_sps_disabled = false,
|
||||||
|
|
||||||
radiation = types.new_zero_radiation_reading(),
|
radiation = types.new_zero_radiation_reading(),
|
||||||
|
|
||||||
@@ -210,6 +228,8 @@ function iocontrol.init(conf, comms)
|
|||||||
---@class ioctl_unit
|
---@class ioctl_unit
|
||||||
local entry = {
|
local entry = {
|
||||||
unit_id = i,
|
unit_id = i,
|
||||||
|
connected = false,
|
||||||
|
rtu_hw = { boilers = {}, turbines = {} },
|
||||||
|
|
||||||
num_boilers = 0,
|
num_boilers = 0,
|
||||||
num_turbines = 0,
|
num_turbines = 0,
|
||||||
@@ -219,7 +239,10 @@ function iocontrol.init(conf, comms)
|
|||||||
control_state = false,
|
control_state = false,
|
||||||
burn_rate_cmd = 0.0,
|
burn_rate_cmd = 0.0,
|
||||||
radiation = types.new_zero_radiation_reading(),
|
radiation = types.new_zero_radiation_reading(),
|
||||||
sna_prod_rate = 0.0,
|
|
||||||
|
sna_peak_rate = 0.0,
|
||||||
|
sna_max_rate = 0.0,
|
||||||
|
sna_out_rate = 0.0,
|
||||||
|
|
||||||
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
||||||
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
@@ -260,18 +283,18 @@ function iocontrol.init(conf, comms)
|
|||||||
|
|
||||||
---@type alarms
|
---@type alarms
|
||||||
alarms = {
|
alarms = {
|
||||||
ALARM_STATE.INACTIVE, -- containment breach
|
ALARM_STATE.INACTIVE, -- containment breach
|
||||||
ALARM_STATE.INACTIVE, -- containment radiation
|
ALARM_STATE.INACTIVE, -- containment radiation
|
||||||
ALARM_STATE.INACTIVE, -- reactor lost
|
ALARM_STATE.INACTIVE, -- reactor lost
|
||||||
ALARM_STATE.INACTIVE, -- damage critical
|
ALARM_STATE.INACTIVE, -- damage critical
|
||||||
ALARM_STATE.INACTIVE, -- reactor taking damage
|
ALARM_STATE.INACTIVE, -- reactor taking damage
|
||||||
ALARM_STATE.INACTIVE, -- reactor over temperature
|
ALARM_STATE.INACTIVE, -- reactor over temperature
|
||||||
ALARM_STATE.INACTIVE, -- reactor high temperature
|
ALARM_STATE.INACTIVE, -- reactor high temperature
|
||||||
ALARM_STATE.INACTIVE, -- waste leak
|
ALARM_STATE.INACTIVE, -- waste leak
|
||||||
ALARM_STATE.INACTIVE, -- waste level high
|
ALARM_STATE.INACTIVE, -- waste level high
|
||||||
ALARM_STATE.INACTIVE, -- RPS transient
|
ALARM_STATE.INACTIVE, -- RPS transient
|
||||||
ALARM_STATE.INACTIVE, -- RCS transient
|
ALARM_STATE.INACTIVE, -- RCS transient
|
||||||
ALARM_STATE.INACTIVE -- turbine trip
|
ALARM_STATE.INACTIVE -- turbine trip
|
||||||
},
|
},
|
||||||
|
|
||||||
annunciator = {}, ---@type annunciator
|
annunciator = {}, ---@type annunciator
|
||||||
@@ -298,12 +321,14 @@ function iocontrol.init(conf, comms)
|
|||||||
for _ = 1, conf.cooling.r_cool[i].BoilerCount do
|
for _ = 1, conf.cooling.r_cool[i].BoilerCount do
|
||||||
table.insert(entry.boiler_ps_tbl, psil.create())
|
table.insert(entry.boiler_ps_tbl, psil.create())
|
||||||
table.insert(entry.boiler_data_tbl, {})
|
table.insert(entry.boiler_data_tbl, {})
|
||||||
|
table.insert(entry.rtu_hw.boilers, { connected = false, faulted = false })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create turbine tables
|
-- create turbine tables
|
||||||
for _ = 1, conf.cooling.r_cool[i].TurbineCount do
|
for _ = 1, conf.cooling.r_cool[i].TurbineCount do
|
||||||
table.insert(entry.turbine_ps_tbl, psil.create())
|
table.insert(entry.turbine_ps_tbl, psil.create())
|
||||||
table.insert(entry.turbine_data_tbl, {})
|
table.insert(entry.turbine_data_tbl, {})
|
||||||
|
table.insert(entry.rtu_hw.turbines, { connected = false, faulted = false })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create tank tables
|
-- create tank tables
|
||||||
@@ -357,6 +382,13 @@ function iocontrol.fp_monitor_state(id, connected)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- report thread (routine) statuses
|
||||||
|
---@param thread string thread name
|
||||||
|
---@param ok boolean thread state
|
||||||
|
function iocontrol.fp_rt_status(thread, ok)
|
||||||
|
io.fp.ps.publish(util.c("routine__", thread), ok)
|
||||||
|
end
|
||||||
|
|
||||||
-- report PKT firmware version and PKT session connection state
|
-- report PKT firmware version and PKT session connection state
|
||||||
---@param session_id integer PKT session
|
---@param session_id integer PKT session
|
||||||
---@param fw string firmware version
|
---@param fw string firmware version
|
||||||
@@ -384,7 +416,7 @@ function iocontrol.fp_pkt_rtt(session_id, rtt)
|
|||||||
elseif rtt > WARN_RTT then
|
elseif rtt > WARN_RTT then
|
||||||
io.fp.ps.publish("pkt_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
io.fp.ps.publish("pkt_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
||||||
else
|
else
|
||||||
io.fp.ps.publish("pkt_" .. session_id .. "_rtt_color", colors.green)
|
io.fp.ps.publish("pkt_" .. session_id .. "_rtt_color", colors.green_hc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -566,7 +598,7 @@ function iocontrol.update_facility_status(status)
|
|||||||
|
|
||||||
local ctl_status = status[1]
|
local ctl_status = status[1]
|
||||||
|
|
||||||
if type(ctl_status) == "table" and #ctl_status == 16 then
|
if type(ctl_status) == "table" and #ctl_status == 17 then
|
||||||
fac.all_sys_ok = ctl_status[1]
|
fac.all_sys_ok = ctl_status[1]
|
||||||
fac.auto_ready = ctl_status[2]
|
fac.auto_ready = ctl_status[2]
|
||||||
|
|
||||||
@@ -617,9 +649,11 @@ function iocontrol.update_facility_status(status)
|
|||||||
|
|
||||||
fac.auto_current_waste_product = ctl_status[15]
|
fac.auto_current_waste_product = ctl_status[15]
|
||||||
fac.auto_pu_fallback_active = ctl_status[16]
|
fac.auto_pu_fallback_active = ctl_status[16]
|
||||||
|
fac.auto_sps_disabled = ctl_status[17]
|
||||||
|
|
||||||
fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
|
fac.ps.publish("current_waste_product", fac.auto_current_waste_product)
|
||||||
fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
|
fac.ps.publish("pu_fallback_active", fac.auto_pu_fallback_active)
|
||||||
|
fac.ps.publish("sps_disabled_low_power", fac.auto_sps_disabled)
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "control status not a table or length mismatch")
|
log.debug(log_header .. "control status not a table or length mismatch")
|
||||||
valid = false
|
valid = false
|
||||||
@@ -636,10 +670,27 @@ function iocontrol.update_facility_status(status)
|
|||||||
fac.rtu_count = rtu_statuses.count
|
fac.rtu_count = rtu_statuses.count
|
||||||
|
|
||||||
-- power statistics
|
-- power statistics
|
||||||
if type(rtu_statuses.power) == "table" then
|
if type(rtu_statuses.power) == "table" and #rtu_statuses.power == 4 then
|
||||||
fac.induction_ps_tbl[1].publish("avg_charge", rtu_statuses.power[1])
|
local data = fac.induction_data_tbl[1] ---@type imatrix_session_db
|
||||||
fac.induction_ps_tbl[1].publish("avg_inflow", rtu_statuses.power[2])
|
local ps = fac.induction_ps_tbl[1] ---@type psil
|
||||||
fac.induction_ps_tbl[1].publish("avg_outflow", rtu_statuses.power[3])
|
|
||||||
|
local chg = tonumber(rtu_statuses.power[1])
|
||||||
|
local in_f = tonumber(rtu_statuses.power[2])
|
||||||
|
local out_f = tonumber(rtu_statuses.power[3])
|
||||||
|
local eta = tonumber(rtu_statuses.power[4])
|
||||||
|
|
||||||
|
ps.publish("avg_charge", chg)
|
||||||
|
ps.publish("avg_inflow", in_f)
|
||||||
|
ps.publish("avg_outflow", out_f)
|
||||||
|
ps.publish("eta_ms", eta)
|
||||||
|
|
||||||
|
ps.publish("is_charging", in_f > out_f)
|
||||||
|
ps.publish("is_discharging", out_f > in_f)
|
||||||
|
|
||||||
|
if data and data.build then
|
||||||
|
local cap = util.joules_to_fe(data.build.transfer_cap)
|
||||||
|
ps.publish("at_max_io", in_f >= cap or out_f >= cap)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "power statistics list not a table")
|
log.debug(log_header .. "power statistics list not a table")
|
||||||
valid = false
|
valid = false
|
||||||
@@ -850,6 +901,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if #reactor_status == 0 then
|
if #reactor_status == 0 then
|
||||||
|
unit.connected = false
|
||||||
unit.unit_ps.publish("computed_status", 1) -- disconnected
|
unit.unit_ps.publish("computed_status", 1) -- disconnected
|
||||||
elseif #reactor_status == 3 then
|
elseif #reactor_status == 3 then
|
||||||
local mek_status = reactor_status[1]
|
local mek_status = reactor_status[1]
|
||||||
@@ -909,6 +961,8 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
unit.unit_ps.publish(key, val)
|
unit.unit_ps.publish(key, val)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
unit.connected = true
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "reactor status length mismatch")
|
log.debug(log_header .. "reactor status length mismatch")
|
||||||
valid = false
|
valid = false
|
||||||
@@ -923,7 +977,10 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local boil_sum = 0
|
local boil_sum = 0
|
||||||
|
|
||||||
for id = 1, #unit.boiler_ps_tbl do
|
for id = 1, #unit.boiler_ps_tbl do
|
||||||
if rtu_statuses.boilers[i] == nil then
|
local connected = rtu_statuses.boilers[id] ~= nil
|
||||||
|
unit.rtu_hw.boilers[id].connected = connected
|
||||||
|
|
||||||
|
if not connected then
|
||||||
-- disconnected
|
-- disconnected
|
||||||
unit.boiler_ps_tbl[id].publish("computed_status", 1)
|
unit.boiler_ps_tbl[id].publish("computed_status", 1)
|
||||||
end
|
end
|
||||||
@@ -935,6 +992,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
local rtu_faulted = _record_multiblock_status(boiler, data, ps)
|
local rtu_faulted = _record_multiblock_status(boiler, data, ps)
|
||||||
|
unit.rtu_hw.boilers[id].faulted = rtu_faulted
|
||||||
|
|
||||||
if rtu_faulted then
|
if rtu_faulted then
|
||||||
ps.publish("computed_status", 3) -- faulted
|
ps.publish("computed_status", 3) -- faulted
|
||||||
@@ -966,7 +1024,10 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local flow_sum = 0
|
local flow_sum = 0
|
||||||
|
|
||||||
for id = 1, #unit.turbine_ps_tbl do
|
for id = 1, #unit.turbine_ps_tbl do
|
||||||
if rtu_statuses.turbines[i] == nil then
|
local connected = rtu_statuses.turbines[id] ~= nil
|
||||||
|
unit.rtu_hw.turbines[id].connected = connected
|
||||||
|
|
||||||
|
if not connected then
|
||||||
-- disconnected
|
-- disconnected
|
||||||
unit.turbine_ps_tbl[id].publish("computed_status", 1)
|
unit.turbine_ps_tbl[id].publish("computed_status", 1)
|
||||||
end
|
end
|
||||||
@@ -978,6 +1039,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
local rtu_faulted = _record_multiblock_status(turbine, data, ps)
|
local rtu_faulted = _record_multiblock_status(turbine, data, ps)
|
||||||
|
unit.rtu_hw.turbines[id].faulted = rtu_faulted
|
||||||
|
|
||||||
if rtu_faulted then
|
if rtu_faulted then
|
||||||
ps.publish("computed_status", 3) -- faulted
|
ps.publish("computed_status", 3) -- faulted
|
||||||
@@ -1009,7 +1071,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
-- dynamic tank statuses
|
-- dynamic tank statuses
|
||||||
if type(rtu_statuses.tanks) == "table" then
|
if type(rtu_statuses.tanks) == "table" then
|
||||||
for id = 1, #unit.tank_ps_tbl do
|
for id = 1, #unit.tank_ps_tbl do
|
||||||
if rtu_statuses.tanks[i] == nil then
|
if rtu_statuses.tanks[id] == nil then
|
||||||
-- disconnected
|
-- disconnected
|
||||||
unit.tank_ps_tbl[id].publish("computed_status", 1)
|
unit.tank_ps_tbl[id].publish("computed_status", 1)
|
||||||
end
|
end
|
||||||
@@ -1048,12 +1110,14 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
-- solar neutron activator status info
|
-- solar neutron activator status info
|
||||||
if type(rtu_statuses.sna) == "table" then
|
if type(rtu_statuses.sna) == "table" then
|
||||||
unit.num_snas = rtu_statuses.sna[1] ---@type integer
|
unit.num_snas = rtu_statuses.sna[1] ---@type integer
|
||||||
unit.sna_prod_rate = rtu_statuses.sna[2] ---@type number
|
unit.sna_peak_rate = rtu_statuses.sna[2] ---@type number
|
||||||
unit.sna_peak_rate = rtu_statuses.sna[3] ---@type number
|
unit.sna_max_rate = rtu_statuses.sna[3] ---@type number
|
||||||
|
unit.sna_out_rate = rtu_statuses.sna[4] ---@type number
|
||||||
|
|
||||||
unit.unit_ps.publish("sna_count", unit.num_snas)
|
unit.unit_ps.publish("sna_count", unit.num_snas)
|
||||||
unit.unit_ps.publish("sna_prod_rate", unit.sna_prod_rate)
|
|
||||||
unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate)
|
unit.unit_ps.publish("sna_peak_rate", unit.sna_peak_rate)
|
||||||
|
unit.unit_ps.publish("sna_max_rate", unit.sna_max_rate)
|
||||||
|
unit.unit_ps.publish("sna_out_rate", unit.sna_out_rate)
|
||||||
|
|
||||||
sna_count_sum = sna_count_sum + unit.num_snas
|
sna_count_sum = sna_count_sum + unit.num_snas
|
||||||
else
|
else
|
||||||
@@ -1201,7 +1265,7 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
|
|
||||||
local u_spent_rate = waste_rate
|
local u_spent_rate = waste_rate
|
||||||
local u_pu_rate = util.trinary(is_pu, waste_rate, 0.0)
|
local u_pu_rate = util.trinary(is_pu, waste_rate, 0.0)
|
||||||
local u_po_rate = util.trinary(not is_pu, math.min(waste_rate, unit.sna_prod_rate), 0.0)
|
local u_po_rate = unit.sna_out_rate
|
||||||
|
|
||||||
unit.unit_ps.publish("pu_rate", u_pu_rate)
|
unit.unit_ps.publish("pu_rate", u_pu_rate)
|
||||||
unit.unit_ps.publish("po_rate", u_po_rate)
|
unit.unit_ps.publish("po_rate", u_po_rate)
|
||||||
@@ -1209,14 +1273,15 @@ function iocontrol.update_unit_statuses(statuses)
|
|||||||
unit.unit_ps.publish("sna_in", util.trinary(is_pu, 0, burn_rate))
|
unit.unit_ps.publish("sna_in", util.trinary(is_pu, 0, burn_rate))
|
||||||
|
|
||||||
if unit.waste_product == types.WASTE_PRODUCT.POLONIUM then
|
if unit.waste_product == types.WASTE_PRODUCT.POLONIUM then
|
||||||
|
u_spent_rate = u_po_rate
|
||||||
unit.unit_ps.publish("po_pl_rate", u_po_rate)
|
unit.unit_ps.publish("po_pl_rate", u_po_rate)
|
||||||
unit.unit_ps.publish("po_am_rate", 0)
|
unit.unit_ps.publish("po_am_rate", 0)
|
||||||
po_pl_rate = po_pl_rate + u_po_rate
|
po_pl_rate = po_pl_rate + u_po_rate
|
||||||
elseif unit.waste_product == types.WASTE_PRODUCT.ANTI_MATTER then
|
elseif unit.waste_product == types.WASTE_PRODUCT.ANTI_MATTER then
|
||||||
|
u_spent_rate = 0
|
||||||
unit.unit_ps.publish("po_pl_rate", 0)
|
unit.unit_ps.publish("po_pl_rate", 0)
|
||||||
unit.unit_ps.publish("po_am_rate", u_po_rate)
|
unit.unit_ps.publish("po_am_rate", u_po_rate)
|
||||||
po_am_rate = po_am_rate + u_po_rate
|
po_am_rate = po_am_rate + u_po_rate
|
||||||
u_spent_rate = 0
|
|
||||||
else
|
else
|
||||||
unit.unit_ps.publish("po_pl_rate", 0)
|
unit.unit_ps.publish("po_pl_rate", 0)
|
||||||
unit.unit_ps.publish("po_am_rate", 0)
|
unit.unit_ps.publish("po_am_rate", 0)
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ local self = {
|
|||||||
gen_target = 0.0,
|
gen_target = 0.0,
|
||||||
limits = {},
|
limits = {},
|
||||||
waste_product = PRODUCT.PLUTONIUM,
|
waste_product = PRODUCT.PLUTONIUM,
|
||||||
pu_fallback = false
|
pu_fallback = false,
|
||||||
|
sps_low_power = false
|
||||||
},
|
},
|
||||||
waste_modes = {},
|
waste_modes = {},
|
||||||
priority_groups = {}
|
priority_groups = {}
|
||||||
@@ -65,6 +66,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
ctl_proc.limits = config.limits
|
ctl_proc.limits = config.limits
|
||||||
ctl_proc.waste_product = config.waste_product
|
ctl_proc.waste_product = config.waste_product
|
||||||
ctl_proc.pu_fallback = config.pu_fallback
|
ctl_proc.pu_fallback = config.pu_fallback
|
||||||
|
ctl_proc.sps_low_power = config.sps_low_power
|
||||||
|
|
||||||
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
self.io.facility.ps.publish("process_mode", ctl_proc.mode)
|
||||||
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
self.io.facility.ps.publish("process_burn_target", ctl_proc.burn_target)
|
||||||
@@ -72,6 +74,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
self.io.facility.ps.publish("process_gen_target", ctl_proc.gen_target)
|
||||||
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
self.io.facility.ps.publish("process_waste_product", ctl_proc.waste_product)
|
||||||
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
self.io.facility.ps.publish("process_pu_fallback", ctl_proc.pu_fallback)
|
||||||
|
self.io.facility.ps.publish("process_sps_low_power", ctl_proc.sps_low_power)
|
||||||
|
|
||||||
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do
|
for id = 1, math.min(#ctl_proc.limits, self.io.facility.num_units) do
|
||||||
local unit = self.io.units[id] ---@type ioctl_unit
|
local unit = self.io.units[id] ---@type ioctl_unit
|
||||||
@@ -83,6 +86,7 @@ function process.init(iocontrol, coord_comms)
|
|||||||
-- notify supervisor of auto waste config
|
-- notify supervisor of auto waste config
|
||||||
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product)
|
self.comms.send_fac_command(FAC_COMMAND.SET_WASTE_MODE, ctl_proc.waste_product)
|
||||||
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback)
|
self.comms.send_fac_command(FAC_COMMAND.SET_PU_FB, ctl_proc.pu_fallback)
|
||||||
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, ctl_proc.sps_low_power)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- unit waste states
|
-- unit waste states
|
||||||
@@ -259,6 +263,18 @@ function process.set_pu_fallback(enabled)
|
|||||||
_write_auto_config()
|
_write_auto_config()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set automatic process control SPS usage at low power
|
||||||
|
---@param enabled boolean whether to enable SPS usage at low power
|
||||||
|
function process.set_sps_low_power(enabled)
|
||||||
|
self.comms.send_fac_command(FAC_COMMAND.SET_SPS_LP, enabled)
|
||||||
|
|
||||||
|
log.debug(util.c("PROCESS: SET SPS LOW POWER ", enabled))
|
||||||
|
|
||||||
|
-- update config table and save
|
||||||
|
self.control_states.process.sps_low_power = enabled
|
||||||
|
_write_auto_config()
|
||||||
|
end
|
||||||
|
|
||||||
-- save process control settings
|
-- save process control settings
|
||||||
---@param mode PROCESS control mode
|
---@param mode PROCESS control mode
|
||||||
---@param burn_target number burn rate target
|
---@param burn_target number burn rate target
|
||||||
|
|||||||
@@ -2,28 +2,33 @@
|
|||||||
-- Graphics Rendering Control
|
-- Graphics Rendering Control
|
||||||
--
|
--
|
||||||
|
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local coordinator = require("coordinator.coordinator")
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
local pgi = require("coordinator.ui.pgi")
|
local pgi = require("coordinator.ui.pgi")
|
||||||
|
|
||||||
local flow_view = require("coordinator.ui.layout.flow_view")
|
local flow_view = require("coordinator.ui.layout.flow_view")
|
||||||
local panel_view = require("coordinator.ui.layout.front_panel")
|
local panel_view = require("coordinator.ui.layout.front_panel")
|
||||||
local main_view = require("coordinator.ui.layout.main_view")
|
local main_view = require("coordinator.ui.layout.main_view")
|
||||||
local unit_view = require("coordinator.ui.layout.unit_view")
|
local unit_view = require("coordinator.ui.layout.unit_view")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
local flasher = require("graphics.flasher")
|
local flasher = require("graphics.flasher")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
|
|
||||||
|
local log_render = coordinator.log_render
|
||||||
|
|
||||||
---@class coord_renderer
|
---@class coord_renderer
|
||||||
local renderer = {}
|
local renderer = {}
|
||||||
|
|
||||||
-- render engine
|
-- render engine
|
||||||
local engine = {
|
local engine = {
|
||||||
|
color_mode = 1, ---@type COLOR_MODE
|
||||||
monitors = nil, ---@type monitors_struct|nil
|
monitors = nil, ---@type monitors_struct|nil
|
||||||
dmesg_window = nil, ---@type table|nil
|
dmesg_window = nil, ---@type table|nil
|
||||||
ui_ready = false,
|
ui_ready = false,
|
||||||
@@ -47,15 +52,34 @@ local function _init_display(monitor)
|
|||||||
monitor.setCursorPos(1, 1)
|
monitor.setCursorPos(1, 1)
|
||||||
|
|
||||||
-- set overridden colors
|
-- set overridden colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
monitor.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
monitor.setPaletteColor(style.theme.colors[i].c, style.theme.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply color mode
|
||||||
|
local c_mode_overrides = style.theme.color_modes[engine.color_mode]
|
||||||
|
for i = 1, #c_mode_overrides do
|
||||||
|
monitor.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- disable the flow view
|
-- print out that the monitor is too small
|
||||||
---@param disable boolean
|
---@param monitor table monitor
|
||||||
function renderer.legacy_disable_flow_view(disable)
|
local function _print_too_small(monitor)
|
||||||
engine.disable_flow_view = disable
|
monitor.setCursorPos(1, 1)
|
||||||
|
monitor.setBackgroundColor(colors.black)
|
||||||
|
monitor.setTextColor(colors.red)
|
||||||
|
monitor.clear()
|
||||||
|
monitor.write("monitor too small")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply renderer configurations
|
||||||
|
---@param config crd_config
|
||||||
|
function renderer.configure(config)
|
||||||
|
style.set_themes(config.MainTheme, config.FrontPanelTheme, config.ColorMode)
|
||||||
|
|
||||||
|
engine.color_mode = config.ColorMode
|
||||||
|
engine.disable_flow_view = config.DisableFlowView
|
||||||
end
|
end
|
||||||
|
|
||||||
-- link to the monitor peripherals
|
-- link to the monitor peripherals
|
||||||
@@ -64,15 +88,15 @@ function renderer.set_displays(monitors)
|
|||||||
engine.monitors = monitors
|
engine.monitors = monitors
|
||||||
|
|
||||||
-- report to front panel as connected
|
-- report to front panel as connected
|
||||||
iocontrol.fp_monitor_state("main", engine.monitors.primary ~= nil)
|
iocontrol.fp_monitor_state("main", engine.monitors.main ~= nil)
|
||||||
iocontrol.fp_monitor_state("flow", engine.monitors.flow ~= nil)
|
iocontrol.fp_monitor_state("flow", engine.monitors.flow ~= nil)
|
||||||
for i = 1, #engine.monitors.unit_displays do iocontrol.fp_monitor_state(i, true) end
|
for i = 1, #engine.monitors.unit_displays do iocontrol.fp_monitor_state(i, true) end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init all displays in use by the renderer
|
-- init all displays in use by the renderer
|
||||||
function renderer.init_displays()
|
function renderer.init_displays()
|
||||||
-- init primary and flow monitors
|
-- init main and flow monitors
|
||||||
_init_display(engine.monitors.primary)
|
_init_display(engine.monitors.main)
|
||||||
if not engine.disable_flow_view then _init_display(engine.monitors.flow) end
|
if not engine.disable_flow_view then _init_display(engine.monitors.flow) end
|
||||||
|
|
||||||
-- init unit displays
|
-- init unit displays
|
||||||
@@ -87,15 +111,21 @@ function renderer.init_displays()
|
|||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
|
|
||||||
-- set overridden colors
|
-- set overridden colors
|
||||||
for i = 1, #style.fp.colors do
|
for i = 1, #style.fp_theme.colors do
|
||||||
term.setPaletteColor(style.fp.colors[i].c, style.fp.colors[i].hex)
|
term.setPaletteColor(style.fp_theme.colors[i].c, style.fp_theme.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply color mode
|
||||||
|
local c_mode_overrides = style.fp_theme.color_modes[engine.color_mode]
|
||||||
|
for i = 1, #c_mode_overrides do
|
||||||
|
term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- initialize the dmesg output window
|
-- initialize the dmesg output window
|
||||||
function renderer.init_dmesg()
|
function renderer.init_dmesg()
|
||||||
local disp_x, disp_y = engine.monitors.primary.getSize()
|
local disp_w, disp_h = engine.monitors.main.getSize()
|
||||||
engine.dmesg_window = window.create(engine.monitors.primary, 1, 1, disp_x, disp_y)
|
engine.dmesg_window = window.create(engine.monitors.main, 1, 1, disp_w, disp_h)
|
||||||
log.direct_dmesg(engine.dmesg_window)
|
log.direct_dmesg(engine.dmesg_window)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -142,9 +172,9 @@ function renderer.close_fp()
|
|||||||
engine.fp_ready = false
|
engine.fp_ready = false
|
||||||
|
|
||||||
-- restore colors
|
-- restore colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.fp_theme.colors do
|
||||||
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
local r, g, b = term.nativePaletteColor(style.fp_theme.colors[i].c)
|
||||||
term.setPaletteColor(style.colors[i].c, r, g, b)
|
term.setPaletteColor(style.fp_theme.colors[i].c, r, g, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
@@ -166,21 +196,24 @@ function renderer.try_start_ui()
|
|||||||
|
|
||||||
status, msg = pcall(function ()
|
status, msg = pcall(function ()
|
||||||
-- show main view on main monitor
|
-- show main view on main monitor
|
||||||
if engine.monitors.primary ~= nil then
|
if engine.monitors.main ~= nil then
|
||||||
engine.ui.main_display = DisplayBox{window=engine.monitors.primary,fg_bg=style.root}
|
engine.ui.main_display = DisplayBox{window=engine.monitors.main,fg_bg=style.root}
|
||||||
main_view(engine.ui.main_display)
|
main_view(engine.ui.main_display)
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- show flow view on flow monitor
|
-- show flow view on flow monitor
|
||||||
if engine.monitors.flow ~= nil then
|
if engine.monitors.flow ~= nil then
|
||||||
engine.ui.flow_display = DisplayBox{window=engine.monitors.flow,fg_bg=style.root}
|
engine.ui.flow_display = DisplayBox{window=engine.monitors.flow,fg_bg=style.root}
|
||||||
flow_view(engine.ui.flow_display)
|
flow_view(engine.ui.flow_display)
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- show unit views on unit displays
|
-- show unit views on unit displays
|
||||||
for idx, display in pairs(engine.monitors.unit_displays) do
|
for idx, display in pairs(engine.monitors.unit_displays) do
|
||||||
engine.ui.unit_displays[idx] = DisplayBox{window=display,fg_bg=style.root}
|
engine.ui.unit_displays[idx] = DisplayBox{window=display,fg_bg=style.root}
|
||||||
unit_view(engine.ui.unit_displays[idx], idx)
|
unit_view(engine.ui.unit_displays[idx], idx)
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -221,6 +254,11 @@ function renderer.close_ui()
|
|||||||
-- clear unit monitors
|
-- clear unit monitors
|
||||||
for _, monitor in ipairs(engine.monitors.unit_displays) do monitor.clear() end
|
for _, monitor in ipairs(engine.monitors.unit_displays) do monitor.clear() end
|
||||||
|
|
||||||
|
if not engine.disable_flow_view then
|
||||||
|
-- clear flow monitor
|
||||||
|
engine.monitors.flow.clear()
|
||||||
|
end
|
||||||
|
|
||||||
-- re-draw dmesg
|
-- re-draw dmesg
|
||||||
engine.dmesg_window.setVisible(true)
|
engine.dmesg_window.setVisible(true)
|
||||||
engine.dmesg_window.redraw()
|
engine.dmesg_window.redraw()
|
||||||
@@ -242,43 +280,43 @@ function renderer.ui_ready() return engine.ui_ready end
|
|||||||
function renderer.handle_disconnect(device)
|
function renderer.handle_disconnect(device)
|
||||||
local is_used = false
|
local is_used = false
|
||||||
|
|
||||||
if engine.monitors ~= nil then
|
if not engine.monitors then return false end
|
||||||
if engine.monitors.primary == device then
|
|
||||||
if engine.ui.main_display ~= nil then
|
|
||||||
-- delete element tree and clear root UI elements
|
|
||||||
engine.ui.main_display.delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
is_used = true
|
if engine.monitors.main == device then
|
||||||
engine.monitors.primary = nil
|
if engine.ui.main_display ~= nil then
|
||||||
engine.ui.main_display = nil
|
-- delete element tree and clear root UI elements
|
||||||
|
engine.ui.main_display.delete()
|
||||||
|
end
|
||||||
|
|
||||||
iocontrol.fp_monitor_state("main", false)
|
is_used = true
|
||||||
elseif engine.monitors.flow == device then
|
engine.monitors.main = nil
|
||||||
if engine.ui.flow_display ~= nil then
|
engine.ui.main_display = nil
|
||||||
-- delete element tree and clear root UI elements
|
|
||||||
engine.ui.flow_display.delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
is_used = true
|
iocontrol.fp_monitor_state("main", false)
|
||||||
engine.monitors.flow = nil
|
elseif engine.monitors.flow == device then
|
||||||
engine.ui.flow_display = nil
|
if engine.ui.flow_display ~= nil then
|
||||||
|
-- delete element tree and clear root UI elements
|
||||||
|
engine.ui.flow_display.delete()
|
||||||
|
end
|
||||||
|
|
||||||
iocontrol.fp_monitor_state("flow", false)
|
is_used = true
|
||||||
else
|
engine.monitors.flow = nil
|
||||||
for idx, monitor in pairs(engine.monitors.unit_displays) do
|
engine.ui.flow_display = nil
|
||||||
if monitor == device then
|
|
||||||
if engine.ui.unit_displays[idx] ~= nil then
|
|
||||||
engine.ui.unit_displays[idx].delete()
|
|
||||||
end
|
|
||||||
|
|
||||||
is_used = true
|
iocontrol.fp_monitor_state("flow", false)
|
||||||
engine.monitors.unit_displays[idx] = nil
|
else
|
||||||
engine.ui.unit_displays[idx] = nil
|
for idx, monitor in pairs(engine.monitors.unit_displays) do
|
||||||
|
if monitor == device then
|
||||||
iocontrol.fp_monitor_state(idx, false)
|
if engine.ui.unit_displays[idx] ~= nil then
|
||||||
break
|
engine.ui.unit_displays[idx].delete()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
engine.monitors.unit_displays[idx] = nil
|
||||||
|
engine.ui.unit_displays[idx] = nil
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(idx, false)
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -293,52 +331,29 @@ end
|
|||||||
function renderer.handle_reconnect(name, device)
|
function renderer.handle_reconnect(name, device)
|
||||||
local is_used = false
|
local is_used = false
|
||||||
|
|
||||||
if engine.monitors ~= nil then
|
if not engine.monitors then return false end
|
||||||
if engine.monitors.primary_name == name then
|
|
||||||
is_used = true
|
|
||||||
_init_display(device)
|
|
||||||
engine.monitors.primary = device
|
|
||||||
|
|
||||||
local disp_x, disp_y = engine.monitors.primary.getSize()
|
-- note: handle_resize is a more adaptive way of re-initializing a connected monitor
|
||||||
engine.dmesg_window.reposition(1, 1, disp_x, disp_y, engine.monitors.primary)
|
-- since it can handle a monitor being reconnected that isn't the right size
|
||||||
|
|
||||||
if engine.ui_ready and (engine.ui.main_display == nil) then
|
if engine.monitors.main_name == name then
|
||||||
engine.dmesg_window.setVisible(false)
|
is_used = true
|
||||||
|
engine.monitors.main = device
|
||||||
|
|
||||||
engine.ui.main_display = DisplayBox{window=device,fg_bg=style.root}
|
renderer.handle_resize(name)
|
||||||
main_view(engine.ui.main_display)
|
elseif engine.monitors.flow_name == name then
|
||||||
else
|
is_used = true
|
||||||
engine.dmesg_window.setVisible(true)
|
engine.monitors.flow = device
|
||||||
engine.dmesg_window.redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
iocontrol.fp_monitor_state("main", true)
|
renderer.handle_resize(name)
|
||||||
elseif engine.monitors.flow_name == name then
|
else
|
||||||
is_used = true
|
for idx, monitor in ipairs(engine.monitors.unit_name_map) do
|
||||||
_init_display(device)
|
if monitor == name then
|
||||||
engine.monitors.flow = device
|
is_used = true
|
||||||
|
engine.monitors.unit_displays[idx] = device
|
||||||
|
|
||||||
if engine.ui_ready and (engine.ui.flow_display == nil) then
|
renderer.handle_resize(name)
|
||||||
engine.ui.flow_display = DisplayBox{window=device,fg_bg=style.root}
|
break
|
||||||
flow_view(engine.ui.flow_display)
|
|
||||||
end
|
|
||||||
|
|
||||||
iocontrol.fp_monitor_state("flow", true)
|
|
||||||
else
|
|
||||||
for idx, monitor in ipairs(engine.monitors.unit_name_map) do
|
|
||||||
if monitor == name then
|
|
||||||
is_used = true
|
|
||||||
_init_display(device)
|
|
||||||
engine.monitors.unit_displays[idx] = device
|
|
||||||
|
|
||||||
if engine.ui_ready and (engine.ui.unit_displays[idx] == nil) then
|
|
||||||
engine.ui.unit_displays[idx] = DisplayBox{window=device,fg_bg=style.root}
|
|
||||||
unit_view(engine.ui.unit_displays[idx], idx)
|
|
||||||
end
|
|
||||||
|
|
||||||
iocontrol.fp_monitor_state(idx, true)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -346,6 +361,142 @@ function renderer.handle_reconnect(name, device)
|
|||||||
return is_used
|
return is_used
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- handle a monitor being resized<br>
|
||||||
|
-- returns if this monitor is assigned + if the assigned screen still fits
|
||||||
|
---@param name string monitor name
|
||||||
|
---@return boolean is_used, boolean is_ok
|
||||||
|
function renderer.handle_resize(name)
|
||||||
|
local is_used = false
|
||||||
|
local is_ok = true
|
||||||
|
local ui = engine.ui
|
||||||
|
|
||||||
|
if not engine.monitors then return false, false end
|
||||||
|
|
||||||
|
if engine.monitors.main_name == name and engine.monitors.main then
|
||||||
|
local device = engine.monitors.main ---@type table
|
||||||
|
|
||||||
|
-- this is necessary if the bottom left block was broken and on reconnect
|
||||||
|
_init_display(device)
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
|
||||||
|
-- resize dmesg window if needed, but don't make it thinner
|
||||||
|
local disp_w, disp_h = engine.monitors.main.getSize()
|
||||||
|
local dmsg_w, _ = engine.dmesg_window.getSize()
|
||||||
|
engine.dmesg_window.reposition(1, 1, math.max(disp_w, dmsg_w), disp_h, engine.monitors.main)
|
||||||
|
|
||||||
|
if ui.main_display then
|
||||||
|
ui.main_display.delete()
|
||||||
|
ui.main_display = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state("main", true)
|
||||||
|
|
||||||
|
engine.dmesg_window.setVisible(not engine.ui_ready)
|
||||||
|
|
||||||
|
if engine.ui_ready then
|
||||||
|
local draw_start = util.time_ms()
|
||||||
|
local ok = pcall(function ()
|
||||||
|
ui.main_display = DisplayBox{window=device,fg_bg=style.root}
|
||||||
|
main_view(ui.main_display)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
log_render("main view re-draw completed in " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
|
else
|
||||||
|
if ui.main_display then
|
||||||
|
ui.main_display.delete()
|
||||||
|
ui.main_display = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
_print_too_small(device)
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state("main", false)
|
||||||
|
is_ok = false
|
||||||
|
end
|
||||||
|
else engine.dmesg_window.redraw() end
|
||||||
|
elseif engine.monitors.flow_name == name and engine.monitors.flow then
|
||||||
|
local device = engine.monitors.flow ---@type table
|
||||||
|
|
||||||
|
-- this is necessary if the bottom left block was broken and on reconnect
|
||||||
|
_init_display(device)
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
|
||||||
|
if ui.flow_display then
|
||||||
|
ui.flow_display.delete()
|
||||||
|
ui.flow_display = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state("flow", true)
|
||||||
|
|
||||||
|
if engine.ui_ready then
|
||||||
|
local draw_start = util.time_ms()
|
||||||
|
local ok = pcall(function ()
|
||||||
|
ui.flow_display = DisplayBox{window=device,fg_bg=style.root}
|
||||||
|
flow_view(ui.flow_display)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
log_render("flow view re-draw completed in " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
|
else
|
||||||
|
if ui.flow_display then
|
||||||
|
ui.flow_display.delete()
|
||||||
|
ui.flow_display = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
_print_too_small(device)
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state("flow", false)
|
||||||
|
is_ok = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for idx, monitor in ipairs(engine.monitors.unit_name_map) do
|
||||||
|
local device = engine.monitors.unit_displays[idx]
|
||||||
|
|
||||||
|
if monitor == name and device then
|
||||||
|
-- this is necessary if the bottom left block was broken and on reconnect
|
||||||
|
_init_display(device)
|
||||||
|
|
||||||
|
is_used = true
|
||||||
|
|
||||||
|
if ui.unit_displays[idx] then
|
||||||
|
ui.unit_displays[idx].delete()
|
||||||
|
ui.unit_displays[idx] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(idx, true)
|
||||||
|
|
||||||
|
if engine.ui_ready then
|
||||||
|
local draw_start = util.time_ms()
|
||||||
|
local ok = pcall(function ()
|
||||||
|
ui.unit_displays[idx] = DisplayBox{window=device,fg_bg=style.root}
|
||||||
|
unit_view(ui.unit_displays[idx], idx)
|
||||||
|
end)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
log_render("unit " .. idx .. " view re-draw completed in " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
|
else
|
||||||
|
if ui.unit_displays[idx] then
|
||||||
|
ui.unit_displays[idx].delete()
|
||||||
|
ui.unit_displays[idx] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
_print_too_small(device)
|
||||||
|
|
||||||
|
iocontrol.fp_monitor_state(idx, false)
|
||||||
|
is_ok = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return is_used, is_ok
|
||||||
|
end
|
||||||
|
|
||||||
-- handle a touch event
|
-- handle a touch event
|
||||||
---@param event mouse_interaction|nil
|
---@param event mouse_interaction|nil
|
||||||
@@ -354,16 +505,15 @@ function renderer.handle_mouse(event)
|
|||||||
if engine.fp_ready and event.monitor == "terminal" then
|
if engine.fp_ready and event.monitor == "terminal" then
|
||||||
engine.ui.front_panel.handle_mouse(event)
|
engine.ui.front_panel.handle_mouse(event)
|
||||||
elseif engine.ui_ready then
|
elseif engine.ui_ready then
|
||||||
if event.monitor == engine.monitors.primary_name then
|
if event.monitor == engine.monitors.main_name then
|
||||||
engine.ui.main_display.handle_mouse(event)
|
if engine.ui.main_display then engine.ui.main_display.handle_mouse(event) end
|
||||||
elseif event.monitor == engine.monitors.flow_name then
|
elseif event.monitor == engine.monitors.flow_name then
|
||||||
engine.ui.flow_display.handle_mouse(event)
|
if engine.ui.flow_display then engine.ui.flow_display.handle_mouse(event) end
|
||||||
else
|
else
|
||||||
for id, monitor in ipairs(engine.monitors.unit_name_map) do
|
for id, monitor in ipairs(engine.monitors.unit_name_map) do
|
||||||
if event.monitor == monitor then
|
local display = engine.ui.unit_displays[id]
|
||||||
local layout = engine.ui.unit_displays[id] ---@type graphics_element
|
if event.monitor == monitor and display then
|
||||||
layout.handle_mouse(event)
|
if display then display.handle_mouse(event) end
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ local iocontrol = require("coordinator.iocontrol")
|
|||||||
local pocket = {}
|
local pocket = {}
|
||||||
|
|
||||||
local PROTOCOL = comms.PROTOCOL
|
local PROTOCOL = comms.PROTOCOL
|
||||||
-- local CRDN_TYPE = comms.CRDN_TYPE
|
local CRDN_TYPE = comms.CRDN_TYPE
|
||||||
local MGMT_TYPE = comms.MGMT_TYPE
|
local MGMT_TYPE = comms.MGMT_TYPE
|
||||||
|
|
||||||
-- retry time constants in ms
|
-- retry time constants in ms
|
||||||
@@ -73,18 +73,18 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- send a CRDN packet
|
-- send a CRDN packet
|
||||||
-----@param msg_type CRDN_TYPE
|
---@param msg_type CRDN_TYPE
|
||||||
-----@param msg table
|
---@param msg table
|
||||||
-- local function _send(msg_type, msg)
|
local function _send(msg_type, msg)
|
||||||
-- local s_pkt = comms.scada_packet()
|
local s_pkt = comms.scada_packet()
|
||||||
-- local c_pkt = comms.crdn_packet()
|
local c_pkt = comms.crdn_packet()
|
||||||
|
|
||||||
-- c_pkt.make(msg_type, msg)
|
c_pkt.make(msg_type, msg)
|
||||||
-- s_pkt.make(self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable())
|
s_pkt.make(s_addr, self.seq_num, PROTOCOL.SCADA_CRDN, c_pkt.raw_sendable())
|
||||||
|
|
||||||
-- out_queue.push_packet(s_pkt)
|
out_queue.push_packet(s_pkt)
|
||||||
-- self.seq_num = self.seq_num + 1
|
self.seq_num = self.seq_num + 1
|
||||||
-- end
|
end
|
||||||
|
|
||||||
-- send a SCADA management packet
|
-- send a SCADA management packet
|
||||||
---@param msg_type MGMT_TYPE
|
---@param msg_type MGMT_TYPE
|
||||||
@@ -120,8 +120,44 @@ function pocket.new_session(id, s_addr, in_queue, out_queue, timeout)
|
|||||||
if pkt.scada_frame.protocol() == PROTOCOL.SCADA_CRDN then
|
if pkt.scada_frame.protocol() == PROTOCOL.SCADA_CRDN then
|
||||||
---@cast pkt crdn_frame
|
---@cast pkt crdn_frame
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
-- handle packet by type
|
-- handle packet by type
|
||||||
if pkt.type == nil then
|
if pkt.type == CRDN_TYPE.API_GET_FAC then
|
||||||
|
local fac = db.facility
|
||||||
|
|
||||||
|
local data = {
|
||||||
|
fac.all_sys_ok,
|
||||||
|
fac.rtu_count,
|
||||||
|
fac.radiation,
|
||||||
|
{ fac.auto_ready, fac.auto_active, fac.auto_ramping, fac.auto_saturated },
|
||||||
|
{ fac.auto_current_waste_product, fac.auto_pu_fallback_active },
|
||||||
|
util.table_len(fac.tank_data_tbl),
|
||||||
|
fac.induction_data_tbl[1] ~= nil,
|
||||||
|
fac.sps_data_tbl[1] ~= nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(CRDN_TYPE.API_GET_FAC, data)
|
||||||
|
elseif pkt.type == CRDN_TYPE.API_GET_UNIT then
|
||||||
|
if pkt.length == 1 and type(pkt.data[1]) == "number" then
|
||||||
|
local u = db.units[pkt.data[1]] ---@type ioctl_unit
|
||||||
|
|
||||||
|
if u then
|
||||||
|
local data = {
|
||||||
|
u.unit_id,
|
||||||
|
u.connected,
|
||||||
|
u.rtu_hw,
|
||||||
|
u.alarms,
|
||||||
|
u.annunciator,
|
||||||
|
u.reactor_data,
|
||||||
|
u.boiler_data_tbl,
|
||||||
|
u.turbine_data_tbl,
|
||||||
|
u.tank_data_tbl
|
||||||
|
}
|
||||||
|
|
||||||
|
_send(CRDN_TYPE.API_GET_UNIT, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "handler received unsupported CRDN packet type " .. pkt.type)
|
log.debug(log_header .. "handler received unsupported CRDN packet type " .. pkt.type)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,30 +7,29 @@ require("/initenv").init_env()
|
|||||||
local comms = require("scada-common.comms")
|
local comms = require("scada-common.comms")
|
||||||
local crash = require("scada-common.crash")
|
local crash = require("scada-common.crash")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
|
local mqueue = require("scada-common.mqueue")
|
||||||
local network = require("scada-common.network")
|
local network = require("scada-common.network")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local tcd = require("scada-common.tcd")
|
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
|
||||||
|
|
||||||
local configure = require("coordinator.configure")
|
local configure = require("coordinator.configure")
|
||||||
local coordinator = require("coordinator.coordinator")
|
local coordinator = require("coordinator.coordinator")
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
local renderer = require("coordinator.renderer")
|
local renderer = require("coordinator.renderer")
|
||||||
local sounder = require("coordinator.sounder")
|
local sounder = require("coordinator.sounder")
|
||||||
|
local threads = require("coordinator.threads")
|
||||||
|
|
||||||
local apisessions = require("coordinator.session.apisessions")
|
local COORDINATOR_VERSION = "v1.4.6"
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v1.2.2"
|
local CHUNK_LOAD_DELAY_S = 30.0
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
|
||||||
local log_graphics = coordinator.log_graphics
|
local log_render = coordinator.log_render
|
||||||
local log_sys = coordinator.log_sys
|
local log_sys = coordinator.log_sys
|
||||||
local log_boot = coordinator.log_boot
|
local log_boot = coordinator.log_boot
|
||||||
local log_comms = coordinator.log_comms
|
local log_comms = coordinator.log_comms
|
||||||
local log_crypto = coordinator.log_crypto
|
local log_crypto = coordinator.log_crypto
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
@@ -40,15 +39,47 @@ local log_crypto = coordinator.log_crypto
|
|||||||
-- mount connected devices (required for monitor setup)
|
-- mount connected devices (required for monitor setup)
|
||||||
ppm.mount_all()
|
ppm.mount_all()
|
||||||
|
|
||||||
|
local wait_on_load = true
|
||||||
local loaded, monitors = coordinator.load_config()
|
local loaded, monitors = coordinator.load_config()
|
||||||
|
|
||||||
|
-- if the computer just started, its chunk may have just loaded (...or the user rebooted)
|
||||||
|
-- if monitor config failed, maybe an adjacent chunk containing all or part of a monitor has not loaded yet, so keep trying
|
||||||
|
while wait_on_load and loaded == 2 and os.clock() < CHUNK_LOAD_DELAY_S do
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
println("There was a monitor configuration problem at boot.\n")
|
||||||
|
println("Startup will keep trying every 2s in case of chunk load delays.\n")
|
||||||
|
println(util.sprintf("The configurator will be started in %ds if all attempts fail.\n", math.max(0, CHUNK_LOAD_DELAY_S - os.clock())))
|
||||||
|
println("(click to skip to the configurator)")
|
||||||
|
|
||||||
|
local timer_id = util.start_timer(2)
|
||||||
|
|
||||||
|
while true do
|
||||||
|
local event, param1 = util.pull_event()
|
||||||
|
if event == "timer" and param1 == timer_id then
|
||||||
|
-- remount and re-attempt
|
||||||
|
ppm.mount_all()
|
||||||
|
loaded, monitors = coordinator.load_config()
|
||||||
|
break
|
||||||
|
elseif event == "mouse_click" or event == "terminate" then
|
||||||
|
wait_on_load = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if loaded ~= 0 then
|
if loaded ~= 0 then
|
||||||
-- try to reconfigure (user action)
|
-- try to reconfigure (user action)
|
||||||
local success, error = configure.configure(loaded, monitors)
|
local success, error = configure.configure(loaded, monitors)
|
||||||
if success then
|
if success then
|
||||||
loaded, monitors = coordinator.load_config()
|
loaded, monitors = coordinator.load_config()
|
||||||
assert(loaded == 0, util.trinary(loaded == 1, "failed to load valid configuration", "monitor configuration invalid"))
|
if loaded ~= 0 then
|
||||||
|
println(util.trinary(loaded == 2, "monitor configuration invalid", "failed to load a valid configuration") .. ", please reconfigure")
|
||||||
|
return
|
||||||
|
end
|
||||||
else
|
else
|
||||||
assert(success, "coordinator configuration error: " .. error)
|
println("configuration error: " .. error)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -69,6 +100,7 @@ log.info("========================================")
|
|||||||
println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<")
|
println(">> SCADA Coordinator " .. COORDINATOR_VERSION .. " <<")
|
||||||
|
|
||||||
crash.set_env("coordinator", COORDINATOR_VERSION)
|
crash.set_env("coordinator", COORDINATOR_VERSION)
|
||||||
|
crash.dbg_log_env()
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main application
|
-- main application
|
||||||
@@ -79,14 +111,14 @@ local function main()
|
|||||||
-- system startup
|
-- system startup
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
-- re-mount devices now that logging is ready
|
-- log mounts now since mounting was done before logging was ready
|
||||||
ppm.mount_all()
|
ppm.log_mounts()
|
||||||
|
|
||||||
-- report versions/init fp PSIL
|
-- report versions/init fp PSIL
|
||||||
iocontrol.init_fp(COORDINATOR_VERSION, comms.version)
|
iocontrol.init_fp(COORDINATOR_VERSION, comms.version)
|
||||||
|
|
||||||
-- init renderer
|
-- init renderer
|
||||||
renderer.legacy_disable_flow_view(config.DisableFlowView)
|
renderer.configure(config)
|
||||||
renderer.set_displays(monitors)
|
renderer.set_displays(monitors)
|
||||||
renderer.init_displays()
|
renderer.init_displays()
|
||||||
renderer.init_dmesg()
|
renderer.init_dmesg()
|
||||||
@@ -94,16 +126,58 @@ local function main()
|
|||||||
-- lets get started!
|
-- lets get started!
|
||||||
log.info("monitors ready, dmesg output incoming...")
|
log.info("monitors ready, dmesg output incoming...")
|
||||||
|
|
||||||
log_graphics("displays connected and reset")
|
log_render("displays connected and reset")
|
||||||
log_sys("system start on " .. os.date("%c"))
|
log_sys("system start on " .. os.date("%c"))
|
||||||
log_boot("starting " .. COORDINATOR_VERSION)
|
log_boot("starting " .. COORDINATOR_VERSION)
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
-- memory allocation
|
||||||
|
----------------------------------------
|
||||||
|
|
||||||
|
-- shared memory across threads
|
||||||
|
---@class crd_shared_memory
|
||||||
|
local __shared_memory = {
|
||||||
|
-- time and date format for display
|
||||||
|
date_format = util.trinary(config.Time24Hour, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y"),
|
||||||
|
|
||||||
|
-- coordinator system state flags
|
||||||
|
---@class crd_state
|
||||||
|
crd_state = {
|
||||||
|
fp_ok = false,
|
||||||
|
ui_ok = true, -- default true, used to abort on fail
|
||||||
|
link_fail = false,
|
||||||
|
shutdown = false
|
||||||
|
},
|
||||||
|
|
||||||
|
-- core coordinator devices
|
||||||
|
crd_dev = {
|
||||||
|
speaker = ppm.get_device("speaker"),
|
||||||
|
modem = ppm.get_wireless_modem()
|
||||||
|
},
|
||||||
|
|
||||||
|
-- system objects
|
||||||
|
crd_sys = {
|
||||||
|
nic = nil, ---@type nic
|
||||||
|
coord_comms = nil, ---@type coord_comms
|
||||||
|
conn_watchdog = nil ---@type watchdog
|
||||||
|
},
|
||||||
|
|
||||||
|
-- message queues
|
||||||
|
q = {
|
||||||
|
mq_render = mqueue.new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
local smem_dev = __shared_memory.crd_dev
|
||||||
|
local smem_sys = __shared_memory.crd_sys
|
||||||
|
|
||||||
|
local crd_state = __shared_memory.crd_state
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- setup alarm sounder subsystem
|
-- setup alarm sounder subsystem
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local speaker = ppm.get_device("speaker")
|
if smem_dev.speaker == nil then
|
||||||
if speaker == nil then
|
|
||||||
log_boot("annunciator alarm speaker not found")
|
log_boot("annunciator alarm speaker not found")
|
||||||
println("startup> speaker not found")
|
println("startup> speaker not found")
|
||||||
log.fatal("no annunciator alarm speaker found")
|
log.fatal("no annunciator alarm speaker found")
|
||||||
@@ -111,7 +185,7 @@ local function main()
|
|||||||
else
|
else
|
||||||
local sounder_start = util.time_ms()
|
local sounder_start = util.time_ms()
|
||||||
log_boot("annunciator alarm speaker connected")
|
log_boot("annunciator alarm speaker connected")
|
||||||
sounder.init(speaker, config.SpeakerVolume)
|
sounder.init(smem_dev.speaker, config.SpeakerVolume)
|
||||||
log_boot("tone generation took " .. (util.time_ms() - sounder_start) .. "ms")
|
log_boot("tone generation took " .. (util.time_ms() - sounder_start) .. "ms")
|
||||||
log_sys("annunciator alarm configured")
|
log_sys("annunciator alarm configured")
|
||||||
iocontrol.fp_has_speaker(true)
|
iocontrol.fp_has_speaker(true)
|
||||||
@@ -128,8 +202,7 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- get the communications modem
|
-- get the communications modem
|
||||||
local modem = ppm.get_wireless_modem()
|
if smem_dev.modem == nil then
|
||||||
if modem == nil then
|
|
||||||
log_comms("wireless modem not found")
|
log_comms("wireless modem not found")
|
||||||
println("startup> wireless modem not found")
|
println("startup> wireless modem not found")
|
||||||
log.fatal("no wireless modem on startup")
|
log.fatal("no wireless modem on startup")
|
||||||
@@ -140,238 +213,54 @@ local function main()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- create connection watchdog
|
-- create connection watchdog
|
||||||
local conn_watchdog = util.new_watchdog(config.SVR_Timeout)
|
smem_sys.conn_watchdog = util.new_watchdog(config.SVR_Timeout)
|
||||||
conn_watchdog.cancel()
|
smem_sys.conn_watchdog.cancel()
|
||||||
log.debug("startup> conn watchdog created")
|
log.debug("startup> conn watchdog created")
|
||||||
|
|
||||||
-- create network interface then setup comms
|
-- create network interface then setup comms
|
||||||
local nic = network.nic(modem)
|
smem_sys.nic = network.nic(smem_dev.modem)
|
||||||
local coord_comms = coordinator.comms(COORDINATOR_VERSION, nic, conn_watchdog)
|
smem_sys.coord_comms = coordinator.comms(COORDINATOR_VERSION, smem_sys.nic, smem_sys.conn_watchdog)
|
||||||
log.debug("startup> comms init")
|
log.debug("startup> comms init")
|
||||||
log_comms("comms initialized")
|
log_comms("comms initialized")
|
||||||
|
|
||||||
-- base loop clock (2Hz, 10 ticks)
|
|
||||||
local MAIN_CLOCK = 0.5
|
|
||||||
local loop_clock = util.new_clock(MAIN_CLOCK)
|
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- start front panel & UI start function
|
-- start front panel
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
log_graphics("starting front panel UI...")
|
log_render("starting front panel UI...")
|
||||||
|
|
||||||
local fp_ok, fp_message = renderer.try_start_fp()
|
local fp_message
|
||||||
if not fp_ok then
|
crd_state.fp_ok, fp_message = renderer.try_start_fp()
|
||||||
log_graphics(util.c("front panel UI error: ", fp_message))
|
if not crd_state.fp_ok then
|
||||||
|
log_render(util.c("front panel UI error: ", fp_message))
|
||||||
println_ts("front panel UI creation failed")
|
println_ts("front panel UI creation failed")
|
||||||
log.fatal(util.c("front panel GUI render failed with error ", fp_message))
|
log.fatal(util.c("front panel GUI render failed with error ", fp_message))
|
||||||
return
|
return
|
||||||
else log_graphics("front panel ready") end
|
else log_render("front panel ready") end
|
||||||
|
|
||||||
-- start up the main UI
|
|
||||||
---@return boolean ui_ok started ok
|
|
||||||
local function start_main_ui()
|
|
||||||
log_graphics("starting main UI...")
|
|
||||||
|
|
||||||
local draw_start = util.time_ms()
|
|
||||||
|
|
||||||
local ui_ok, ui_message = renderer.try_start_ui()
|
|
||||||
if not ui_ok then
|
|
||||||
log_graphics(util.c("main UI error: ", ui_message))
|
|
||||||
log.fatal(util.c("main GUI render failed with error ", ui_message))
|
|
||||||
else
|
|
||||||
log_graphics("main UI draw took " .. (util.time_ms() - draw_start) .. "ms")
|
|
||||||
end
|
|
||||||
|
|
||||||
return ui_ok
|
|
||||||
end
|
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main event loop
|
-- start system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
local link_failed = false
|
-- init threads
|
||||||
local ui_ok = true
|
local main_thread = threads.thread__main(__shared_memory)
|
||||||
local date_format = util.trinary(config.Time24Hour, "%X \x04 %A, %B %d %Y", "%r \x04 %A, %B %d %Y")
|
local render_thread = threads.thread__render(__shared_memory)
|
||||||
|
|
||||||
-- start clock
|
log.info("startup> completed")
|
||||||
loop_clock.start()
|
|
||||||
|
|
||||||
log_sys("system started successfully")
|
-- run threads
|
||||||
|
parallel.waitForAll(main_thread.p_exec, render_thread.p_exec)
|
||||||
-- main event loop
|
|
||||||
while true do
|
|
||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
|
||||||
|
|
||||||
-- handle event
|
|
||||||
if event == "peripheral_detach" then
|
|
||||||
local type, device = ppm.handle_unmount(param1)
|
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
|
||||||
if type == "modem" then
|
|
||||||
-- we only really care if this is our wireless modem
|
|
||||||
-- if it is another modem, handle other peripheral losses separately
|
|
||||||
if nic.is_modem(device) then
|
|
||||||
nic.disconnect()
|
|
||||||
log_sys("comms modem disconnected")
|
|
||||||
|
|
||||||
local other_modem = ppm.get_wireless_modem()
|
|
||||||
if other_modem then
|
|
||||||
log_sys("found another wireless modem, using it for comms")
|
|
||||||
nic.connect(other_modem)
|
|
||||||
else
|
|
||||||
-- close out main UI
|
|
||||||
renderer.close_ui()
|
|
||||||
|
|
||||||
-- alert user to status
|
|
||||||
log_sys("awaiting comms modem reconnect...")
|
|
||||||
|
|
||||||
iocontrol.fp_has_modem(false)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log_sys("non-comms modem disconnected")
|
|
||||||
end
|
|
||||||
elseif type == "monitor" then
|
|
||||||
if renderer.handle_disconnect(device) then
|
|
||||||
log_sys("lost a configured monitor")
|
|
||||||
else
|
|
||||||
log_sys("lost an unused monitor")
|
|
||||||
end
|
|
||||||
elseif type == "speaker" then
|
|
||||||
log_sys("lost alarm sounder speaker")
|
|
||||||
iocontrol.fp_has_speaker(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == "peripheral" then
|
|
||||||
local type, device = ppm.mount(param1)
|
|
||||||
|
|
||||||
if type ~= nil and device ~= nil then
|
|
||||||
if type == "modem" then
|
|
||||||
if device.isWireless() and not nic.is_connected() then
|
|
||||||
-- reconnected modem
|
|
||||||
log_sys("comms modem reconnected")
|
|
||||||
nic.connect(device)
|
|
||||||
iocontrol.fp_has_modem(true)
|
|
||||||
elseif device.isWireless() then
|
|
||||||
log.info("unused wireless modem reconnected")
|
|
||||||
else
|
|
||||||
log_sys("wired modem reconnected")
|
|
||||||
end
|
|
||||||
elseif type == "monitor" then
|
|
||||||
if renderer.handle_reconnect(param1, device) then
|
|
||||||
log_sys(util.c("configured monitor ", param1, " reconnected"))
|
|
||||||
else
|
|
||||||
log_sys(util.c("unused monitor ", param1, " connected"))
|
|
||||||
end
|
|
||||||
elseif type == "speaker" then
|
|
||||||
log_sys("alarm sounder speaker reconnected")
|
|
||||||
sounder.reconnect(device)
|
|
||||||
iocontrol.fp_has_speaker(true)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif event == "timer" then
|
|
||||||
if loop_clock.is_clock(param1) then
|
|
||||||
-- main loop tick
|
|
||||||
|
|
||||||
-- toggle heartbeat
|
|
||||||
iocontrol.heartbeat()
|
|
||||||
|
|
||||||
-- maintain connection
|
|
||||||
if nic.is_connected() then
|
|
||||||
local ok, start_ui = coord_comms.try_connect()
|
|
||||||
if not ok then
|
|
||||||
link_failed = true
|
|
||||||
log_sys("supervisor connection failed, shutting down...")
|
|
||||||
log.fatal("failed to connect to supervisor")
|
|
||||||
break
|
|
||||||
elseif start_ui then
|
|
||||||
log_sys("supervisor connected, proceeding to main UI start")
|
|
||||||
ui_ok = start_main_ui()
|
|
||||||
if not ui_ok then break end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- iterate sessions
|
|
||||||
apisessions.iterate_all()
|
|
||||||
|
|
||||||
-- free any closed sessions
|
|
||||||
apisessions.free_all_closed()
|
|
||||||
|
|
||||||
-- update date and time string for main display
|
|
||||||
if coord_comms.is_linked() then
|
|
||||||
iocontrol.get_db().facility.ps.publish("date_time", os.date(date_format))
|
|
||||||
end
|
|
||||||
|
|
||||||
loop_clock.start()
|
|
||||||
elseif conn_watchdog.is_timer(param1) then
|
|
||||||
-- supervisor watchdog timeout
|
|
||||||
log_comms("supervisor server timeout")
|
|
||||||
|
|
||||||
-- close connection, main UI, and stop sounder
|
|
||||||
coord_comms.close()
|
|
||||||
renderer.close_ui()
|
|
||||||
sounder.stop()
|
|
||||||
else
|
|
||||||
-- a non-clock/main watchdog timer event
|
|
||||||
|
|
||||||
-- check API watchdogs
|
|
||||||
apisessions.check_all_watchdogs(param1)
|
|
||||||
|
|
||||||
-- notify timer callback dispatcher
|
|
||||||
tcd.handle(param1)
|
|
||||||
end
|
|
||||||
elseif event == "modem_message" then
|
|
||||||
-- got a packet
|
|
||||||
local packet = coord_comms.parse_packet(param1, param2, param3, param4, param5)
|
|
||||||
|
|
||||||
-- handle then check if it was a disconnect
|
|
||||||
if coord_comms.handle_packet(packet) then
|
|
||||||
log_comms("supervisor closed connection")
|
|
||||||
|
|
||||||
-- close connection, main UI, and stop sounder
|
|
||||||
coord_comms.close()
|
|
||||||
renderer.close_ui()
|
|
||||||
sounder.stop()
|
|
||||||
end
|
|
||||||
elseif event == "monitor_touch" or event == "mouse_click" or event == "mouse_up" or
|
|
||||||
event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
|
||||||
-- handle a mouse event
|
|
||||||
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
|
||||||
elseif event == "speaker_audio_empty" then
|
|
||||||
-- handle speaker buffer emptied
|
|
||||||
sounder.continue()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- check for termination request
|
|
||||||
if event == "terminate" or ppm.should_terminate() then
|
|
||||||
-- handle supervisor connection
|
|
||||||
coord_comms.try_connect(true)
|
|
||||||
|
|
||||||
if coord_comms.is_linked() then
|
|
||||||
log_comms("terminate requested, closing supervisor connection...")
|
|
||||||
else link_failed = true end
|
|
||||||
|
|
||||||
coord_comms.close()
|
|
||||||
log_comms("supervisor connection closed")
|
|
||||||
|
|
||||||
-- handle API sessions
|
|
||||||
log_comms("closing api sessions...")
|
|
||||||
apisessions.close_all()
|
|
||||||
log_comms("api sessions closed")
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
renderer.close_ui()
|
renderer.close_ui()
|
||||||
renderer.close_fp()
|
renderer.close_fp()
|
||||||
sounder.stop()
|
sounder.stop()
|
||||||
log_sys("system shutdown")
|
log_sys("system shutdown")
|
||||||
|
|
||||||
if link_failed then println_ts("failed to connect to supervisor") end
|
if crd_state.link_fail then println_ts("failed to connect to supervisor") end
|
||||||
if not ui_ok then println_ts("main UI creation failed") end
|
if not crd_state.ui_ok then println_ts("main UI creation failed") end
|
||||||
|
|
||||||
-- close on error exit (such as UI error)
|
-- close on error exit (such as UI error)
|
||||||
if coord_comms.is_linked() then coord_comms.close() end
|
if smem_sys.coord_comms.is_linked() then smem_sys.coord_comms.close() end
|
||||||
|
|
||||||
println_ts("exited")
|
println_ts("exited")
|
||||||
log.info("exited")
|
log.info("exited")
|
||||||
|
|||||||
363
coordinator/threads.lua
Normal file
363
coordinator/threads.lua
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
local log = require("scada-common.log")
|
||||||
|
local mqueue = require("scada-common.mqueue")
|
||||||
|
local ppm = require("scada-common.ppm")
|
||||||
|
local tcd = require("scada-common.tcd")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local coordinator = require("coordinator.coordinator")
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
local renderer = require("coordinator.renderer")
|
||||||
|
local sounder = require("coordinator.sounder")
|
||||||
|
|
||||||
|
local apisessions = require("coordinator.session.apisessions")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local log_render = coordinator.log_render
|
||||||
|
local log_sys = coordinator.log_sys
|
||||||
|
local log_comms = coordinator.log_comms
|
||||||
|
|
||||||
|
local threads = {}
|
||||||
|
|
||||||
|
local MAIN_CLOCK = 0.5 -- (2Hz, 10 ticks)
|
||||||
|
local RENDER_SLEEP = 100 -- (100ms, 2 ticks)
|
||||||
|
|
||||||
|
local MQ__RENDER_CMD = {
|
||||||
|
START_MAIN_UI = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local MQ__RENDER_DATA = {
|
||||||
|
MON_CONNECT = 1,
|
||||||
|
MON_DISCONNECT = 2,
|
||||||
|
MON_RESIZE = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
-- main thread
|
||||||
|
---@nodiscard
|
||||||
|
---@param smem crd_shared_memory
|
||||||
|
function threads.thread__main(smem)
|
||||||
|
---@class parallel_thread
|
||||||
|
local public = {}
|
||||||
|
|
||||||
|
-- execute thread
|
||||||
|
function public.exec()
|
||||||
|
iocontrol.fp_rt_status("main", true)
|
||||||
|
log.debug("main thread start")
|
||||||
|
|
||||||
|
local loop_clock = util.new_clock(MAIN_CLOCK)
|
||||||
|
|
||||||
|
-- start clock
|
||||||
|
loop_clock.start()
|
||||||
|
|
||||||
|
log_sys("system started successfully")
|
||||||
|
|
||||||
|
-- load in from shared memory
|
||||||
|
local crd_state = smem.crd_state
|
||||||
|
local nic = smem.crd_sys.nic
|
||||||
|
local coord_comms = smem.crd_sys.coord_comms
|
||||||
|
local conn_watchdog = smem.crd_sys.conn_watchdog
|
||||||
|
|
||||||
|
-- event loop
|
||||||
|
while true do
|
||||||
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
|
-- handle event
|
||||||
|
if event == "peripheral_detach" then
|
||||||
|
local type, device = ppm.handle_unmount(param1)
|
||||||
|
|
||||||
|
if type ~= nil and device ~= nil then
|
||||||
|
if type == "modem" then
|
||||||
|
-- we only really care if this is our wireless modem
|
||||||
|
-- if it is another modem, handle other peripheral losses separately
|
||||||
|
if nic.is_modem(device) then
|
||||||
|
nic.disconnect()
|
||||||
|
log_sys("comms modem disconnected")
|
||||||
|
|
||||||
|
local other_modem = ppm.get_wireless_modem()
|
||||||
|
if other_modem then
|
||||||
|
log_sys("found another wireless modem, using it for comms")
|
||||||
|
nic.connect(other_modem)
|
||||||
|
else
|
||||||
|
-- close out main UI
|
||||||
|
renderer.close_ui()
|
||||||
|
|
||||||
|
-- alert user to status
|
||||||
|
log_sys("awaiting comms modem reconnect...")
|
||||||
|
|
||||||
|
iocontrol.fp_has_modem(false)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log_sys("non-comms modem disconnected")
|
||||||
|
end
|
||||||
|
elseif type == "monitor" then
|
||||||
|
smem.q.mq_render.push_data(MQ__RENDER_DATA.MON_DISCONNECT, device)
|
||||||
|
elseif type == "speaker" then
|
||||||
|
log_sys("lost alarm sounder speaker")
|
||||||
|
iocontrol.fp_has_speaker(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "peripheral" then
|
||||||
|
local type, device = ppm.mount(param1)
|
||||||
|
|
||||||
|
if type ~= nil and device ~= nil then
|
||||||
|
if type == "modem" then
|
||||||
|
if device.isWireless() and not nic.is_connected() then
|
||||||
|
-- reconnected modem
|
||||||
|
log_sys("comms modem reconnected")
|
||||||
|
nic.connect(device)
|
||||||
|
iocontrol.fp_has_modem(true)
|
||||||
|
elseif device.isWireless() then
|
||||||
|
log.info("unused wireless modem reconnected")
|
||||||
|
else
|
||||||
|
log_sys("wired modem reconnected")
|
||||||
|
end
|
||||||
|
elseif type == "monitor" then
|
||||||
|
smem.q.mq_render.push_data(MQ__RENDER_DATA.MON_CONNECT, { name = param1, device = device })
|
||||||
|
elseif type == "speaker" then
|
||||||
|
log_sys("alarm sounder speaker reconnected")
|
||||||
|
sounder.reconnect(device)
|
||||||
|
iocontrol.fp_has_speaker(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif event == "monitor_resize" then
|
||||||
|
smem.q.mq_render.push_data(MQ__RENDER_DATA.MON_RESIZE, param1)
|
||||||
|
elseif event == "timer" then
|
||||||
|
if loop_clock.is_clock(param1) then
|
||||||
|
-- main loop tick
|
||||||
|
|
||||||
|
-- toggle heartbeat
|
||||||
|
iocontrol.heartbeat()
|
||||||
|
|
||||||
|
-- maintain connection
|
||||||
|
if nic.is_connected() then
|
||||||
|
local ok, start_ui = coord_comms.try_connect()
|
||||||
|
if not ok then
|
||||||
|
crd_state.link_fail = true
|
||||||
|
crd_state.shutdown = true
|
||||||
|
log_sys("supervisor connection failed, shutting down...")
|
||||||
|
log.fatal("failed to connect to supervisor")
|
||||||
|
break
|
||||||
|
elseif start_ui then
|
||||||
|
log_sys("supervisor connected, dispatching main UI start")
|
||||||
|
smem.q.mq_render.push_command(MQ__RENDER_CMD.START_MAIN_UI)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- iterate sessions and free any closed ones
|
||||||
|
apisessions.iterate_all()
|
||||||
|
apisessions.free_all_closed()
|
||||||
|
|
||||||
|
if renderer.ui_ready() then
|
||||||
|
-- update clock used on main and flow monitors
|
||||||
|
iocontrol.get_db().facility.ps.publish("date_time", os.date(smem.date_format))
|
||||||
|
end
|
||||||
|
|
||||||
|
loop_clock.start()
|
||||||
|
elseif conn_watchdog.is_timer(param1) then
|
||||||
|
-- supervisor watchdog timeout
|
||||||
|
log_comms("supervisor server timeout")
|
||||||
|
|
||||||
|
-- close connection, main UI, and stop sounder
|
||||||
|
coord_comms.close()
|
||||||
|
renderer.close_ui()
|
||||||
|
sounder.stop()
|
||||||
|
else
|
||||||
|
-- a non-clock/main watchdog timer event
|
||||||
|
|
||||||
|
-- check API watchdogs
|
||||||
|
apisessions.check_all_watchdogs(param1)
|
||||||
|
|
||||||
|
-- notify timer callback dispatcher
|
||||||
|
tcd.handle(param1)
|
||||||
|
end
|
||||||
|
elseif event == "modem_message" then
|
||||||
|
-- got a packet
|
||||||
|
local packet = coord_comms.parse_packet(param1, param2, param3, param4, param5)
|
||||||
|
|
||||||
|
-- handle then check if it was a disconnect
|
||||||
|
if coord_comms.handle_packet(packet) then
|
||||||
|
log_comms("supervisor closed connection")
|
||||||
|
|
||||||
|
-- close connection, main UI, and stop sounder
|
||||||
|
coord_comms.close()
|
||||||
|
renderer.close_ui()
|
||||||
|
sounder.stop()
|
||||||
|
end
|
||||||
|
elseif event == "monitor_touch" or event == "mouse_click" or event == "mouse_up" or
|
||||||
|
event == "mouse_drag" or event == "mouse_scroll" or event == "double_click" then
|
||||||
|
-- handle a mouse event
|
||||||
|
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
||||||
|
elseif event == "speaker_audio_empty" then
|
||||||
|
-- handle speaker buffer emptied
|
||||||
|
sounder.continue()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request or UI crash
|
||||||
|
if event == "terminate" or ppm.should_terminate() then
|
||||||
|
crd_state.shutdown = true
|
||||||
|
log.info("terminate requested, main thread exiting")
|
||||||
|
elseif not crd_state.ui_ok then
|
||||||
|
crd_state.shutdown = true
|
||||||
|
log.info("terminating due to fatal UI error")
|
||||||
|
end
|
||||||
|
|
||||||
|
if crd_state.shutdown then
|
||||||
|
-- handle closing supervisor connection
|
||||||
|
coord_comms.try_connect(true)
|
||||||
|
|
||||||
|
if coord_comms.is_linked() then
|
||||||
|
log_comms("closing supervisor connection...")
|
||||||
|
else crd_state.link_fail = true end
|
||||||
|
|
||||||
|
coord_comms.close()
|
||||||
|
log_comms("supervisor connection closed")
|
||||||
|
|
||||||
|
-- handle API sessions
|
||||||
|
log_comms("closing api sessions...")
|
||||||
|
apisessions.close_all()
|
||||||
|
log_comms("api sessions closed")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute the thread in a protected mode, retrying it on return if not shutting down
|
||||||
|
function public.p_exec()
|
||||||
|
local crd_state = smem.crd_state
|
||||||
|
|
||||||
|
while not crd_state.shutdown do
|
||||||
|
local status, result = pcall(public.exec)
|
||||||
|
if status == false then
|
||||||
|
log.fatal(util.strval(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_rt_status("main", false)
|
||||||
|
|
||||||
|
-- if status is true, then we are probably exiting, so this won't matter
|
||||||
|
-- this thread cannot be slept because it will miss events (namely "terminate")
|
||||||
|
if not crd_state.shutdown then
|
||||||
|
log.info("main thread restarting now...")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return public
|
||||||
|
end
|
||||||
|
|
||||||
|
-- coordinator renderer thread, tasked with long duration re-draws
|
||||||
|
---@nodiscard
|
||||||
|
---@param smem crd_shared_memory
|
||||||
|
function threads.thread__render(smem)
|
||||||
|
---@class parallel_thread
|
||||||
|
local public = {}
|
||||||
|
|
||||||
|
-- execute thread
|
||||||
|
function public.exec()
|
||||||
|
iocontrol.fp_rt_status("render", true)
|
||||||
|
log.debug("render thread start")
|
||||||
|
|
||||||
|
-- load in from shared memory
|
||||||
|
local crd_state = smem.crd_state
|
||||||
|
local render_queue = smem.q.mq_render
|
||||||
|
|
||||||
|
local last_update = util.time()
|
||||||
|
|
||||||
|
-- thread loop
|
||||||
|
while true do
|
||||||
|
-- check for messages in the message queue
|
||||||
|
while render_queue.ready() and not crd_state.shutdown do
|
||||||
|
local msg = render_queue.pop()
|
||||||
|
|
||||||
|
if msg ~= nil then
|
||||||
|
if msg.qtype == mqueue.TYPE.COMMAND then
|
||||||
|
-- received a command
|
||||||
|
if msg.message == MQ__RENDER_CMD.START_MAIN_UI then
|
||||||
|
-- stop the UI if it was already started
|
||||||
|
-- this may occur on a quick supervisor disconnect -> connect
|
||||||
|
if renderer.ui_ready() then
|
||||||
|
log_render("closing main UI before executing new request to start")
|
||||||
|
renderer.close_ui()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start up the main UI
|
||||||
|
log_render("starting main UI...")
|
||||||
|
|
||||||
|
local draw_start = util.time_ms()
|
||||||
|
|
||||||
|
local ui_message
|
||||||
|
crd_state.ui_ok, ui_message = renderer.try_start_ui()
|
||||||
|
if not crd_state.ui_ok then
|
||||||
|
log_render(util.c("main UI error: ", ui_message))
|
||||||
|
log.fatal(util.c("main GUI render failed with error ", ui_message))
|
||||||
|
else
|
||||||
|
log_render("main UI draw took " .. (util.time_ms() - draw_start) .. "ms")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif msg.qtype == mqueue.TYPE.DATA then
|
||||||
|
-- received data
|
||||||
|
local cmd = msg.message ---@type queue_data
|
||||||
|
|
||||||
|
if cmd.key == MQ__RENDER_DATA.MON_CONNECT then
|
||||||
|
-- monitor connected
|
||||||
|
if renderer.handle_reconnect(cmd.val.name, cmd.val.device) then
|
||||||
|
log_sys(util.c("configured monitor ", cmd.val.name, " reconnected"))
|
||||||
|
else
|
||||||
|
log_sys(util.c("unused monitor ", cmd.val.name, " connected"))
|
||||||
|
end
|
||||||
|
elseif cmd.key == MQ__RENDER_DATA.MON_DISCONNECT then
|
||||||
|
-- monitor disconnected
|
||||||
|
if renderer.handle_disconnect(cmd.val) then
|
||||||
|
log_sys("lost a configured monitor")
|
||||||
|
else
|
||||||
|
log_sys("lost an unused monitor")
|
||||||
|
end
|
||||||
|
elseif cmd.key == MQ__RENDER_DATA.MON_RESIZE then
|
||||||
|
-- monitor resized
|
||||||
|
local is_used, is_ok = renderer.handle_resize(cmd.val)
|
||||||
|
if is_used then
|
||||||
|
log_sys(util.c("configured monitor ", cmd.val, " resized, ", util.trinary(is_ok, "display fits", "display does not fit")))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif msg.qtype == mqueue.TYPE.PACKET then
|
||||||
|
-- received a packet
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- quick yield
|
||||||
|
util.nop()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for termination request
|
||||||
|
if crd_state.shutdown then
|
||||||
|
log.info("render thread exiting")
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
-- delay before next check
|
||||||
|
last_update = util.adaptive_delay(RENDER_SLEEP, last_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- execute the thread in a protected mode, retrying it on return if not shutting down
|
||||||
|
function public.p_exec()
|
||||||
|
local crd_state = smem.crd_state
|
||||||
|
|
||||||
|
while not crd_state.shutdown do
|
||||||
|
local status, result = pcall(public.exec)
|
||||||
|
if status == false then
|
||||||
|
log.fatal(util.strval(result))
|
||||||
|
end
|
||||||
|
|
||||||
|
iocontrol.fp_rt_status("render", false)
|
||||||
|
|
||||||
|
if not crd_state.shutdown then
|
||||||
|
log.info("render thread restarting in 5 seconds...")
|
||||||
|
util.psleep(5)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return public
|
||||||
|
end
|
||||||
|
|
||||||
|
return threads
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
|
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Rectangle = require("graphics.elements.rectangle")
|
local Rectangle = require("graphics.elements.rectangle")
|
||||||
@@ -12,28 +14,31 @@ local VerticalBar = require("graphics.elements.indicators.vbar")
|
|||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
local border = core.border
|
local border = core.border
|
||||||
|
|
||||||
local text_fg_bg = style.text_colors
|
|
||||||
|
|
||||||
-- new boiler view
|
-- new boiler view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
---@param y integer top left y
|
---@param y integer top left y
|
||||||
---@param ps psil ps interface
|
---@param ps psil ps interface
|
||||||
local function new_view(root, x, y, ps)
|
local function new_view(root, x, y, ps)
|
||||||
|
local text_fg = style.theme.text_fg
|
||||||
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local boiler = Rectangle{parent=root,border=border(1,colors.gray,true),width=31,height=7,x=x,y=y}
|
local boiler = Rectangle{parent=root,border=border(1,colors.gray,true),width=31,height=7,x=x,y=y}
|
||||||
|
|
||||||
local status = StateIndicator{parent=boiler,x=9,y=1,states=style.boiler.states,value=1,min_width=12}
|
local status = StateIndicator{parent=boiler,x=9,y=1,states=style.boiler.states,value=1,min_width=12}
|
||||||
local temp = DataIndicator{parent=boiler,x=5,y=3,lu_colors=style.lu_col,label="Temp:",unit="K",format="%10.2f",value=0,width=22,fg_bg=text_fg_bg}
|
local temp = DataIndicator{parent=boiler,x=5,y=3,lu_colors=lu_col,label="Temp:",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=22,fg_bg=text_fg}
|
||||||
local boil_r = DataIndicator{parent=boiler,x=5,y=4,lu_colors=style.lu_col,label="Boil:",unit="mB/t",format="%10.0f",value=0,commas=true,width=22,fg_bg=text_fg_bg}
|
local boil_r = DataIndicator{parent=boiler,x=5,y=4,lu_colors=lu_col,label="Boil:",unit="mB/t",format="%10.0f",value=0,commas=true,width=22,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
temp.register(ps, "temperature", temp.update)
|
temp.register(ps, "temperature", function (t) temp.update(db.temp_convert(t)) end)
|
||||||
boil_r.register(ps, "boil_rate", boil_r.update)
|
boil_r.register(ps, "boil_rate", boil_r.update)
|
||||||
|
|
||||||
TextBox{parent=boiler,text="H",x=2,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=boiler,text="H",x=2,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="W",x=3,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=boiler,text="W",x=3,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="S",x=27,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=boiler,text="S",x=27,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=boiler,text="C",x=28,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=boiler,text="C",x=28,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
|
|
||||||
local hcool = VerticalBar{parent=boiler,x=2,y=1,fg_bg=cpair(colors.orange,colors.gray),height=4,width=1}
|
local hcool = VerticalBar{parent=boiler,x=2,y=1,fg_bg=cpair(colors.orange,colors.gray),height=4,width=1}
|
||||||
local water = VerticalBar{parent=boiler,x=3,y=1,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
local water = VerticalBar{parent=boiler,x=3,y=1,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ local Rectangle = require("graphics.elements.rectangle")
|
|||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
local DataIndicator = require("graphics.elements.indicators.data")
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local IndicatorLight = require("graphics.elements.indicators.light")
|
||||||
local PowerIndicator = require("graphics.elements.indicators.power")
|
local PowerIndicator = require("graphics.elements.indicators.power")
|
||||||
local StateIndicator = require("graphics.elements.indicators.state")
|
local StateIndicator = require("graphics.elements.indicators.state")
|
||||||
local VerticalBar = require("graphics.elements.indicators.vbar")
|
local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
@@ -18,9 +19,6 @@ local border = core.border
|
|||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
local text_fg_bg = style.text_colors
|
|
||||||
local lu_col = style.lu_colors
|
|
||||||
|
|
||||||
-- new induction matrix view
|
-- new induction matrix view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
@@ -29,57 +27,67 @@ local lu_col = style.lu_colors
|
|||||||
---@param ps psil ps interface
|
---@param ps psil ps interface
|
||||||
---@param id number? matrix ID
|
---@param id number? matrix ID
|
||||||
local function new_view(root, x, y, data, ps, id)
|
local function new_view(root, x, y, data, ps, id)
|
||||||
|
local label_fg = style.theme.label_fg
|
||||||
|
local text_fg = style.theme.text_fg
|
||||||
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
|
local ind_yel = style.ind_yel
|
||||||
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
local title = "INDUCTION MATRIX"
|
local title = "INDUCTION MATRIX"
|
||||||
if type(id) == "number" then title = title .. id end
|
if type(id) == "number" then title = title .. id end
|
||||||
|
|
||||||
local matrix = Div{parent=root,fg_bg=style.root,width=33,height=24,x=x,y=y}
|
local matrix = Div{parent=root,fg_bg=style.root,width=33,height=24,x=x,y=y}
|
||||||
|
|
||||||
TextBox{parent=matrix,text=" ",width=33,height=1,x=1,y=1,fg_bg=style.lg_gray}
|
-- black has low contrast with dark gray, so if background is black use white instead
|
||||||
TextBox{parent=matrix,text=title,alignment=ALIGN.CENTER,width=33,height=1,x=1,y=2,fg_bg=style.lg_gray}
|
local cutout_fg_bg = cpair(util.trinary(style.theme.bg == colors.black, colors.white, style.theme.bg), colors.gray)
|
||||||
|
|
||||||
|
TextBox{parent=matrix,text=" ",width=33,height=1,x=1,y=1,fg_bg=cutout_fg_bg}
|
||||||
|
TextBox{parent=matrix,text=title,alignment=ALIGN.CENTER,width=33,height=1,x=1,y=2,fg_bg=cutout_fg_bg}
|
||||||
|
|
||||||
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
local rect = Rectangle{parent=matrix,border=border(1,colors.gray,true),width=33,height=22,x=1,y=3}
|
||||||
|
|
||||||
local label_fg_bg = cpair(colors.gray, colors.lightGray)
|
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
||||||
|
local capacity = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local status = StateIndicator{parent=rect,x=10,y=1,states=style.imatrix.states,value=1,min_width=14}
|
local energy = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local energy = PowerIndicator{parent=rect,x=7,y=3,lu_colors=lu_col,label="Energy: ",format="%8.2f",value=0,width=26,fg_bg=text_fg_bg}
|
local avg_chg = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local capacity = PowerIndicator{parent=rect,x=7,y=4,lu_colors=lu_col,label="Capacity:",format="%8.2f",value=0,width=26,fg_bg=text_fg_bg}
|
local input = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local input = PowerIndicator{parent=rect,x=7,y=5,lu_colors=lu_col,label="Input: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg_bg}
|
local avg_in = PowerIndicator{parent=rect,x=7,y=7,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local output = PowerIndicator{parent=rect,x=7,y=6,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg_bg}
|
local output = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Output: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
|
local avg_out = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="\xb7Average:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_chg = PowerIndicator{parent=rect,x=7,y=8,lu_colors=lu_col,label="Avg. Chg:",format="%8.2f",value=0,width=26,fg_bg=text_fg_bg}
|
local trans_cap = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Max I/O: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg}
|
||||||
local avg_in = PowerIndicator{parent=rect,x=7,y=9,lu_colors=lu_col,label="Avg. In: ",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg_bg}
|
|
||||||
local avg_out = PowerIndicator{parent=rect,x=7,y=10,lu_colors=lu_col,label="Avg. Out:",format="%8.2f",rate=true,value=0,width=26,fg_bg=text_fg_bg}
|
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end)
|
|
||||||
capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end)
|
capacity.register(ps, "max_energy", function (val) capacity.update(util.joules_to_fe(val)) end)
|
||||||
input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end)
|
energy.register(ps, "energy", function (val) energy.update(util.joules_to_fe(val)) end)
|
||||||
output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end)
|
|
||||||
|
|
||||||
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
avg_chg.register(ps, "avg_charge", avg_chg.update)
|
||||||
|
input.register(ps, "last_input", function (val) input.update(util.joules_to_fe(val)) end)
|
||||||
avg_in.register(ps, "avg_inflow", avg_in.update)
|
avg_in.register(ps, "avg_inflow", avg_in.update)
|
||||||
|
output.register(ps, "last_output", function (val) output.update(util.joules_to_fe(val)) end)
|
||||||
avg_out.register(ps, "avg_outflow", avg_out.update)
|
avg_out.register(ps, "avg_outflow", avg_out.update)
|
||||||
|
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end)
|
||||||
|
|
||||||
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill:",unit="%",format="%8.2f",value=0,width=18,fg_bg=text_fg_bg}
|
local fill = DataIndicator{parent=rect,x=11,y=12,lu_colors=lu_col,label="Fill: ",format="%7.2f",unit="%",value=0,width=20,fg_bg=text_fg}
|
||||||
|
local cells = DataIndicator{parent=rect,x=11,y=13,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg}
|
||||||
local cells = DataIndicator{parent=rect,x=11,y=14,lu_colors=lu_col,label="Cells: ",format="%7d",value=0,width=18,fg_bg=text_fg_bg}
|
local providers = DataIndicator{parent=rect,x=11,y=14,lu_colors=lu_col,label="Providers:",format="%7d",value=0,width=18,fg_bg=text_fg}
|
||||||
local providers = DataIndicator{parent=rect,x=11,y=15,lu_colors=lu_col,label="Providers:",format="%7d",value=0,width=18,fg_bg=text_fg_bg}
|
|
||||||
|
|
||||||
TextBox{parent=rect,text="Transfer Capacity",x=11,y=17,height=1,width=17,fg_bg=label_fg_bg}
|
|
||||||
local trans_cap = PowerIndicator{parent=rect,x=19,y=18,lu_colors=lu_col,label="",format="%5.2f",rate=true,value=0,width=12,fg_bg=text_fg_bg}
|
|
||||||
|
|
||||||
|
fill.register(ps, "energy_fill", function (val) fill.update(val * 100) end)
|
||||||
cells.register(ps, "cells", cells.update)
|
cells.register(ps, "cells", cells.update)
|
||||||
providers.register(ps, "providers", providers.update)
|
providers.register(ps, "providers", providers.update)
|
||||||
fill.register(ps, "energy_fill", function (val) fill.update(val * 100) end)
|
|
||||||
trans_cap.register(ps, "transfer_cap", function (val) trans_cap.update(util.joules_to_fe(val)) end)
|
local chging = IndicatorLight{parent=rect,x=11,y=16,label="Charging",colors=ind_wht}
|
||||||
|
local dischg = IndicatorLight{parent=rect,x=11,y=17,label="Discharging",colors=ind_wht}
|
||||||
|
local max_io = IndicatorLight{parent=rect,x=11,y=18,label="Max I/O Rate",colors=ind_yel}
|
||||||
|
|
||||||
|
chging.register(ps, "is_charging", chging.update)
|
||||||
|
dischg.register(ps, "is_discharging", dischg.update)
|
||||||
|
max_io.register(ps, "at_max_io", max_io.update)
|
||||||
|
|
||||||
local charge = VerticalBar{parent=rect,x=2,y=2,fg_bg=cpair(colors.green,colors.gray),height=17,width=4}
|
local charge = VerticalBar{parent=rect,x=2,y=2,fg_bg=cpair(colors.green,colors.gray),height=17,width=4}
|
||||||
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
local in_cap = VerticalBar{parent=rect,x=7,y=12,fg_bg=cpair(colors.red,colors.gray),height=7,width=1}
|
||||||
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
local out_cap = VerticalBar{parent=rect,x=9,y=12,fg_bg=cpair(colors.blue,colors.gray),height=7,width=1}
|
||||||
|
|
||||||
TextBox{parent=rect,text="FILL",x=2,y=20,height=1,width=4,fg_bg=text_fg_bg}
|
TextBox{parent=rect,text="FILL I/O",x=2,y=20,height=1,width=8,fg_bg=label_fg}
|
||||||
TextBox{parent=rect,text="I/O",x=7,y=20,height=1,width=3,fg_bg=text_fg_bg}
|
|
||||||
|
|
||||||
local function calc_saturation(val)
|
local function calc_saturation(val)
|
||||||
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
if (type(data.build) == "table") and (type(data.build.transfer_cap) == "number") and (data.build.transfer_cap > 0) then
|
||||||
@@ -90,6 +98,49 @@ local function new_view(root, x, y, data, ps, id)
|
|||||||
charge.register(ps, "energy_fill", charge.update)
|
charge.register(ps, "energy_fill", charge.update)
|
||||||
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
in_cap.register(ps, "last_input", function (val) in_cap.update(calc_saturation(val)) end)
|
||||||
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
out_cap.register(ps, "last_output", function (val) out_cap.update(calc_saturation(val)) end)
|
||||||
|
|
||||||
|
local eta = TextBox{parent=rect,x=11,y=20,width=20,height=1,text="ETA Unknown",alignment=ALIGN.CENTER,fg_bg=style.theme.field_box}
|
||||||
|
|
||||||
|
eta.register(ps, "eta_ms", function (eta_ms)
|
||||||
|
local str, pre = "", util.trinary(eta_ms >= 0, "Full in ", "Empty in ")
|
||||||
|
|
||||||
|
local seconds = math.abs(eta_ms) / 1000
|
||||||
|
local minutes = seconds / 60
|
||||||
|
local hours = minutes / 60
|
||||||
|
local days = hours / 24
|
||||||
|
|
||||||
|
if math.abs(eta_ms) < 1000 or (eta_ms ~= eta_ms) then
|
||||||
|
-- really small or NaN
|
||||||
|
str = "No ETA"
|
||||||
|
elseif days < 1000 then
|
||||||
|
days = math.floor(days)
|
||||||
|
hours = math.floor(hours % 24)
|
||||||
|
minutes = math.floor(minutes % 60)
|
||||||
|
seconds = math.floor(seconds % 60)
|
||||||
|
|
||||||
|
if days > 0 then
|
||||||
|
str = days .. "d"
|
||||||
|
elseif hours > 0 then
|
||||||
|
str = hours .. "h " .. minutes .. "m"
|
||||||
|
elseif minutes > 0 then
|
||||||
|
str = minutes .. "m " .. seconds .. "s"
|
||||||
|
elseif seconds > 0 then
|
||||||
|
str = seconds .. "s"
|
||||||
|
end
|
||||||
|
|
||||||
|
str = pre .. str
|
||||||
|
else
|
||||||
|
local years = math.floor(days / 365.25)
|
||||||
|
|
||||||
|
if years <= 99999999 then
|
||||||
|
str = pre .. years .. "y"
|
||||||
|
else
|
||||||
|
str = pre .. "eras"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
eta.set_value(str)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_view
|
return new_view
|
||||||
|
|||||||
@@ -17,33 +17,35 @@ local ALIGN = core.ALIGN
|
|||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local text_fg_bg = style.text_colors
|
|
||||||
local lg_wh = style.lg_white
|
|
||||||
|
|
||||||
-- create a pocket list entry
|
-- create a pocket list entry
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param id integer PKT session ID
|
---@param id integer PKT session ID
|
||||||
local function init(parent, id)
|
local function init(parent, id)
|
||||||
|
local s_hi_box = style.fp_theme.highlight_box
|
||||||
|
local s_hi_bright = style.fp_theme.highlight_box_bright
|
||||||
|
|
||||||
|
local label_fg = style.fp.label_fg
|
||||||
|
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
local ps_prefix = "pkt_" .. id .. "_"
|
local ps_prefix = "pkt_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=text_fg_bg,nav_active=cpair(colors.gray,colors.black)}
|
local pkt_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value)
|
pkt_addr.register(ps, ps_prefix .. "addr", pkt_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
||||||
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=lg_wh}
|
local pkt_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
||||||
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
pkt_fw_v.register(ps, ps_prefix .. "fw", pkt_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
||||||
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_wh}
|
local pkt_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_wh}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
pkt_rtt.register(ps, ps_prefix .. "rtt", pkt_rtt.update)
|
||||||
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
pkt_rtt.register(ps, ps_prefix .. "rtt_color", pkt_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -29,16 +29,6 @@ local cpair = core.cpair
|
|||||||
local border = core.border
|
local border = core.border
|
||||||
|
|
||||||
local bw_fg_bg = style.bw_fg_bg
|
local bw_fg_bg = style.bw_fg_bg
|
||||||
local lu_cpair = style.lu_colors
|
|
||||||
local hzd_fg_bg = style.hzd_fg_bg
|
|
||||||
local dis_colors = style.dis_colors
|
|
||||||
|
|
||||||
local gry_wht = style.gray_white
|
|
||||||
|
|
||||||
local ind_grn = style.ind_grn
|
|
||||||
local ind_yel = style.ind_yel
|
|
||||||
local ind_red = style.ind_red
|
|
||||||
local ind_wht = style.ind_wht
|
|
||||||
|
|
||||||
local period = core.flasher.PERIOD
|
local period = core.flasher.PERIOD
|
||||||
|
|
||||||
@@ -47,6 +37,19 @@ local period = core.flasher.PERIOD
|
|||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
---@param y integer top left y
|
---@param y integer top left y
|
||||||
local function new_view(root, x, y)
|
local function new_view(root, x, y)
|
||||||
|
local s_hi_box = style.theme.highlight_box
|
||||||
|
local s_field = style.theme.field_box
|
||||||
|
|
||||||
|
local lu_cpair = style.lu_colors
|
||||||
|
local hzd_fg_bg = style.hzd_fg_bg
|
||||||
|
local dis_colors = style.dis_colors
|
||||||
|
local arrow_fg_bg = cpair(style.theme.label, s_hi_box.bkg)
|
||||||
|
|
||||||
|
local ind_grn = style.ind_grn
|
||||||
|
local ind_yel = style.ind_yel
|
||||||
|
local ind_red = style.ind_red
|
||||||
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
assert(root.get_height() >= (y + 24), "main display not of sufficient vertical resolution (add an additional row of monitors)")
|
assert(root.get_height() >= (y + 24), "main display not of sufficient vertical resolution (add an additional row of monitors)")
|
||||||
|
|
||||||
local black = cpair(colors.black, colors.black)
|
local black = cpair(colors.black, colors.black)
|
||||||
@@ -65,7 +68,7 @@ local function new_view(root, x, y)
|
|||||||
facility.ack_alarms_ack = ack_a.on_response
|
facility.ack_alarms_ack = ack_a.on_response
|
||||||
|
|
||||||
local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=ind_grn}
|
local all_ok = IndicatorLight{parent=main,y=5,label="Unit Systems Online",colors=ind_grn}
|
||||||
local rad_mon = TriIndicatorLight{parent=main,label="Radiation Monitor",c1=colors.gray,c2=colors.yellow,c3=colors.green}
|
local rad_mon = TriIndicatorLight{parent=main,label="Radiation Monitor",c1=style.ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd}
|
||||||
local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=ind_grn}
|
local ind_mat = IndicatorLight{parent=main,label="Induction Matrix",colors=ind_grn}
|
||||||
local sps = IndicatorLight{parent=main,label="SPS Connected",colors=ind_grn}
|
local sps = IndicatorLight{parent=main,label="SPS Connected",colors=ind_grn}
|
||||||
|
|
||||||
@@ -103,11 +106,11 @@ local function new_view(root, x, y)
|
|||||||
gen_fault.register(facility.ps, "as_gen_fault", gen_fault.update)
|
gen_fault.register(facility.ps, "as_gen_fault", gen_fault.update)
|
||||||
|
|
||||||
TextBox{parent=main,y=23,text="Radiation",height=1,width=13,fg_bg=style.label}
|
TextBox{parent=main,y=23,text="Radiation",height=1,width=13,fg_bg=style.label}
|
||||||
local radiation = RadIndicator{parent=main,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=bw_fg_bg}
|
local radiation = RadIndicator{parent=main,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
radiation.register(facility.ps, "radiation", radiation.update)
|
radiation.register(facility.ps, "radiation", radiation.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=15,y=23,text="Linked RTUs",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=main,x=15,y=23,text="Linked RTUs",height=1,width=11,fg_bg=style.label}
|
||||||
local rtu_count = DataIndicator{parent=main,x=15,y=24,label="",format="%11d",value=0,lu_colors=lu_cpair,width=11,fg_bg=bw_fg_bg}
|
local rtu_count = DataIndicator{parent=main,x=15,y=24,label="",format="%11d",value=0,lu_colors=lu_cpair,width=11,fg_bg=s_field}
|
||||||
rtu_count.register(facility.ps, "rtu_count", rtu_count.update)
|
rtu_count.register(facility.ps, "rtu_count", rtu_count.update)
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
@@ -125,9 +128,9 @@ local function new_view(root, x, y)
|
|||||||
local burn_tag = Div{parent=targets,x=1,y=1,width=8,height=4,fg_bg=blk_pur}
|
local burn_tag = Div{parent=targets,x=1,y=1,width=8,height=4,fg_bg=blk_pur}
|
||||||
TextBox{parent=burn_tag,x=2,y=2,text="Burn Target",width=7,height=2}
|
TextBox{parent=burn_tag,x=2,y=2,text="Burn Target",width=7,height=2}
|
||||||
|
|
||||||
local burn_target = Div{parent=targets,x=9,y=1,width=23,height=3,fg_bg=gry_wht}
|
local burn_target = Div{parent=targets,x=9,y=1,width=23,height=3,fg_bg=s_hi_box}
|
||||||
local b_target = SpinboxNumeric{parent=burn_target,x=11,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
|
local b_target = SpinboxNumeric{parent=burn_target,x=11,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=burn_target,x=18,y=2,text="mB/t"}
|
TextBox{parent=burn_target,x=18,y=2,text="mB/t",fg_bg=style.theme.label_fg}
|
||||||
local burn_sum = DataIndicator{parent=targets,x=9,y=4,label="",format="%18.1f",value=0,unit="mB/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local burn_sum = DataIndicator{parent=targets,x=9,y=4,label="",format="%18.1f",value=0,unit="mB/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
b_target.register(facility.ps, "process_burn_target", b_target.set_value)
|
b_target.register(facility.ps, "process_burn_target", b_target.set_value)
|
||||||
@@ -136,20 +139,20 @@ local function new_view(root, x, y)
|
|||||||
local chg_tag = Div{parent=targets,x=1,y=6,width=8,height=4,fg_bg=blk_pur}
|
local chg_tag = Div{parent=targets,x=1,y=6,width=8,height=4,fg_bg=blk_pur}
|
||||||
TextBox{parent=chg_tag,x=2,y=2,text="Charge Target",width=7,height=2}
|
TextBox{parent=chg_tag,x=2,y=2,text="Charge Target",width=7,height=2}
|
||||||
|
|
||||||
local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=gry_wht}
|
local chg_target = Div{parent=targets,x=9,y=6,width=23,height=3,fg_bg=s_hi_box}
|
||||||
local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
|
local c_target = SpinboxNumeric{parent=chg_target,x=2,y=1,whole_num_precision=15,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=chg_target,x=18,y=2,text="MFE"}
|
TextBox{parent=chg_target,x=18,y=2,text="MFE",fg_bg=style.theme.label_fg}
|
||||||
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local cur_charge = DataIndicator{parent=targets,x=9,y=9,label="",format="%19d",value=0,unit="MFE",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
c_target.register(facility.ps, "process_charge_target", c_target.set_value)
|
||||||
cur_charge.register(facility.induction_ps_tbl[1], "energy", function (j) cur_charge.update(util.joules_to_fe(j) / 1000000) end)
|
cur_charge.register(facility.induction_ps_tbl[1], "avg_charge", function (fe) cur_charge.update(fe / 1000000) end)
|
||||||
|
|
||||||
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
local gen_tag = Div{parent=targets,x=1,y=11,width=8,height=4,fg_bg=blk_pur}
|
||||||
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
TextBox{parent=gen_tag,x=2,y=2,text="Gen. Target",width=7,height=2}
|
||||||
|
|
||||||
local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=gry_wht}
|
local gen_target = Div{parent=targets,x=9,y=11,width=23,height=3,fg_bg=s_hi_box}
|
||||||
local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
|
local g_target = SpinboxNumeric{parent=gen_target,x=8,y=1,whole_num_precision=9,fractional_precision=0,min=0,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=gen_target,x=18,y=2,text="kFE/t"}
|
TextBox{parent=gen_target,x=18,y=2,text="kFE/t",fg_bg=style.theme.label_fg}
|
||||||
local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
local cur_gen = DataIndicator{parent=targets,x=9,y=14,label="",format="%17d",value=0,unit="kFE/t",commas=true,lu_colors=black,width=23,fg_bg=blk_brn}
|
||||||
|
|
||||||
g_target.register(facility.ps, "process_gen_target", g_target.set_value)
|
g_target.register(facility.ps, "process_gen_target", g_target.set_value)
|
||||||
@@ -165,17 +168,17 @@ local function new_view(root, x, y)
|
|||||||
|
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
local unit
|
local unit
|
||||||
local tag_fg_bg = gry_wht
|
local tag_fg_bg = cpair(style.theme.disabled, s_hi_box.bkg)
|
||||||
local lim_fg_bg = style.lg_white
|
local lim_fg_bg = cpair(style.theme.disabled, s_hi_box.bkg)
|
||||||
local ctl_fg = colors.lightGray
|
local label_fg = style.theme.disabled_fg
|
||||||
local cur_fg_bg = style.lg_white
|
local cur_fg_bg = cpair(style.theme.disabled, s_hi_box.bkg)
|
||||||
local cur_lu = colors.lightGray
|
local cur_lu = style.theme.disabled
|
||||||
|
|
||||||
if i <= facility.num_units then
|
if i <= facility.num_units then
|
||||||
unit = units[i] ---@type ioctl_unit
|
unit = units[i] ---@type ioctl_unit
|
||||||
tag_fg_bg = cpair(colors.black,colors.lightBlue)
|
tag_fg_bg = cpair(colors.black, colors.lightBlue)
|
||||||
lim_fg_bg = bw_fg_bg
|
lim_fg_bg = s_hi_box
|
||||||
ctl_fg = colors.gray
|
label_fg = style.theme.label_fg
|
||||||
cur_fg_bg = blk_brn
|
cur_fg_bg = blk_brn
|
||||||
cur_lu = colors.black
|
cur_lu = colors.black
|
||||||
end
|
end
|
||||||
@@ -185,9 +188,9 @@ local function new_view(root, x, y)
|
|||||||
local unit_tag = Div{parent=limit_div,x=1,y=_y,width=8,height=4,fg_bg=tag_fg_bg}
|
local unit_tag = Div{parent=limit_div,x=1,y=_y,width=8,height=4,fg_bg=tag_fg_bg}
|
||||||
TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Limit",width=7,height=2}
|
TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Limit",width=7,height=2}
|
||||||
|
|
||||||
local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=cpair(ctl_fg,colors.white)}
|
local lim_ctl = Div{parent=limit_div,x=9,y=_y,width=14,height=3,fg_bg=s_hi_box}
|
||||||
local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=lim_fg_bg}
|
local lim = SpinboxNumeric{parent=lim_ctl,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled,fg_bg=lim_fg_bg}
|
||||||
TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,height=1}
|
TextBox{parent=lim_ctl,x=9,y=2,text="mB/t",width=4,height=1,fg_bg=label_fg}
|
||||||
|
|
||||||
local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg}
|
local cur_burn = DataIndicator{parent=limit_div,x=9,y=_y+3,label="",format="%7.1f",value=0,unit="mB/t",commas=false,lu_colors=cpair(cur_lu,cur_lu),width=14,fg_bg=cur_fg_bg}
|
||||||
|
|
||||||
@@ -209,14 +212,14 @@ local function new_view(root, x, y)
|
|||||||
local stat_div = Div{parent=proc,width=22,height=24,x=57,y=6}
|
local stat_div = Div{parent=proc,width=22,height=24,x=57,y=6}
|
||||||
|
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
local tag_fg_bg = gry_wht
|
local tag_fg_bg = cpair(style.theme.disabled, s_hi_box.bkg)
|
||||||
local ind_fg_bg = style.lg_white
|
local ind_fg_bg = cpair(style.theme.disabled, s_hi_box.bkg)
|
||||||
local ind_off = colors.lightGray
|
local ind_off = style.theme.disabled
|
||||||
|
|
||||||
if i <= facility.num_units then
|
if i <= facility.num_units then
|
||||||
tag_fg_bg = cpair(colors.black, colors.cyan)
|
tag_fg_bg = cpair(colors.black, colors.cyan)
|
||||||
ind_fg_bg = bw_fg_bg
|
ind_fg_bg = cpair(style.theme.text, s_hi_box.bkg)
|
||||||
ind_off = colors.gray
|
ind_off = style.ind_hi_box_bg
|
||||||
end
|
end
|
||||||
|
|
||||||
local _y = ((i - 1) * 5) + 1
|
local _y = ((i - 1) * 5) + 1
|
||||||
@@ -225,8 +228,8 @@ local function new_view(root, x, y)
|
|||||||
TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Status",width=7,height=2}
|
TextBox{parent=unit_tag,x=2,y=2,text="Unit "..i.." Status",width=7,height=2}
|
||||||
|
|
||||||
local lights = Div{parent=stat_div,x=9,y=_y,width=14,height=4,fg_bg=ind_fg_bg}
|
local lights = Div{parent=stat_div,x=9,y=_y,width=14,height=4,fg_bg=ind_fg_bg}
|
||||||
local ready = IndicatorLight{parent=lights,x=2,y=2,label="Ready",colors=cpair(colors.green,ind_off)}
|
local ready = IndicatorLight{parent=lights,x=2,y=2,label="Ready",colors=cpair(ind_grn.fgd,ind_off)}
|
||||||
local degraded = IndicatorLight{parent=lights,x=2,y=3,label="Degraded",colors=cpair(colors.red,ind_off),flash=true,period=period.BLINK_250_MS}
|
local degraded = IndicatorLight{parent=lights,x=2,y=3,label="Degraded",colors=cpair(ind_red.fgd,ind_off),flash=true,period=period.BLINK_250_MS}
|
||||||
|
|
||||||
if i <= facility.num_units then
|
if i <= facility.num_units then
|
||||||
local unit = units[i] ---@type ioctl_unit
|
local unit = units[i] ---@type ioctl_unit
|
||||||
@@ -241,18 +244,18 @@ local function new_view(root, x, y)
|
|||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
local ctl_opts = { "Monitored Max Burn", "Combined Burn Rate", "Charge Level", "Generation Rate" }
|
local ctl_opts = { "Monitored Max Burn", "Combined Burn Rate", "Charge Level", "Generation Rate" }
|
||||||
local mode = RadioButton{parent=proc,x=34,y=1,options=ctl_opts,callback=function()end,radio_colors=cpair(colors.gray,colors.white),select_color=colors.purple}
|
local mode = RadioButton{parent=proc,x=34,y=1,options=ctl_opts,callback=function()end,radio_colors=cpair(style.theme.accent_dark,style.theme.accent_light),select_color=colors.purple}
|
||||||
|
|
||||||
mode.register(facility.ps, "process_mode", mode.set_value)
|
mode.register(facility.ps, "process_mode", mode.set_value)
|
||||||
|
|
||||||
local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg}
|
local u_stat = Rectangle{parent=proc,border=border(1,colors.gray,true),thin=true,width=31,height=4,x=1,y=16,fg_bg=bw_fg_bg}
|
||||||
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
local stat_line_1 = TextBox{parent=u_stat,x=1,y=1,text="UNKNOWN",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=bw_fg_bg}
|
||||||
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=gry_wht}
|
local stat_line_2 = TextBox{parent=u_stat,x=1,y=2,text="awaiting data...",width=31,height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value)
|
stat_line_1.register(facility.ps, "status_line_1", stat_line_1.set_value)
|
||||||
stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
|
stat_line_2.register(facility.ps, "status_line_2", stat_line_2.set_value)
|
||||||
|
|
||||||
local auto_controls = Div{parent=proc,x=1,y=20,width=31,height=5,fg_bg=gry_wht}
|
local auto_controls = Div{parent=proc,x=1,y=20,width=31,height=5,fg_bg=s_hi_box}
|
||||||
|
|
||||||
-- save the automatic process control configuration without starting
|
-- save the automatic process control configuration without starting
|
||||||
local function _save_cfg()
|
local function _save_cfg()
|
||||||
@@ -327,40 +330,36 @@ local function new_view(root, x, y)
|
|||||||
|
|
||||||
local waste_sel = Div{parent=proc,width=21,height=24,x=81,y=1}
|
local waste_sel = Div{parent=proc,width=21,height=24,x=81,y=1}
|
||||||
|
|
||||||
TextBox{parent=waste_sel,text=" ",width=21,height=1,x=1,y=1,fg_bg=blk_brn}
|
local cutout_fg_bg = cpair(style.theme.bg, colors.brown)
|
||||||
TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=ALIGN.CENTER,width=21,height=1,x=1,y=2,fg_bg=cpair(colors.lightGray,colors.brown)}
|
|
||||||
|
TextBox{parent=waste_sel,text=" ",width=21,height=1,x=1,y=1,fg_bg=cutout_fg_bg}
|
||||||
|
TextBox{parent=waste_sel,text="WASTE PRODUCTION",alignment=ALIGN.CENTER,width=21,height=1,x=1,y=2,fg_bg=cutout_fg_bg}
|
||||||
|
|
||||||
local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3}
|
local rect = Rectangle{parent=waste_sel,border=border(1,colors.brown,true),width=21,height=22,x=1,y=3}
|
||||||
local status = StateIndicator{parent=rect,x=2,y=1,states=style.waste.states,value=1,min_width=17}
|
local status = StateIndicator{parent=rect,x=2,y=1,states=style.waste.states,value=1,min_width=17}
|
||||||
|
|
||||||
status.register(facility.ps, "current_waste_product", status.update)
|
status.register(facility.ps, "current_waste_product", status.update)
|
||||||
|
|
||||||
local waste_prod = RadioButton{parent=rect,x=2,y=3,options=style.waste.options,callback=process.set_process_waste,radio_colors=cpair(colors.gray,colors.white),select_color=colors.brown}
|
local waste_prod = RadioButton{parent=rect,x=2,y=3,options=style.waste.options,callback=process.set_process_waste,radio_colors=cpair(style.theme.accent_dark,style.theme.accent_light),select_color=colors.brown}
|
||||||
local pu_fallback = Checkbox{parent=rect,x=2,y=7,label="Pu Fallback",callback=process.set_pu_fallback,box_fg_bg=cpair(colors.green,colors.black)}
|
|
||||||
|
|
||||||
waste_prod.register(facility.ps, "process_waste_product", waste_prod.set_value)
|
waste_prod.register(facility.ps, "process_waste_product", waste_prod.set_value)
|
||||||
pu_fallback.register(facility.ps, "process_pu_fallback", pu_fallback.set_value)
|
|
||||||
|
|
||||||
local fb_active = IndicatorLight{parent=rect,x=2,y=9,label="Fallback Active",colors=ind_wht}
|
local fb_active = IndicatorLight{parent=rect,x=2,y=7,label="Fallback Active",colors=ind_wht}
|
||||||
|
local sps_disabled = IndicatorLight{parent=rect,x=2,y=8,label="SPS Disabled LC",colors=ind_yel}
|
||||||
|
|
||||||
fb_active.register(facility.ps, "pu_fallback_active", fb_active.update)
|
fb_active.register(facility.ps, "pu_fallback_active", fb_active.update)
|
||||||
|
sps_disabled.register(facility.ps, "sps_disabled_low_power", sps_disabled.update)
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=11,text="Plutonium Rate",height=1,width=17,fg_bg=style.label}
|
local pu_fallback = Checkbox{parent=rect,x=2,y=10,label="Pu Fallback",callback=process.set_pu_fallback,box_fg_bg=cpair(colors.brown,style.theme.checkbox_bg)}
|
||||||
local pu_rate = DataIndicator{parent=rect,x=2,label="",unit="mB/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17}
|
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=14,text="Polonium Rate",height=1,width=17,fg_bg=style.label}
|
TextBox{parent=rect,x=2,y=12,height=3,text="Switch to Pu when SNAs cannot keep up with waste.",fg_bg=style.label}
|
||||||
local po_rate = DataIndicator{parent=rect,x=2,label="",unit="mB/t",format="%12.2f",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17}
|
|
||||||
|
|
||||||
TextBox{parent=rect,x=2,y=17,text="Antimatter Rate",height=1,width=17,fg_bg=style.label}
|
local lc_sps = Checkbox{parent=rect,x=2,y=16,label="Low Charge SPS",callback=process.set_sps_low_power,box_fg_bg=cpair(colors.brown,style.theme.checkbox_bg)}
|
||||||
local am_rate = DataIndicator{parent=rect,x=2,label="",unit="\xb5B/t",format="%12d",value=0,lu_colors=lu_cpair,fg_bg=bw_fg_bg,width=17}
|
|
||||||
|
|
||||||
pu_rate.register(facility.ps, "pu_rate", pu_rate.update)
|
TextBox{parent=rect,x=2,y=18,height=3,text="Use SPS at low charge, otherwise switches to Po.",fg_bg=style.label}
|
||||||
po_rate.register(facility.ps, "po_rate", po_rate.update)
|
|
||||||
am_rate.register(facility.ps, "am_rate", am_rate.update)
|
|
||||||
|
|
||||||
local sna_count = DataIndicator{parent=rect,x=2,y=20,label="Linked SNAs:",format="%4d",value=0,lu_colors=lu_cpair,width=17}
|
pu_fallback.register(facility.ps, "process_pu_fallback", pu_fallback.set_value)
|
||||||
|
lc_sps.register(facility.ps, "process_sps_low_power", lc_sps.set_value)
|
||||||
sna_count.register(facility.ps, "sna_count", sna_count.update)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_view
|
return new_view
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
|
|
||||||
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
@@ -14,35 +16,37 @@ local StateIndicator = require("graphics.elements.indicators.state")
|
|||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
local border = core.border
|
local border = core.border
|
||||||
|
|
||||||
local text_fg_bg = style.text_colors
|
|
||||||
local lu_col = style.lu_colors
|
|
||||||
|
|
||||||
-- create new reactor view
|
-- create new reactor view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
---@param y integer top left y
|
---@param y integer top left y
|
||||||
---@param ps psil ps interface
|
---@param ps psil ps interface
|
||||||
local function new_view(root, x, y, ps)
|
local function new_view(root, x, y, ps)
|
||||||
local reactor = Rectangle{parent=root,border=border(1, colors.gray, true),width=30,height=7,x=x,y=y}
|
local text_fg = style.theme.text_fg
|
||||||
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local reactor = Rectangle{parent=root,border=border(1,colors.gray,true),width=30,height=7,x=x,y=y}
|
||||||
|
|
||||||
local status = StateIndicator{parent=reactor,x=6,y=1,states=style.reactor.states,value=1,min_width=16}
|
local status = StateIndicator{parent=reactor,x=6,y=1,states=style.reactor.states,value=1,min_width=16}
|
||||||
local core_temp = DataIndicator{parent=reactor,x=2,y=3,lu_colors=lu_col,label="Core Temp:",unit="K",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg}
|
local core_temp = DataIndicator{parent=reactor,x=2,y=3,lu_colors=lu_col,label="Core Temp:",unit=db.temp_label,format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
local burn_r = DataIndicator{parent=reactor,x=2,y=4,lu_colors=lu_col,label="Burn Rate:",unit="mB/t",format="%10.2f",value=0,width=26,fg_bg=text_fg_bg}
|
local burn_r = DataIndicator{parent=reactor,x=2,y=4,lu_colors=lu_col,label="Burn Rate:",unit="mB/t",format="%10.2f",value=0,width=26,fg_bg=text_fg}
|
||||||
local heating_r = DataIndicator{parent=reactor,x=2,y=5,lu_colors=lu_col,label="Heating:",unit="mB/t",format="%12.0f",value=0,commas=true,width=26,fg_bg=text_fg_bg}
|
local heating_r = DataIndicator{parent=reactor,x=2,y=5,lu_colors=lu_col,label="Heating:",unit="mB/t",format="%12.0f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
core_temp.register(ps, "temp", core_temp.update)
|
core_temp.register(ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end)
|
||||||
burn_r.register(ps, "act_burn_rate", burn_r.update)
|
burn_r.register(ps, "act_burn_rate", burn_r.update)
|
||||||
heating_r.register(ps, "heating_rate", heating_r.update)
|
heating_r.register(ps, "heating_rate", heating_r.update)
|
||||||
|
|
||||||
local reactor_fills = Rectangle{parent=root,border=border(1, colors.gray, true),width=24,height=7,x=(x + 29),y=y}
|
local reactor_fills = Rectangle{parent=root,border=border(1, colors.gray, true),width=24,height=7,x=(x + 29),y=y}
|
||||||
|
|
||||||
TextBox{parent=reactor_fills,text="FUEL",x=2,y=1,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=reactor_fills,text="FUEL",x=2,y=1,height=1,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="COOL",x=2,y=2,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=reactor_fills,text="COOL",x=2,y=2,height=1,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="HCOOL",x=2,y=4,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=reactor_fills,text="HCOOL",x=2,y=4,height=1,fg_bg=text_fg}
|
||||||
TextBox{parent=reactor_fills,text="WASTE",x=2,y=5,height=1,fg_bg=text_fg_bg}
|
TextBox{parent=reactor_fills,text="WASTE",x=2,y=5,height=1,fg_bg=text_fg}
|
||||||
|
|
||||||
local fuel = HorizontalBar{parent=reactor_fills,x=8,y=1,show_percent=true,bar_fg_bg=cpair(colors.black,colors.gray),height=1,width=14}
|
local fuel = HorizontalBar{parent=reactor_fills,x=8,y=1,show_percent=true,bar_fg_bg=cpair(style.theme.fuel_color,colors.gray),height=1,width=14}
|
||||||
local ccool = HorizontalBar{parent=reactor_fills,x=8,y=2,show_percent=true,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=14}
|
local ccool = HorizontalBar{parent=reactor_fills,x=8,y=2,show_percent=true,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=14}
|
||||||
local hcool = HorizontalBar{parent=reactor_fills,x=8,y=4,show_percent=true,bar_fg_bg=cpair(colors.white,colors.gray),height=1,width=14}
|
local hcool = HorizontalBar{parent=reactor_fills,x=8,y=4,show_percent=true,bar_fg_bg=cpair(colors.white,colors.gray),height=1,width=14}
|
||||||
local waste = HorizontalBar{parent=reactor_fills,x=8,y=5,show_percent=true,bar_fg_bg=cpair(colors.brown,colors.gray),height=1,width=14}
|
local waste = HorizontalBar{parent=reactor_fills,x=8,y=5,show_percent=true,bar_fg_bg=cpair(colors.brown,colors.gray),height=1,width=14}
|
||||||
|
|||||||
@@ -15,20 +15,20 @@ local VerticalBar = require("graphics.elements.indicators.vbar")
|
|||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
local border = core.border
|
local border = core.border
|
||||||
|
|
||||||
local text_fg_bg = style.text_colors
|
|
||||||
local lu_col = style.lu_colors
|
|
||||||
|
|
||||||
-- new turbine view
|
-- new turbine view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
---@param y integer top left y
|
---@param y integer top left y
|
||||||
---@param ps psil ps interface
|
---@param ps psil ps interface
|
||||||
local function new_view(root, x, y, ps)
|
local function new_view(root, x, y, ps)
|
||||||
|
local text_fg = style.theme.text_fg
|
||||||
|
local lu_col = style.lu_colors
|
||||||
|
|
||||||
local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y}
|
local turbine = Rectangle{parent=root,border=border(1,colors.gray,true),width=23,height=7,x=x,y=y}
|
||||||
|
|
||||||
local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12}
|
local status = StateIndicator{parent=turbine,x=7,y=1,states=style.turbine.states,value=1,min_width=12}
|
||||||
local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg_bg}
|
local prod_rate = PowerIndicator{parent=turbine,x=5,y=3,lu_colors=lu_col,label="",format="%10.2f",value=0,rate=true,width=16,fg_bg=text_fg}
|
||||||
local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg_bg}
|
local flow_rate = DataIndicator{parent=turbine,x=5,y=4,lu_colors=lu_col,label="",unit="mB/t",format="%10.0f",value=0,commas=true,width=16,fg_bg=text_fg}
|
||||||
|
|
||||||
status.register(ps, "computed_status", status.update)
|
status.register(ps, "computed_status", status.update)
|
||||||
prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end)
|
prod_rate.register(ps, "prod_rate", function (val) prod_rate.update(util.joules_to_fe(val)) end)
|
||||||
@@ -37,8 +37,8 @@ local function new_view(root, x, y, ps)
|
|||||||
local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
local steam = VerticalBar{parent=turbine,x=2,y=1,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
||||||
local energy = VerticalBar{parent=turbine,x=3,y=1,fg_bg=cpair(colors.green,colors.gray),height=4,width=1}
|
local energy = VerticalBar{parent=turbine,x=3,y=1,fg_bg=cpair(colors.green,colors.gray),height=4,width=1}
|
||||||
|
|
||||||
TextBox{parent=turbine,text="S",x=2,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=turbine,text="S",x=2,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
TextBox{parent=turbine,text="E",x=3,y=5,height=1,width=1,fg_bg=text_fg_bg}
|
TextBox{parent=turbine,text="E",x=3,y=5,height=1,width=1,fg_bg=text_fg}
|
||||||
|
|
||||||
steam.register(ps, "steam_fill", steam.update)
|
steam.register(ps, "steam_fill", steam.update)
|
||||||
energy.register(ps, "energy_fill", energy.update)
|
energy.register(ps, "energy_fill", energy.update)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
--
|
--
|
||||||
|
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
@@ -34,25 +35,33 @@ local cpair = core.cpair
|
|||||||
local border = core.border
|
local border = core.border
|
||||||
|
|
||||||
local bw_fg_bg = style.bw_fg_bg
|
local bw_fg_bg = style.bw_fg_bg
|
||||||
local lu_cpair = style.lu_colors
|
|
||||||
local hzd_fg_bg = style.hzd_fg_bg
|
|
||||||
local dis_colors = style.dis_colors
|
|
||||||
|
|
||||||
local gry_wht = style.gray_white
|
local gry_wht = style.gray_white
|
||||||
|
|
||||||
local ind_grn = style.ind_grn
|
|
||||||
local ind_yel = style.ind_yel
|
|
||||||
local ind_red = style.ind_red
|
|
||||||
local ind_wht = style.ind_wht
|
|
||||||
|
|
||||||
local period = core.flasher.PERIOD
|
local period = core.flasher.PERIOD
|
||||||
|
|
||||||
-- create a unit view
|
-- create a unit view
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param id integer
|
---@param id integer
|
||||||
local function init(parent, id)
|
local function init(parent, id)
|
||||||
local unit = iocontrol.get_db().units[id] ---@type ioctl_unit
|
local s_hi_box = style.theme.highlight_box
|
||||||
local f_ps = iocontrol.get_db().facility.ps
|
local s_hi_bright = style.theme.highlight_box_bright
|
||||||
|
local s_field = style.theme.field_box
|
||||||
|
|
||||||
|
local hc_text = style.hc_text
|
||||||
|
local lu_cpair = style.lu_colors
|
||||||
|
local hzd_fg_bg = style.hzd_fg_bg
|
||||||
|
local dis_colors = style.dis_colors
|
||||||
|
local arrow_fg_bg = cpair(style.theme.label, s_hi_box.bkg)
|
||||||
|
|
||||||
|
local ind_bkg = style.ind_bkg
|
||||||
|
local ind_grn = style.ind_grn
|
||||||
|
local ind_yel = style.ind_yel
|
||||||
|
local ind_red = style.ind_red
|
||||||
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
local unit = db.units[id] ---@type ioctl_unit
|
||||||
|
local f_ps = db.facility.ps
|
||||||
|
|
||||||
local main = Div{parent=parent,x=1,y=1}
|
local main = Div{parent=parent,x=1,y=1}
|
||||||
|
|
||||||
@@ -62,7 +71,7 @@ local function init(parent, id)
|
|||||||
local b_ps = unit.boiler_ps_tbl
|
local b_ps = unit.boiler_ps_tbl
|
||||||
local t_ps = unit.turbine_ps_tbl
|
local t_ps = unit.turbine_ps_tbl
|
||||||
|
|
||||||
TextBox{parent=main,text="Reactor Unit #" .. id,alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=main,text="Reactor Unit #" .. id,alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
|
|
||||||
-----------------------------
|
-----------------------------
|
||||||
-- main stats and core map --
|
-- main stats and core map --
|
||||||
@@ -73,11 +82,11 @@ local function init(parent, id)
|
|||||||
core_map.register(u_ps, "size", function (s) core_map.resize(s[1], s[2]) end)
|
core_map.register(u_ps, "size", function (s) core_map.resize(s[1], s[2]) end)
|
||||||
|
|
||||||
TextBox{parent=main,x=12,y=22,text="Heating Rate",height=1,width=12,fg_bg=style.label}
|
TextBox{parent=main,x=12,y=22,text="Heating Rate",height=1,width=12,fg_bg=style.label}
|
||||||
local heating_r = DataIndicator{parent=main,x=12,label="",format="%14.0f",value=0,unit="mB/t",commas=true,lu_colors=lu_cpair,width=19,fg_bg=bw_fg_bg}
|
local heating_r = DataIndicator{parent=main,x=12,label="",format="%14.0f",value=0,unit="mB/t",commas=true,lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
||||||
heating_r.register(u_ps, "heating_rate", heating_r.update)
|
heating_r.register(u_ps, "heating_rate", heating_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=12,y=25,text="Commanded Burn Rate",height=1,width=19,fg_bg=style.label}
|
TextBox{parent=main,x=12,y=25,text="Commanded Burn Rate",height=1,width=19,fg_bg=style.label}
|
||||||
local burn_r = DataIndicator{parent=main,x=12,label="",format="%14.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=19,fg_bg=bw_fg_bg}
|
local burn_r = DataIndicator{parent=main,x=12,label="",format="%14.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=19,fg_bg=s_field}
|
||||||
burn_r.register(u_ps, "burn_rate", burn_r.update)
|
burn_r.register(u_ps, "burn_rate", burn_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,text="F",x=2,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="F",x=2,y=22,width=1,height=1,fg_bg=style.label}
|
||||||
@@ -87,7 +96,7 @@ local function init(parent, id)
|
|||||||
TextBox{parent=main,text="H",x=8,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="H",x=8,y=22,width=1,height=1,fg_bg=style.label}
|
||||||
TextBox{parent=main,text="W",x=10,y=22,width=1,height=1,fg_bg=style.label}
|
TextBox{parent=main,text="W",x=10,y=22,width=1,height=1,fg_bg=style.label}
|
||||||
|
|
||||||
local fuel = VerticalBar{parent=main,x=2,y=23,fg_bg=cpair(colors.black,colors.gray),height=4,width=1}
|
local fuel = VerticalBar{parent=main,x=2,y=23,fg_bg=cpair(style.theme.fuel_color,colors.gray),height=4,width=1}
|
||||||
local ccool = VerticalBar{parent=main,x=4,y=23,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
local ccool = VerticalBar{parent=main,x=4,y=23,fg_bg=cpair(colors.blue,colors.gray),height=4,width=1}
|
||||||
local hcool = VerticalBar{parent=main,x=8,y=23,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
local hcool = VerticalBar{parent=main,x=8,y=23,fg_bg=cpair(colors.white,colors.gray),height=4,width=1}
|
||||||
local waste = VerticalBar{parent=main,x=10,y=23,fg_bg=cpair(colors.brown,colors.gray),height=4,width=1}
|
local waste = VerticalBar{parent=main,x=10,y=23,fg_bg=cpair(colors.brown,colors.gray),height=4,width=1}
|
||||||
@@ -114,19 +123,20 @@ local function init(parent, id)
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=22,text="Core Temp",height=1,width=9,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=22,text="Core Temp",height=1,width=9,fg_bg=style.label}
|
||||||
local core_temp = DataIndicator{parent=main,x=32,label="",format="%11.2f",value=0,unit="K",lu_colors=lu_cpair,width=13,fg_bg=bw_fg_bg}
|
local fmt = util.trinary(string.len(db.temp_label) == 2, "%10.2f", "%11.2f")
|
||||||
core_temp.register(u_ps, "temp", core_temp.update)
|
local core_temp = DataIndicator{parent=main,x=32,label="",format=fmt,value=0,commas=true,unit=db.temp_label,lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
|
core_temp.register(u_ps, "temp", function (t) core_temp.update(db.temp_convert(t)) end)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=25,text="Burn Rate",height=1,width=9,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=25,text="Burn Rate",height=1,width=9,fg_bg=style.label}
|
||||||
local act_burn_r = DataIndicator{parent=main,x=32,label="",format="%8.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=13,fg_bg=bw_fg_bg}
|
local act_burn_r = DataIndicator{parent=main,x=32,label="",format="%8.2f",value=0,unit="mB/t",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
act_burn_r.register(u_ps, "act_burn_rate", act_burn_r.update)
|
act_burn_r.register(u_ps, "act_burn_rate", act_burn_r.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=28,text="Damage",height=1,width=6,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=28,text="Damage",height=1,width=6,fg_bg=style.label}
|
||||||
local damage_p = DataIndicator{parent=main,x=32,label="",format="%11.0f",value=0,unit="%",lu_colors=lu_cpair,width=13,fg_bg=bw_fg_bg}
|
local damage_p = DataIndicator{parent=main,x=32,label="",format="%11.0f",value=0,unit="%",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
damage_p.register(u_ps, "damage", damage_p.update)
|
damage_p.register(u_ps, "damage", damage_p.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=32,y=31,text="Radiation",height=1,width=21,fg_bg=style.label}
|
TextBox{parent=main,x=32,y=31,text="Radiation",height=1,width=21,fg_bg=style.label}
|
||||||
local radiation = RadIndicator{parent=main,x=32,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=bw_fg_bg}
|
local radiation = RadIndicator{parent=main,x=32,label="",format="%9.3f",lu_colors=lu_cpair,width=13,fg_bg=s_field}
|
||||||
radiation.register(u_ps, "radiation", radiation.update)
|
radiation.register(u_ps, "radiation", radiation.update)
|
||||||
|
|
||||||
-------------------
|
-------------------
|
||||||
@@ -149,9 +159,9 @@ local function init(parent, id)
|
|||||||
local annunciator = Div{parent=main,width=23,height=18,x=22,y=3}
|
local annunciator = Div{parent=main,width=23,height=18,x=22,y=3}
|
||||||
|
|
||||||
-- connectivity
|
-- connectivity
|
||||||
local plc_online = IndicatorLight{parent=annunciator,label="PLC Online",colors=cpair(colors.green,colors.red)}
|
local plc_online = IndicatorLight{parent=annunciator,label="PLC Online",colors=cpair(ind_grn.fgd,ind_red.fgd)}
|
||||||
local plc_hbeat = IndicatorLight{parent=annunciator,label="PLC Heartbeat",colors=ind_wht}
|
local plc_hbeat = IndicatorLight{parent=annunciator,label="PLC Heartbeat",colors=ind_wht}
|
||||||
local rad_mon = TriIndicatorLight{parent=annunciator,label="Radiation Monitor",c1=colors.gray,c2=colors.yellow,c3=colors.green}
|
local rad_mon = TriIndicatorLight{parent=annunciator,label="Radiation Monitor",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd}
|
||||||
|
|
||||||
plc_online.register(u_ps, "PLCOnline", plc_online.update)
|
plc_online.register(u_ps, "PLCOnline", plc_online.update)
|
||||||
plc_hbeat.register(u_ps, "PLCHeartbeat", plc_hbeat.update)
|
plc_hbeat.register(u_ps, "PLCHeartbeat", plc_hbeat.update)
|
||||||
@@ -173,7 +183,7 @@ local function init(parent, id)
|
|||||||
local rad_wrn = IndicatorLight{parent=annunciator,label="Radiation Warning",colors=ind_yel}
|
local rad_wrn = IndicatorLight{parent=annunciator,label="Radiation Warning",colors=ind_yel}
|
||||||
local r_rtrip = IndicatorLight{parent=annunciator,label="RCP Trip",colors=ind_red}
|
local r_rtrip = IndicatorLight{parent=annunciator,label="RCP Trip",colors=ind_red}
|
||||||
local r_cflow = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=ind_yel}
|
local r_cflow = IndicatorLight{parent=annunciator,label="RCS Flow Low",colors=ind_yel}
|
||||||
local r_clow = IndicatorLight{parent=annunciator,label="Coolant Level Low",colors=ind_yel}
|
local r_clow = IndicatorLight{parent=annunciator,label="Coolant Level Low",colors=ind_yel}
|
||||||
local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=ind_red}
|
local r_temp = IndicatorLight{parent=annunciator,label="Reactor Temp. High",colors=ind_red}
|
||||||
local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=ind_yel}
|
local r_rhdt = IndicatorLight{parent=annunciator,label="Reactor High Delta T",colors=ind_yel}
|
||||||
local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=ind_yel}
|
local r_firl = IndicatorLight{parent=annunciator,label="Fuel Input Rate Low",colors=ind_yel}
|
||||||
@@ -208,7 +218,7 @@ local function init(parent, id)
|
|||||||
local rps_loc = IndicatorLight{parent=rps_annunc,label="Coolant Level Low Low",colors=ind_yel}
|
local rps_loc = IndicatorLight{parent=rps_annunc,label="Coolant Level Low Low",colors=ind_yel}
|
||||||
local rps_flt = IndicatorLight{parent=rps_annunc,label="PPM Fault",colors=ind_yel,flash=true,period=period.BLINK_500_MS}
|
local rps_flt = IndicatorLight{parent=rps_annunc,label="PPM Fault",colors=ind_yel,flash=true,period=period.BLINK_500_MS}
|
||||||
local rps_tmo = IndicatorLight{parent=rps_annunc,label="Connection Timeout",colors=ind_yel,flash=true,period=period.BLINK_500_MS}
|
local rps_tmo = IndicatorLight{parent=rps_annunc,label="Connection Timeout",colors=ind_yel,flash=true,period=period.BLINK_500_MS}
|
||||||
local rps_sfl = IndicatorLight{parent=rps_annunc,label="System Failure",colors=cpair(colors.orange,colors.gray),flash=true,period=period.BLINK_500_MS}
|
local rps_sfl = IndicatorLight{parent=rps_annunc,label="System Failure",colors=ind_red,flash=true,period=period.BLINK_500_MS}
|
||||||
|
|
||||||
rps_trp.register(u_ps, "rps_tripped", rps_trp.update)
|
rps_trp.register(u_ps, "rps_tripped", rps_trp.update)
|
||||||
rps_dmg.register(u_ps, "high_dmg", rps_dmg.update)
|
rps_dmg.register(u_ps, "high_dmg", rps_dmg.update)
|
||||||
@@ -229,7 +239,7 @@ local function init(parent, id)
|
|||||||
local rcs_tags = Div{parent=rcs,width=2,height=16,x=1,y=7}
|
local rcs_tags = Div{parent=rcs,width=2,height=16,x=1,y=7}
|
||||||
|
|
||||||
local c_flt = IndicatorLight{parent=rcs_annunc,label="RCS Hardware Fault",colors=ind_yel}
|
local c_flt = IndicatorLight{parent=rcs_annunc,label="RCS Hardware Fault",colors=ind_yel}
|
||||||
local c_emg = TriIndicatorLight{parent=rcs_annunc,label="Emergency Coolant",c1=colors.gray,c2=colors.white,c3=colors.green}
|
local c_emg = TriIndicatorLight{parent=rcs_annunc,label="Emergency Coolant",c1=ind_bkg,c2=ind_wht.fgd,c3=ind_grn.fgd}
|
||||||
local c_cfm = IndicatorLight{parent=rcs_annunc,label="Coolant Feed Mismatch",colors=ind_yel}
|
local c_cfm = IndicatorLight{parent=rcs_annunc,label="Coolant Feed Mismatch",colors=ind_yel}
|
||||||
local c_brm = IndicatorLight{parent=rcs_annunc,label="Boil Rate Mismatch",colors=ind_yel}
|
local c_brm = IndicatorLight{parent=rcs_annunc,label="Boil Rate Mismatch",colors=ind_yel}
|
||||||
local c_sfm = IndicatorLight{parent=rcs_annunc,label="Steam Feed Mismatch",colors=ind_yel}
|
local c_sfm = IndicatorLight{parent=rcs_annunc,label="Steam Feed Mismatch",colors=ind_yel}
|
||||||
@@ -252,14 +262,14 @@ local function init(parent, id)
|
|||||||
|
|
||||||
-- boiler annunciator panel(s)
|
-- boiler annunciator panel(s)
|
||||||
|
|
||||||
if available_space > 0 then _add_space() end
|
|
||||||
|
|
||||||
if unit.num_boilers > 0 then
|
if unit.num_boilers > 0 then
|
||||||
TextBox{parent=rcs_tags,x=1,text="B1",width=2,height=1,fg_bg=bw_fg_bg}
|
if available_space > 0 then _add_space() end
|
||||||
|
|
||||||
|
TextBox{parent=rcs_tags,x=1,text="B1",width=2,height=1,fg_bg=hc_text}
|
||||||
local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
local b1_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
||||||
b1_wll.register(b_ps[1], "WaterLevelLow", b1_wll.update)
|
b1_wll.register(b_ps[1], "WaterLevelLow", b1_wll.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B1",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="B1",width=2,height=1,fg_bg=hc_text}
|
||||||
local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
local b1_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
||||||
b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update)
|
b1_hr.register(b_ps[1], "HeatingRateLow", b1_hr.update)
|
||||||
end
|
end
|
||||||
@@ -271,11 +281,11 @@ local function init(parent, id)
|
|||||||
_add_space()
|
_add_space()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=hc_text}
|
||||||
local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
local b2_wll = IndicatorLight{parent=rcs_annunc,label="Water Level Low",colors=ind_red}
|
||||||
b2_wll.register(b_ps[2], "WaterLevelLow", b2_wll.update)
|
b2_wll.register(b_ps[2], "WaterLevelLow", b2_wll.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="B2",width=2,height=1,fg_bg=hc_text}
|
||||||
local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
local b2_hr = IndicatorLight{parent=rcs_annunc,label="Heating Rate Low",colors=ind_yel}
|
||||||
b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update)
|
b2_hr.register(b_ps[2], "HeatingRateLow", b2_hr.update)
|
||||||
end
|
end
|
||||||
@@ -284,19 +294,19 @@ local function init(parent, id)
|
|||||||
|
|
||||||
if available_space > 1 then _add_space() end
|
if available_space > 1 then _add_space() end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
||||||
local t1_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=colors.gray,c2=colors.yellow,c3=colors.red}
|
local t1_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t1_sdo.register(t_ps[1], "SteamDumpOpen", t1_sdo.update)
|
t1_sdo.register(t_ps[1], "SteamDumpOpen", t1_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
||||||
local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t1_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t1_tos.register(t_ps[1], "TurbineOverSpeed", t1_tos.update)
|
t1_tos.register(t_ps[1], "TurbineOverSpeed", t1_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
||||||
local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t1_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t1_gtrp.register(t_ps[1], "GeneratorTrip", t1_gtrp.update)
|
t1_gtrp.register(t_ps[1], "GeneratorTrip", t1_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T1",width=2,height=1,fg_bg=hc_text}
|
||||||
local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t1_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update)
|
t1_trp.register(t_ps[1], "TurbineTrip", t1_trp.update)
|
||||||
|
|
||||||
@@ -305,19 +315,19 @@ local function init(parent, id)
|
|||||||
_add_space()
|
_add_space()
|
||||||
end
|
end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
||||||
local t2_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=colors.gray,c2=colors.yellow,c3=colors.red}
|
local t2_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t2_sdo.register(t_ps[2], "SteamDumpOpen", t2_sdo.update)
|
t2_sdo.register(t_ps[2], "SteamDumpOpen", t2_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
||||||
local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t2_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t2_tos.register(t_ps[2], "TurbineOverSpeed", t2_tos.update)
|
t2_tos.register(t_ps[2], "TurbineOverSpeed", t2_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
||||||
local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t2_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t2_gtrp.register(t_ps[2], "GeneratorTrip", t2_gtrp.update)
|
t2_gtrp.register(t_ps[2], "GeneratorTrip", t2_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T2",width=2,height=1,fg_bg=hc_text}
|
||||||
local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t2_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update)
|
t2_trp.register(t_ps[2], "TurbineTrip", t2_trp.update)
|
||||||
end
|
end
|
||||||
@@ -325,30 +335,32 @@ local function init(parent, id)
|
|||||||
if unit.num_turbines > 2 then
|
if unit.num_turbines > 2 then
|
||||||
if available_space > 3 then _add_space() end
|
if available_space > 3 then _add_space() end
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
||||||
local t3_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=colors.gray,c2=colors.yellow,c3=colors.red}
|
local t3_sdo = TriIndicatorLight{parent=rcs_annunc,label="Steam Relief Valve Open",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_red.fgd}
|
||||||
t3_sdo.register(t_ps[3], "SteamDumpOpen", t3_sdo.update)
|
t3_sdo.register(t_ps[3], "SteamDumpOpen", t3_sdo.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
||||||
local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
local t3_tos = IndicatorLight{parent=rcs_annunc,label="Turbine Over Speed",colors=ind_red}
|
||||||
t3_tos.register(t_ps[3], "TurbineOverSpeed", t3_tos.update)
|
t3_tos.register(t_ps[3], "TurbineOverSpeed", t3_tos.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
||||||
local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
local t3_gtrp = IndicatorLight{parent=rcs_annunc,label="Generator Trip",colors=ind_yel,flash=true,period=period.BLINK_250_MS}
|
||||||
t3_gtrp.register(t_ps[3], "GeneratorTrip", t3_gtrp.update)
|
t3_gtrp.register(t_ps[3], "GeneratorTrip", t3_gtrp.update)
|
||||||
|
|
||||||
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=bw_fg_bg}
|
TextBox{parent=rcs_tags,text="T3",width=2,height=1,fg_bg=hc_text}
|
||||||
local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
local t3_trp = IndicatorLight{parent=rcs_annunc,label="Turbine Trip",colors=ind_red,flash=true,period=period.BLINK_250_MS}
|
||||||
t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update)
|
t3_trp.register(t_ps[3], "TurbineTrip", t3_trp.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
-- reactor controls --
|
-- reactor controls --
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
local burn_control = Div{parent=main,x=12,y=28,width=19,height=3,fg_bg=gry_wht}
|
local burn_control = Div{parent=main,x=12,y=28,width=19,height=3,fg_bg=s_hi_box}
|
||||||
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=gry_wht,fg_bg=bw_fg_bg}
|
local burn_rate = SpinboxNumeric{parent=burn_control,x=2,y=1,whole_num_precision=4,fractional_precision=1,min=0.1,arrow_fg_bg=arrow_fg_bg,arrow_disable=style.theme.disabled}
|
||||||
TextBox{parent=burn_control,x=9,y=2,text="mB/t"}
|
TextBox{parent=burn_control,x=9,y=2,text="mB/t",fg_bg=style.theme.label_fg}
|
||||||
|
|
||||||
local set_burn = function () unit.set_burn(burn_rate.get_value()) end
|
local set_burn = function () unit.set_burn(burn_rate.get_value()) end
|
||||||
local set_burn_btn = PushButton{parent=burn_control,x=14,y=2,text="SET",min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=dis_colors,callback=set_burn}
|
local set_burn_btn = PushButton{parent=burn_control,x=14,y=2,text="SET",min_width=5,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=style.wh_gray,dis_fg_bg=dis_colors,callback=set_burn}
|
||||||
@@ -394,22 +406,22 @@ local function init(parent, id)
|
|||||||
-- alarm management --
|
-- alarm management --
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
local alarm_panel = Div{parent=main,x=2,y=36,width=29,height=16,fg_bg=bw_fg_bg}
|
local alarm_panel = Div{parent=main,x=2,y=36,width=29,height=16,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
local a_brc = AlarmLight{parent=alarm_panel,x=6,y=2,label="Containment Breach",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_brc = AlarmLight{parent=alarm_panel,x=6,y=2,label="Containment Breach",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_rad = AlarmLight{parent=alarm_panel,x=6,label="Containment Radiation",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_rad = AlarmLight{parent=alarm_panel,x=6,label="Containment Radiation",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_dmg = AlarmLight{parent=alarm_panel,x=6,label="Critical Damage",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_dmg = AlarmLight{parent=alarm_panel,x=6,label="Critical Damage",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
alarm_panel.line_break()
|
alarm_panel.line_break()
|
||||||
local a_rcl = AlarmLight{parent=alarm_panel,x=6,label="Reactor Lost",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_rcl = AlarmLight{parent=alarm_panel,x=6,label="Reactor Lost",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_rcd = AlarmLight{parent=alarm_panel,x=6,label="Reactor Damage",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_rcd = AlarmLight{parent=alarm_panel,x=6,label="Reactor Damage",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_rot = AlarmLight{parent=alarm_panel,x=6,label="Reactor Over Temp",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_rot = AlarmLight{parent=alarm_panel,x=6,label="Reactor Over Temp",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_rht = AlarmLight{parent=alarm_panel,x=6,label="Reactor High Temp",c1=colors.gray,c2=colors.yellow,c3=colors.green,flash=true,period=period.BLINK_500_MS}
|
local a_rht = AlarmLight{parent=alarm_panel,x=6,label="Reactor High Temp",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_500_MS}
|
||||||
local a_rwl = AlarmLight{parent=alarm_panel,x=6,label="Reactor Waste Leak",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_rwl = AlarmLight{parent=alarm_panel,x=6,label="Reactor Waste Leak",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
local a_rwh = AlarmLight{parent=alarm_panel,x=6,label="Reactor Waste High",c1=colors.gray,c2=colors.yellow,c3=colors.green,flash=true,period=period.BLINK_500_MS}
|
local a_rwh = AlarmLight{parent=alarm_panel,x=6,label="Reactor Waste High",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_500_MS}
|
||||||
alarm_panel.line_break()
|
alarm_panel.line_break()
|
||||||
local a_rps = AlarmLight{parent=alarm_panel,x=6,label="RPS Transient",c1=colors.gray,c2=colors.yellow,c3=colors.green,flash=true,period=period.BLINK_500_MS}
|
local a_rps = AlarmLight{parent=alarm_panel,x=6,label="RPS Transient",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_500_MS}
|
||||||
local a_clt = AlarmLight{parent=alarm_panel,x=6,label="RCS Transient",c1=colors.gray,c2=colors.yellow,c3=colors.green,flash=true,period=period.BLINK_500_MS}
|
local a_clt = AlarmLight{parent=alarm_panel,x=6,label="RCS Transient",c1=ind_bkg,c2=ind_yel.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_500_MS}
|
||||||
local a_tbt = AlarmLight{parent=alarm_panel,x=6,label="Turbine Trip",c1=colors.gray,c2=colors.red,c3=colors.green,flash=true,period=period.BLINK_250_MS}
|
local a_tbt = AlarmLight{parent=alarm_panel,x=6,label="Turbine Trip",c1=ind_bkg,c2=ind_red.fgd,c3=ind_grn.fgd,flash=true,period=period.BLINK_250_MS}
|
||||||
|
|
||||||
a_brc.register(u_ps, "Alarm_1", a_brc.update)
|
a_brc.register(u_ps, "Alarm_1", a_brc.update)
|
||||||
a_rad.register(u_ps, "Alarm_2", a_rad.update)
|
a_rad.register(u_ps, "Alarm_2", a_rad.update)
|
||||||
@@ -462,9 +474,9 @@ local function init(parent, id)
|
|||||||
|
|
||||||
-- color tags
|
-- color tags
|
||||||
|
|
||||||
TextBox{parent=alarm_panel,x=5,y=13,text="\x95",width=1,height=1,fg_bg=cpair(colors.white,colors.cyan)}
|
TextBox{parent=alarm_panel,x=5,y=13,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.cyan)}
|
||||||
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(colors.white,colors.blue)}
|
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
||||||
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(colors.white,colors.blue)}
|
TextBox{parent=alarm_panel,x=5,text="\x95",width=1,height=1,fg_bg=cpair(s_hi_bright.bkg,colors.blue)}
|
||||||
|
|
||||||
--------------------------------
|
--------------------------------
|
||||||
-- automatic control settings --
|
-- automatic control settings --
|
||||||
@@ -476,7 +488,7 @@ local function init(parent, id)
|
|||||||
|
|
||||||
local ctl_opts = { "Manual", "Primary", "Secondary", "Tertiary", "Backup" }
|
local ctl_opts = { "Manual", "Primary", "Secondary", "Tertiary", "Backup" }
|
||||||
|
|
||||||
local group = RadioButton{parent=auto_div,options=ctl_opts,callback=function()end,radio_colors=cpair(colors.gray,colors.white),select_color=colors.purple}
|
local group = RadioButton{parent=auto_div,options=ctl_opts,callback=function()end,radio_colors=cpair(style.theme.accent_dark,style.theme.accent_light),select_color=colors.purple}
|
||||||
|
|
||||||
group.register(u_ps, "auto_group_id", function (gid) group.set_value(gid + 1) end)
|
group.register(u_ps, "auto_group_id", function (gid) group.set_value(gid + 1) end)
|
||||||
|
|
||||||
@@ -488,7 +500,7 @@ local function init(parent, id)
|
|||||||
auto_div.line_break()
|
auto_div.line_break()
|
||||||
|
|
||||||
TextBox{parent=auto_div,text="Prio. Group",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=auto_div,text="Prio. Group",height=1,width=11,fg_bg=style.label}
|
||||||
local auto_grp = TextBox{parent=auto_div,text="Manual",height=1,width=11,fg_bg=bw_fg_bg}
|
local auto_grp = TextBox{parent=auto_div,text="Manual",height=1,width=11,fg_bg=s_field}
|
||||||
|
|
||||||
auto_grp.register(u_ps, "auto_group", auto_grp.set_value)
|
auto_grp.register(u_ps, "auto_group", auto_grp.set_value)
|
||||||
|
|
||||||
|
|||||||
@@ -24,17 +24,12 @@ local ALIGN = core.ALIGN
|
|||||||
local sprintf = util.sprintf
|
local sprintf = util.sprintf
|
||||||
|
|
||||||
local border = core.border
|
local border = core.border
|
||||||
|
local cpair = core.cpair
|
||||||
local pipe = core.pipe
|
local pipe = core.pipe
|
||||||
|
|
||||||
local wh_gray = style.wh_gray
|
local wh_gray = style.wh_gray
|
||||||
local bw_fg_bg = style.bw_fg_bg
|
|
||||||
local text_c = style.text_colors
|
|
||||||
local lu_c = style.lu_colors
|
|
||||||
local lg_gray = style.lg_gray
|
local lg_gray = style.lg_gray
|
||||||
|
|
||||||
local ind_grn = style.ind_grn
|
|
||||||
local ind_wht = style.ind_wht
|
|
||||||
|
|
||||||
-- make a new unit flow window
|
-- make a new unit flow window
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param x integer top left x
|
---@param x integer top left x
|
||||||
@@ -42,6 +37,15 @@ local ind_wht = style.ind_wht
|
|||||||
---@param wide boolean whether to render wide version
|
---@param wide boolean whether to render wide version
|
||||||
---@param unit ioctl_unit unit database entry
|
---@param unit ioctl_unit unit database entry
|
||||||
local function make(parent, x, y, wide, unit)
|
local function make(parent, x, y, wide, unit)
|
||||||
|
local s_field = style.theme.field_box
|
||||||
|
|
||||||
|
local text_c = style.text_colors
|
||||||
|
local lu_c = style.lu_colors
|
||||||
|
local lu_c_d = style.lu_colors_dark
|
||||||
|
|
||||||
|
local ind_grn = style.ind_grn
|
||||||
|
local ind_wht = style.ind_wht
|
||||||
|
|
||||||
local height = 16
|
local height = 16
|
||||||
|
|
||||||
local v_start = 1 + ((unit.unit_id - 1) * 5)
|
local v_start = 1 + ((unit.unit_id - 1) * 5)
|
||||||
@@ -99,35 +103,35 @@ local function make(parent, x, y, wide, unit)
|
|||||||
table.insert(rc_pipes, pipe(_wide(92, 78), py, _wide(104, 83), py, colors.white, true))
|
table.insert(rc_pipes, pipe(_wide(92, 78), py, _wide(104, 83), py, colors.white, true))
|
||||||
end
|
end
|
||||||
|
|
||||||
PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=colors.lightGray}
|
PipeNetwork{parent=root,x=20,y=1,pipes=rc_pipes,bg=style.theme.bg}
|
||||||
|
|
||||||
if unit.num_boilers > 0 then
|
if unit.num_boilers > 0 then
|
||||||
local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local cc_rate = DataIndicator{parent=root,x=_wide(25,22),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local hc_rate = DataIndicator{parent=root,x=_wide(25,22),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
|
|
||||||
cc_rate.register(unit.unit_ps, "boiler_boil_sum", function (sum) cc_rate.update(sum * 10) end)
|
cc_rate.register(unit.unit_ps, "boiler_boil_sum", function (sum) cc_rate.update(sum * 10) end)
|
||||||
hc_rate.register(unit.unit_ps, "heating_rate", hc_rate.update)
|
hc_rate.register(unit.unit_ps, "heating_rate", hc_rate.update)
|
||||||
|
|
||||||
local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray}
|
local boiler = Rectangle{parent=root,x=_wide(47,40),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
||||||
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=boiler,y=1,text="THERMO-ELECTRIC",alignment=ALIGN.CENTER,height=1}
|
||||||
TextBox{parent=boiler,y=3,text=util.trinary(unit.num_boilers>1,"BOILERS","BOILER"),alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=boiler,y=3,text=util.trinary(unit.num_boilers>1,"BOILERS","BOILER"),alignment=ALIGN.CENTER,height=1}
|
||||||
TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(47,40),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(65,58),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
|
|
||||||
local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local wt_rate = DataIndicator{parent=root,x=_wide(71,61),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local st_rate = DataIndicator{parent=root,x=_wide(71,61),y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
|
|
||||||
wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update)
|
wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update)
|
||||||
st_rate.register(unit.unit_ps, "boiler_boil_sum", st_rate.update)
|
st_rate.register(unit.unit_ps, "boiler_boil_sum", st_rate.update)
|
||||||
else
|
else
|
||||||
local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local wt_rate = DataIndicator{parent=root,x=28,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=bw_fg_bg}
|
local st_rate = DataIndicator{parent=root,x=28,y=5,lu_colors=lu_c,label="",unit="mB/t",format="%11.0f",value=0,commas=true,width=16,fg_bg=s_field}
|
||||||
|
|
||||||
wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update)
|
wt_rate.register(unit.unit_ps, "turbine_flow_sum", wt_rate.update)
|
||||||
st_rate.register(unit.unit_ps, "heating_rate", st_rate.update)
|
st_rate.register(unit.unit_ps, "heating_rate", st_rate.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1, colors.gray, true),width=19,height=5,fg_bg=wh_gray}
|
local turbine = Rectangle{parent=root,x=_wide(93,79),y=1,border=border(1,colors.gray,true),width=19,height=5,fg_bg=wh_gray}
|
||||||
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=turbine,y=1,text="STEAM TURBINE",alignment=ALIGN.CENTER,height=1}
|
||||||
TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=ALIGN.CENTER,height=1}
|
TextBox{parent=turbine,y=3,text=util.trinary(unit.num_turbines>1,"GENERATORS","GENERATOR"),alignment=ALIGN.CENTER,height=1}
|
||||||
TextBox{parent=root,x=_wide(93,79),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
TextBox{parent=root,x=_wide(93,79),y=2,text="\x1b \x80 \x1a",width=1,height=3,fg_bg=lg_gray}
|
||||||
@@ -135,7 +139,7 @@ local function make(parent, x, y, wide, unit)
|
|||||||
for i = 1, unit.num_turbines do
|
for i = 1, unit.num_turbines do
|
||||||
local ry = 1 + (2 * (i - 1)) + prv_yo
|
local ry = 1 + (2 * (i - 1)) + prv_yo
|
||||||
TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1}
|
TextBox{parent=root,x=_wide(125,103),y=ry,text="\x10\x11\x7f",fg_bg=text_c,width=3,height=1}
|
||||||
local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=colors.gray,c2=colors.yellow,c3=colors.red}
|
local state = TriIndicatorLight{parent=root,x=_wide(129,107),y=ry,label=v_names[i+4],c1=style.ind_bkg,c2=style.ind_yel.fgd,c3=style.ind_red.fgd}
|
||||||
state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update)
|
state.register(unit.turbine_ps_tbl[i], "SteamDumpOpen", state.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -145,6 +149,8 @@ local function make(parent, x, y, wide, unit)
|
|||||||
|
|
||||||
local waste = Div{parent=root,x=3,y=6}
|
local waste = Div{parent=root,x=3,y=6}
|
||||||
|
|
||||||
|
local waste_c = style.theme.fuel_color
|
||||||
|
|
||||||
local waste_pipes = {
|
local waste_pipes = {
|
||||||
pipe(0, 0, _wide(19, 16), 1, colors.brown, true),
|
pipe(0, 0, _wide(19, 16), 1, colors.brown, true),
|
||||||
pipe(_wide(14, 13), 1, _wide(19, 17), 5, colors.brown, true),
|
pipe(_wide(14, 13), 1, _wide(19, 17), 5, colors.brown, true),
|
||||||
@@ -158,12 +164,12 @@ local function make(parent, x, y, wide, unit)
|
|||||||
pipe(_wide(74, 63), 4, _wide(95, 81), 4, colors.cyan, true),
|
pipe(_wide(74, 63), 4, _wide(95, 81), 4, colors.cyan, true),
|
||||||
pipe(_wide(74, 63), 8, _wide(133, 111), 8, colors.cyan, true),
|
pipe(_wide(74, 63), 8, _wide(133, 111), 8, colors.cyan, true),
|
||||||
|
|
||||||
pipe(_wide(108, 94), 1, _wide(132, 110), 6, colors.black, true, true),
|
pipe(_wide(108, 94), 1, _wide(132, 110), 6, waste_c, true, true),
|
||||||
pipe(_wide(108, 94), 4, _wide(111, 95), 1, colors.black, true, true),
|
pipe(_wide(108, 94), 4, _wide(111, 95), 1, waste_c, true, true),
|
||||||
pipe(_wide(132, 110), 6, _wide(130, 108), 6, colors.black, true, true)
|
pipe(_wide(132, 110), 6, _wide(130, 108), 6, waste_c, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=colors.lightGray}
|
PipeNetwork{parent=waste,x=1,y=1,pipes=waste_pipes,bg=style.theme.bg}
|
||||||
|
|
||||||
local function _valve(vx, vy, n)
|
local function _valve(vx, vy, n)
|
||||||
TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1}
|
TextBox{parent=waste,x=vx,y=vy,text="\x10\x11",fg_bg=text_c,width=2,height=1}
|
||||||
@@ -175,16 +181,16 @@ local function make(parent, x, y, wide, unit)
|
|||||||
|
|
||||||
local function _machine(mx, my, name)
|
local function _machine(mx, my, name)
|
||||||
local l = string.len(name) + 2
|
local l = string.len(name) + 2
|
||||||
TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=ALIGN.CENTER,fg_bg=lg_gray,width=l,height=1}
|
TextBox{parent=waste,x=mx,y=my,text=string.rep("\x8f",l),alignment=ALIGN.CENTER,fg_bg=cpair(style.theme.bg,style.theme.header.bkg),width=l,height=1}
|
||||||
TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=ALIGN.CENTER,fg_bg=wh_gray,width=l,height=1}
|
TextBox{parent=waste,x=mx,y=my+1,text=name,alignment=ALIGN.CENTER,fg_bg=style.theme.header,width=l,height=1}
|
||||||
end
|
end
|
||||||
|
|
||||||
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=bw_fg_bg}
|
local waste_rate = DataIndicator{parent=waste,x=1,y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
||||||
local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg}
|
local pu_rate = DataIndicator{parent=waste,x=_wide(82,70),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=s_field}
|
||||||
local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg}
|
local po_rate = DataIndicator{parent=waste,x=_wide(52,45),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
||||||
local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg}
|
local popl_rate = DataIndicator{parent=waste,x=_wide(82,70),y=6,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
||||||
local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg}
|
local poam_rate = DataIndicator{parent=waste,x=_wide(82,70),y=10,lu_colors=lu_c,label="",unit="mB/t",format="%7.2f",value=0,width=12,fg_bg=s_field}
|
||||||
local spent_rate = DataIndicator{parent=waste,x=_wide(117,99),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%7.3f",value=0,width=12,fg_bg=bw_fg_bg}
|
local spent_rate = DataIndicator{parent=waste,x=_wide(117,98),y=3,lu_colors=lu_c,label="",unit="mB/t",format="%8.3f",value=0,width=13,fg_bg=s_field}
|
||||||
|
|
||||||
waste_rate.register(unit.unit_ps, "act_burn_rate", waste_rate.update)
|
waste_rate.register(unit.unit_ps, "act_burn_rate", waste_rate.update)
|
||||||
pu_rate.register(unit.unit_ps, "pu_rate", pu_rate.update)
|
pu_rate.register(unit.unit_ps, "pu_rate", pu_rate.update)
|
||||||
@@ -204,17 +210,17 @@ local function make(parent, x, y, wide, unit)
|
|||||||
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
_machine(_wide(116, 94), 6, "SPENT WASTE \x1b")
|
||||||
|
|
||||||
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=waste,x=_wide(30,25),y=3,text="SNAs [Po]",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
||||||
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=bw_fg_bg}
|
local sna_po = Rectangle{parent=waste,x=_wide(30,25),y=4,border=border(1,colors.gray,true),width=19,height=7,thin=true,fg_bg=style.theme.highlight_box_bright}
|
||||||
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
local sna_act = IndicatorLight{parent=sna_po,label="ACTIVE",colors=ind_grn}
|
||||||
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c,label="CNT",unit="",format="%2d",value=0,width=7}
|
local sna_cnt = DataIndicator{parent=sna_po,x=12,y=1,lu_colors=lu_c_d,label="CNT",unit="",format="%2d",value=0,width=7}
|
||||||
local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c,label="PEAK",unit="mB/t",format="%7.2f",value=0,width=17}
|
local sna_pk = DataIndicator{parent=sna_po,y=3,lu_colors=lu_c_d,label="PEAK",unit="mB/t",format="%7.2f",value=0,width=17}
|
||||||
local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c,label="MAX",unit="mB/t",format="%8.2f",value=0,width=17}
|
local sna_max = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="MAX",unit="mB/t",format="%8.2f",value=0,width=17}
|
||||||
local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c,label="IN",unit="mB/t",format="%9.2f",value=0,width=17}
|
local sna_in = DataIndicator{parent=sna_po,lu_colors=lu_c_d,label="IN",unit="mB/t",format="%9.2f",value=0,width=17}
|
||||||
|
|
||||||
sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end)
|
sna_act.register(unit.unit_ps, "po_rate", function (r) sna_act.update(r > 0) end)
|
||||||
sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update)
|
sna_cnt.register(unit.unit_ps, "sna_count", sna_cnt.update)
|
||||||
sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update)
|
sna_pk.register(unit.unit_ps, "sna_peak_rate", sna_pk.update)
|
||||||
sna_max.register(unit.unit_ps, "sna_prod_rate", sna_max.update)
|
sna_max.register(unit.unit_ps, "sna_max_rate", sna_max.update)
|
||||||
sna_in.register(unit.unit_ps, "sna_in", sna_in.update)
|
sna_in.register(unit.unit_ps, "sna_in", sna_in.update)
|
||||||
|
|
||||||
return root
|
return root
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ local function make(parent, x, y, unit)
|
|||||||
local root = Div{parent=parent,x=x,y=y,width=80,height=height}
|
local root = Div{parent=parent,x=x,y=y,width=80,height=height}
|
||||||
|
|
||||||
-- unit header message
|
-- unit header message
|
||||||
TextBox{parent=root,text="Unit #"..unit.unit_id,alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
TextBox{parent=root,text="Unit #"..unit.unit_id,alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
-- REACTOR --
|
-- REACTOR --
|
||||||
@@ -66,7 +66,7 @@ local function make(parent, x, y, unit)
|
|||||||
table.insert(coolant_pipes, pipe(2, 0, 11, 11, colors.orange))
|
table.insert(coolant_pipes, pipe(2, 0, 11, 11, colors.orange))
|
||||||
end
|
end
|
||||||
|
|
||||||
PipeNetwork{parent=root,x=4,y=10,pipes=coolant_pipes,bg=colors.lightGray}
|
PipeNetwork{parent=root,x=4,y=10,pipes=coolant_pipes,bg=style.theme.bg}
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
@@ -164,10 +164,10 @@ local function make(parent, x, y, unit)
|
|||||||
table.insert(steam_pipes_b, pipe(0, 18, 2, 18, colors.blue, false, true)) -- water boiler 2 to turbine 2 junction
|
table.insert(steam_pipes_b, pipe(0, 18, 2, 18, colors.blue, false, true)) -- water boiler 2 to turbine 2 junction
|
||||||
end
|
end
|
||||||
|
|
||||||
PipeNetwork{parent=root,x=47,y=11,pipes=steam_pipes_a,bg=colors.lightGray}
|
PipeNetwork{parent=root,x=47,y=11,pipes=steam_pipes_a,bg=style.theme.bg}
|
||||||
end
|
end
|
||||||
|
|
||||||
PipeNetwork{parent=root,x=54,y=3,pipes=steam_pipes_b,bg=colors.lightGray}
|
PipeNetwork{parent=root,x=54,y=3,pipes=steam_pipes_b,bg=style.theme.bg}
|
||||||
|
|
||||||
return root
|
return root
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,13 +32,16 @@ local border = core.border
|
|||||||
local pipe = core.pipe
|
local pipe = core.pipe
|
||||||
|
|
||||||
local wh_gray = style.wh_gray
|
local wh_gray = style.wh_gray
|
||||||
local bw_fg_bg = style.bw_fg_bg
|
|
||||||
local text_col = style.text_colors
|
|
||||||
local lu_col = style.lu_colors
|
|
||||||
|
|
||||||
-- create new flow view
|
-- create new flow view
|
||||||
---@param main graphics_element main displaybox
|
---@param main graphics_element main displaybox
|
||||||
local function init(main)
|
local function init(main)
|
||||||
|
local s_hi_bright = style.theme.highlight_box_bright
|
||||||
|
local s_field = style.theme.field_box
|
||||||
|
local text_col = style.text_colors
|
||||||
|
local lu_col = style.lu_colors
|
||||||
|
local lu_c_d = style.lu_colors_dark
|
||||||
|
|
||||||
local facility = iocontrol.get_db().facility
|
local facility = iocontrol.get_db().facility
|
||||||
local units = iocontrol.get_db().units
|
local units = iocontrol.get_db().units
|
||||||
|
|
||||||
@@ -46,9 +49,9 @@ local function init(main)
|
|||||||
local tank_list = facility.tank_list
|
local tank_list = facility.tank_list
|
||||||
|
|
||||||
-- window header message
|
-- window header message
|
||||||
local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
local header = TextBox{parent=main,y=1,text="Facility Coolant and Waste Flow Monitor",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
||||||
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=style.header}
|
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=style.theme.header}
|
||||||
|
|
||||||
datetime.register(facility.ps, "date_time", datetime.set_value)
|
datetime.register(facility.ps, "date_time", datetime.set_value)
|
||||||
|
|
||||||
@@ -240,16 +243,17 @@ local function init(main)
|
|||||||
local flow_x = 3
|
local flow_x = 3
|
||||||
if #water_pipes > 0 then
|
if #water_pipes > 0 then
|
||||||
flow_x = 25
|
flow_x = 25
|
||||||
PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=colors.lightGray}
|
PipeNetwork{parent=main,x=2,y=3,pipes=water_pipes,bg=style.theme.bg}
|
||||||
end
|
end
|
||||||
|
|
||||||
for i = 1, facility.num_units do
|
for i = 1, facility.num_units do
|
||||||
local y_offset = y_ofs(i)
|
local y_offset = y_ofs(i)
|
||||||
unit_flow(main, flow_x, 5 + y_offset, #water_pipes == 0, units[i])
|
unit_flow(main, flow_x, 5 + y_offset, #water_pipes == 0, units[i])
|
||||||
table.insert(po_pipes, pipe(0, 3 + y_offset, 4, 0, colors.cyan, true, true))
|
table.insert(po_pipes, pipe(0, 3 + y_offset, 4, 0, colors.cyan, true, true))
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
|
|
||||||
PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=colors.lightGray}
|
PipeNetwork{parent=main,x=139,y=15,pipes=po_pipes,bg=style.theme.bg}
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
-- tank valves --
|
-- tank valves --
|
||||||
@@ -297,7 +301,7 @@ local function init(main)
|
|||||||
|
|
||||||
TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label}
|
TextBox{parent=tank_box,x=2,y=3,text="Fill",height=1,width=10,fg_bg=style.label}
|
||||||
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
|
local tank_pcnt = DataIndicator{parent=tank_box,x=10,y=3,label="",format="%5.2f",value=100,unit="%",lu_colors=lu_col,width=8,fg_bg=text_col}
|
||||||
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=16,fg_bg=bw_fg_bg}
|
local tank_amnt = DataIndicator{parent=tank_box,x=2,label="",format="%13d",value=0,commas=true,unit="mB",lu_colors=lu_col,width=16,fg_bg=s_field}
|
||||||
|
|
||||||
TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label}
|
TextBox{parent=tank_box,x=2,y=6,text="Water Level",height=1,width=11,fg_bg=style.label}
|
||||||
local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
|
local level = HorizontalBar{parent=tank_box,x=2,y=7,bar_fg_bg=cpair(colors.blue,colors.gray),height=1,width=16}
|
||||||
@@ -332,6 +336,8 @@ local function init(main)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
---------
|
---------
|
||||||
-- SPS --
|
-- SPS --
|
||||||
---------
|
---------
|
||||||
@@ -348,12 +354,12 @@ local function init(main)
|
|||||||
status.register(facility.sps_ps_tbl[1], "computed_status", status.update)
|
status.register(facility.sps_ps_tbl[1], "computed_status", status.update)
|
||||||
|
|
||||||
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label}
|
TextBox{parent=sps_box,x=2,y=3,text="Input Rate",height=1,width=10,fg_bg=style.label}
|
||||||
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.3f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg}
|
local sps_in = DataIndicator{parent=sps_box,x=2,label="",format="%15.2f",value=0,unit="mB/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
||||||
|
|
||||||
sps_in.register(facility.ps, "po_am_rate", sps_in.update)
|
sps_in.register(facility.ps, "po_am_rate", sps_in.update)
|
||||||
|
|
||||||
TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label}
|
TextBox{parent=sps_box,x=2,y=6,text="Production Rate",height=1,width=15,fg_bg=style.label}
|
||||||
local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=bw_fg_bg}
|
local sps_rate = DataIndicator{parent=sps_box,x=2,label="",format="%15d",value=0,unit="\xb5B/t",lu_colors=lu_col,width=20,fg_bg=s_field}
|
||||||
|
|
||||||
sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end)
|
sps_rate.register(facility.sps_ps_tbl[1], "process_rate", function (r) sps_rate.update(r * 1000) end)
|
||||||
|
|
||||||
@@ -362,24 +368,24 @@ local function init(main)
|
|||||||
----------------
|
----------------
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=16,text="RAW WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=16,text="RAW WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
||||||
local raw_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg}
|
local raw_waste = Rectangle{parent=main,x=145,y=17,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
||||||
local sum_raw_waste = DataIndicator{parent=raw_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.2f",value=0,width=17}
|
local sum_raw_waste = DataIndicator{parent=raw_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.2f",value=0,width=17}
|
||||||
|
|
||||||
sum_raw_waste.register(facility.ps, "burn_sum", sum_raw_waste.update)
|
sum_raw_waste.register(facility.ps, "burn_sum", sum_raw_waste.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=21,text="PROC. WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=21,text="PROC. WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
||||||
local pr_waste = Rectangle{parent=main,x=145,y=22,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=bw_fg_bg}
|
local pr_waste = Rectangle{parent=main,x=145,y=22,border=border(1,colors.gray,true),width=19,height=5,thin=true,fg_bg=s_hi_bright}
|
||||||
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
|
local pu = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Pu",unit="mB/t",format="%9.3f",value=0,width=17}
|
||||||
local po = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="Po",unit="mB/t",format="%9.3f",value=0,width=17}
|
local po = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="Po",unit="mB/t",format="%9.2f",value=0,width=17}
|
||||||
local popl = DataIndicator{parent=pr_waste,lu_colors=lu_col,label="PoPl",unit="mB/t",format="%7.3f",value=0,width=17}
|
local popl = DataIndicator{parent=pr_waste,lu_colors=lu_c_d,label="PoPl",unit="mB/t",format="%7.2f",value=0,width=17}
|
||||||
|
|
||||||
pu.register(facility.ps, "pu_rate", pu.update)
|
pu.register(facility.ps, "pu_rate", pu.update)
|
||||||
po.register(facility.ps, "po_rate", po.update)
|
po.register(facility.ps, "po_rate", po.update)
|
||||||
popl.register(facility.ps, "po_pl_rate", popl.update)
|
popl.register(facility.ps, "po_pl_rate", popl.update)
|
||||||
|
|
||||||
TextBox{parent=main,x=145,y=28,text="SPENT WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
TextBox{parent=main,x=145,y=28,text="SPENT WASTE",alignment=ALIGN.CENTER,width=19,height=1,fg_bg=wh_gray}
|
||||||
local sp_waste = Rectangle{parent=main,x=145,y=29,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=bw_fg_bg}
|
local sp_waste = Rectangle{parent=main,x=145,y=29,border=border(1,colors.gray,true),width=19,height=3,thin=true,fg_bg=s_hi_bright}
|
||||||
local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_col,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17}
|
local sum_sp_waste = DataIndicator{parent=sp_waste,lu_colors=lu_c_d,label="SUM",unit="mB/t",format="%8.3f",value=0,width=17}
|
||||||
|
|
||||||
sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update)
|
sum_sp_waste.register(facility.ps, "spent_waste_rate", sum_sp_waste.update)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,8 +22,11 @@ local TextBox = require("graphics.elements.textbox")
|
|||||||
local TabBar = require("graphics.elements.controls.tabbar")
|
local TabBar = require("graphics.elements.controls.tabbar")
|
||||||
|
|
||||||
local LED = require("graphics.elements.indicators.led")
|
local LED = require("graphics.elements.indicators.led")
|
||||||
|
local LEDPair = require("graphics.elements.indicators.ledpair")
|
||||||
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||||
|
|
||||||
|
local LINK_STATE = types.PANEL_LINK_STATE
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
@@ -36,7 +39,7 @@ local led_grn = style.led_grn
|
|||||||
local function init(panel, num_units)
|
local function init(panel, num_units)
|
||||||
local ps = iocontrol.get_db().fp.ps
|
local ps = iocontrol.get_db().fp.ps
|
||||||
|
|
||||||
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.fp.header}
|
TextBox{parent=panel,y=1,text="SCADA COORDINATOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.fp_theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
|
|
||||||
@@ -56,19 +59,58 @@ local function init(panel, num_units)
|
|||||||
heartbeat.register(ps, "heartbeat", heartbeat.update)
|
heartbeat.register(ps, "heartbeat", heartbeat.update)
|
||||||
|
|
||||||
local modem = LED{parent=system,label="MODEM",colors=led_grn}
|
local modem = LED{parent=system,label="MODEM",colors=led_grn}
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
|
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
if not style.colorblind then
|
||||||
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.fp_ind_bkg}}
|
||||||
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
|
network.register(ps, "link_state", network.update)
|
||||||
|
else
|
||||||
|
local nt_lnk = LEDPair{parent=system,label="NT LINKED",off=style.fp_ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
local nt_ver = LEDPair{parent=system,label="NT VERSION",off=style.fp_ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
|
||||||
|
nt_lnk.register(ps, "link_state", function (state)
|
||||||
|
local value = 2
|
||||||
|
|
||||||
|
if state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
elseif state == LINK_STATE.LINKED then
|
||||||
|
value = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_lnk.update(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
nt_ver.register(ps, "link_state", function (state)
|
||||||
|
local value = 3
|
||||||
|
|
||||||
|
if state == LINK_STATE.BAD_VERSION then
|
||||||
|
value = 2
|
||||||
|
elseif state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_ver.update(value)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
system.line_break()
|
system.line_break()
|
||||||
|
|
||||||
modem.register(ps, "has_modem", modem.update)
|
modem.register(ps, "has_modem", modem.update)
|
||||||
network.register(ps, "link_state", network.update)
|
|
||||||
|
|
||||||
local speaker = LED{parent=system,label="SPEAKER",colors=led_grn}
|
local speaker = LED{parent=system,label="SPEAKER",colors=led_grn}
|
||||||
speaker.register(ps, "has_speaker", speaker.update)
|
speaker.register(ps, "has_speaker", speaker.update)
|
||||||
|
|
||||||
|
system.line_break()
|
||||||
|
|
||||||
|
local rt_main = LED{parent=system,label="RT MAIN",colors=led_grn}
|
||||||
|
local rt_render = LED{parent=system,label="RT RENDER",colors=led_grn}
|
||||||
|
|
||||||
|
rt_main.register(ps, "routine__main", rt_main.update)
|
||||||
|
rt_render.register(ps, "routine__render", rt_render.update)
|
||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp_label}
|
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp.disabled_fg}
|
||||||
|
|
||||||
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
|
local monitors = Div{parent=main_page,width=16,height=17,x=18,y=2}
|
||||||
|
|
||||||
@@ -89,7 +131,7 @@ local function init(panel, num_units)
|
|||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp_label}
|
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
|
|
||||||
@@ -103,7 +145,7 @@ local function init(panel, num_units)
|
|||||||
-- API page
|
-- API page
|
||||||
|
|
||||||
local api_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local api_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local api_list = ListBox{parent=api_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp_text,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local api_list = ListBox{parent=api_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=api_list,height=1,hidden=true} -- padding
|
local _ = Div{parent=api_list,height=1,hidden=true} -- padding
|
||||||
|
|
||||||
-- assemble page panes
|
-- assemble page panes
|
||||||
@@ -113,11 +155,11 @@ local function init(panel, num_units)
|
|||||||
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
||||||
|
|
||||||
local tabs = {
|
local tabs = {
|
||||||
{ name = "CRD", color = style.fp_text },
|
{ name = "CRD", color = style.fp.text },
|
||||||
{ name = "API", color = style.fp_text },
|
{ name = "API", color = style.fp.text },
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=style.bw_fg_bg}
|
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=style.fp_theme.highlight_box_bright}
|
||||||
|
|
||||||
-- link pocket API list management to PGI
|
-- link pocket API list management to PGI
|
||||||
pgi.link_elements(api_list, pkt_entry)
|
pgi.link_elements(api_list, pkt_entry)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
-- Main SCADA Coordinator GUI
|
-- Main SCADA Coordinator GUI
|
||||||
--
|
--
|
||||||
|
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local iocontrol = require("coordinator.iocontrol")
|
local iocontrol = require("coordinator.iocontrol")
|
||||||
|
|
||||||
local style = require("coordinator.ui.style")
|
local style = require("coordinator.ui.style")
|
||||||
@@ -21,14 +23,16 @@ local ALIGN = core.ALIGN
|
|||||||
-- create new main view
|
-- create new main view
|
||||||
---@param main graphics_element main displaybox
|
---@param main graphics_element main displaybox
|
||||||
local function init(main)
|
local function init(main)
|
||||||
|
local s_header = style.theme.header
|
||||||
|
|
||||||
local facility = iocontrol.get_db().facility
|
local facility = iocontrol.get_db().facility
|
||||||
local units = iocontrol.get_db().units
|
local units = iocontrol.get_db().units
|
||||||
|
|
||||||
-- window header message
|
-- window header message
|
||||||
local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
local header = TextBox{parent=main,y=1,text="Nuclear Generation Facility SCADA Coordinator",alignment=ALIGN.CENTER,height=1,fg_bg=s_header}
|
||||||
local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=style.lg_white,width=12,fg_bg=style.header}
|
local ping = DataIndicator{parent=main,x=1,y=1,label="SVTT",format="%d",value=0,unit="ms",lu_colors=style.lg_white,width=12,fg_bg=s_header}
|
||||||
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
-- max length example: "01:23:45 AM - Wednesday, September 28 2022"
|
||||||
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=style.header}
|
local datetime = TextBox{parent=main,x=(header.get_width()-42),y=1,text="",alignment=ALIGN.RIGHT,width=42,height=1,fg_bg=s_header}
|
||||||
|
|
||||||
ping.register(facility.ps, "sv_ping", ping.update)
|
ping.register(facility.ps, "sv_ping", ping.update)
|
||||||
datetime.register(facility.ps, "date_time", datetime.set_value)
|
datetime.register(facility.ps, "date_time", datetime.set_value)
|
||||||
@@ -51,6 +55,8 @@ local function init(main)
|
|||||||
|
|
||||||
cnc_y_start = cnc_y_start + row_1_height + 1
|
cnc_y_start = cnc_y_start + row_1_height + 1
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
if facility.num_units >= 3 then
|
if facility.num_units >= 3 then
|
||||||
-- base offset 3, spacing 1, max height of units 1 and 2
|
-- base offset 3, spacing 1, max height of units 1 and 2
|
||||||
local row_2_offset = cnc_y_start
|
local row_2_offset = cnc_y_start
|
||||||
@@ -62,12 +68,12 @@ local function init(main)
|
|||||||
uo_4 = unit_overview(main, 84, row_2_offset, units[4])
|
uo_4 = unit_overview(main, 84, row_2_offset, units[4])
|
||||||
cnc_y_start = math.max(cnc_y_start, row_2_offset + uo_4.get_height() + 1)
|
cnc_y_start = math.max(cnc_y_start, row_2_offset + uo_4.get_height() + 1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.nop()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- command & control
|
-- command & control
|
||||||
|
|
||||||
cnc_y_start = cnc_y_start
|
|
||||||
|
|
||||||
-- induction matrix and process control interfaces are 24 tall + space needed for divider
|
-- induction matrix and process control interfaces are 24 tall + space needed for divider
|
||||||
local cnc_bottom_align_start = main.get_height() - 26
|
local cnc_bottom_align_start = main.get_height() - 26
|
||||||
|
|
||||||
@@ -79,6 +85,8 @@ local function init(main)
|
|||||||
|
|
||||||
process_ctl(main, 2, cnc_bottom_align_start)
|
process_ctl(main, 2, cnc_bottom_align_start)
|
||||||
|
|
||||||
|
util.nop()
|
||||||
|
|
||||||
imatrix(main, 131, cnc_bottom_align_start, facility.induction_data_tbl[1], facility.induction_ps_tbl[1])
|
imatrix(main, 131, cnc_bottom_align_start, facility.induction_data_tbl[1], facility.induction_ps_tbl[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,79 +2,141 @@
|
|||||||
-- Graphics Style Options
|
-- Graphics Style Options
|
||||||
--
|
--
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
|
---@class crd_style
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
-- GLOBAL --
|
|
||||||
|
|
||||||
-- add color mappings for front panel
|
|
||||||
colors.ivory = colors.pink
|
|
||||||
colors.yellow_hc = colors.purple
|
|
||||||
colors.red_off = colors.brown
|
|
||||||
colors.yellow_off = colors.magenta
|
|
||||||
colors.green_off = colors.lime
|
|
||||||
|
|
||||||
-- front panel styling
|
-- front panel styling
|
||||||
|
|
||||||
style.fp = {}
|
style.fp_theme = themes.sandstone
|
||||||
|
style.fp = themes.get_fp_style(style.fp_theme)
|
||||||
|
|
||||||
style.fp.root = cpair(colors.black, colors.ivory)
|
style.led_grn = cpair(colors.green, colors.green_off)
|
||||||
style.fp.header = cpair(colors.black, colors.lightGray)
|
|
||||||
|
|
||||||
style.fp.colors = {
|
|
||||||
{ c = colors.red, hex = 0xdf4949 }, -- RED ON
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xf9fb53 }, -- YELLOW ON
|
|
||||||
{ c = colors.lime, hex = 0x16665a }, -- GREEN OFF
|
|
||||||
{ c = colors.green, hex = 0x6be551 }, -- GREEN ON
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee }, -- YELLOW HIGH CONTRAST
|
|
||||||
{ c = colors.pink, hex = 0xdcd9ca }, -- IVORY
|
|
||||||
{ c = colors.magenta, hex = 0x85862c }, -- YELLOW OFF
|
|
||||||
-- { c = colors.white, hex = 0xdcd9ca },
|
|
||||||
{ c = colors.lightGray, hex = 0xb1b8b3 },
|
|
||||||
{ c = colors.gray, hex = 0x575757 },
|
|
||||||
-- { c = colors.black, hex = 0x191919 },
|
|
||||||
{ c = colors.brown, hex = 0x672223 } -- RED OFF
|
|
||||||
}
|
|
||||||
|
|
||||||
-- main GUI styling
|
-- main GUI styling
|
||||||
|
|
||||||
style.root = cpair(colors.black, colors.lightGray)
|
---@class theme
|
||||||
style.header = cpair(colors.white, colors.gray)
|
local smooth_stone = {
|
||||||
style.label = cpair(colors.gray, colors.lightGray)
|
text = colors.black,
|
||||||
|
text_inv = colors.white,
|
||||||
|
label = colors.gray,
|
||||||
|
label_dark = colors.gray,
|
||||||
|
disabled = colors.lightGray,
|
||||||
|
bg = colors.lightGray,
|
||||||
|
checkbox_bg = colors.black,
|
||||||
|
accent_light = colors.white,
|
||||||
|
accent_dark = colors.gray,
|
||||||
|
|
||||||
style.colors = {
|
fuel_color = colors.black,
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
header = cpair(colors.white, colors.gray),
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
text_fg = cpair(colors.black, colors._INHERIT),
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
label_fg = cpair(colors.gray, colors._INHERIT),
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
disabled_fg = cpair(colors.lightGray, colors._INHERIT),
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
highlight_box = cpair(colors.black, colors.white),
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
highlight_box_bright = cpair(colors.black, colors.white),
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
field_box = cpair(colors.black, colors.white),
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
-- { c = colors.white, hex = 0xf0f0f0 },
|
colors = themes.smooth_stone.colors,
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 },
|
-- color re-mappings for assistive modes
|
||||||
-- { c = colors.black, hex = 0x191919 },
|
color_modes = themes.smooth_stone.color_modes
|
||||||
-- { c = colors.brown, hex = 0x7f664c }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
---@type theme
|
||||||
|
local deepslate = {
|
||||||
|
text = colors.white,
|
||||||
|
text_inv = colors.black,
|
||||||
|
label = colors.lightGray,
|
||||||
|
label_dark = colors.gray,
|
||||||
|
disabled = colors.gray,
|
||||||
|
bg = colors.black,
|
||||||
|
checkbox_bg = colors.gray,
|
||||||
|
accent_light = colors.gray,
|
||||||
|
accent_dark = colors.lightGray,
|
||||||
|
|
||||||
|
fuel_color = colors.lightGray,
|
||||||
|
|
||||||
|
header = cpair(colors.white, colors.gray),
|
||||||
|
|
||||||
|
text_fg = cpair(colors.white, colors._INHERIT),
|
||||||
|
label_fg = cpair(colors.lightGray, colors._INHERIT),
|
||||||
|
disabled_fg = cpair(colors.gray, colors._INHERIT),
|
||||||
|
|
||||||
|
highlight_box = cpair(colors.white, colors.gray),
|
||||||
|
highlight_box_bright = cpair(colors.black, colors.lightGray),
|
||||||
|
field_box = cpair(colors.white, colors.gray),
|
||||||
|
|
||||||
|
colors = themes.deepslate.colors,
|
||||||
|
|
||||||
|
-- color re-mappings for assistive modes
|
||||||
|
color_modes = themes.deepslate.color_modes
|
||||||
|
}
|
||||||
|
|
||||||
|
style.theme = smooth_stone
|
||||||
|
|
||||||
|
-- set themes per configurations
|
||||||
|
---@param main UI_THEME main UI theme
|
||||||
|
---@param fp FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE the color mode to use
|
||||||
|
function style.set_themes(main, fp, color_mode)
|
||||||
|
local colorblind = color_mode ~= themes.COLOR_MODE.STANDARD and color_mode ~= themes.COLOR_MODE.STD_ON_BLACK
|
||||||
|
local gray_ind_off = color_mode == themes.COLOR_MODE.STANDARD or color_mode == themes.COLOR_MODE.BLUE_IND
|
||||||
|
|
||||||
|
style.ind_bkg = colors.gray
|
||||||
|
style.fp_ind_bkg = util.trinary(gray_ind_off, colors.gray, colors.black)
|
||||||
|
style.ind_hi_box_bg = util.trinary(gray_ind_off, colors.gray, colors.black)
|
||||||
|
|
||||||
|
if main == themes.UI_THEME.SMOOTH_STONE then
|
||||||
|
style.theme = smooth_stone
|
||||||
|
style.ind_bkg = util.trinary(gray_ind_off, colors.gray, colors.black)
|
||||||
|
elseif main == themes.UI_THEME.DEEPSLATE then
|
||||||
|
style.theme = deepslate
|
||||||
|
style.ind_hi_box_bg = util.trinary(gray_ind_off, colors.lightGray, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
|
style.colorblind = colorblind
|
||||||
|
|
||||||
|
style.root = cpair(style.theme.text, style.theme.bg)
|
||||||
|
style.label = cpair(style.theme.label, style.theme.bg)
|
||||||
|
|
||||||
|
-- high contrast text (also tags)
|
||||||
|
style.hc_text = cpair(style.theme.text, style.theme.text_inv)
|
||||||
|
-- text on default background
|
||||||
|
style.text_colors = cpair(style.theme.text, style.theme.bg)
|
||||||
|
-- label & unit colors
|
||||||
|
style.lu_colors = cpair(style.theme.label, style.theme.label)
|
||||||
|
-- label & unit colors (darker if set)
|
||||||
|
style.lu_colors_dark = cpair(style.theme.label_dark, style.theme.label_dark)
|
||||||
|
|
||||||
|
style.ind_grn = cpair(util.trinary(colorblind, colors.blue, colors.green), style.ind_bkg)
|
||||||
|
style.ind_yel = cpair(colors.yellow, style.ind_bkg)
|
||||||
|
style.ind_red = cpair(colors.red, style.ind_bkg)
|
||||||
|
style.ind_wht = cpair(colors.white, style.ind_bkg)
|
||||||
|
|
||||||
|
if fp == themes.FP_THEME.SANDSTONE then
|
||||||
|
style.fp_theme = themes.sandstone
|
||||||
|
elseif fp == themes.FP_THEME.BASALT then
|
||||||
|
style.fp_theme = themes.basalt
|
||||||
|
end
|
||||||
|
|
||||||
|
style.fp = themes.get_fp_style(style.fp_theme)
|
||||||
|
end
|
||||||
|
|
||||||
-- COMMON COLOR PAIRS --
|
-- COMMON COLOR PAIRS --
|
||||||
|
|
||||||
style.wh_gray = cpair(colors.white, colors.gray)
|
style.wh_gray = cpair(colors.white, colors.gray)
|
||||||
|
|
||||||
style.bw_fg_bg = cpair(colors.black, colors.white)
|
style.bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
style.text_colors = cpair(colors.black, colors.lightGray)
|
|
||||||
style.lu_colors = cpair(colors.gray, colors.gray)
|
|
||||||
style.hzd_fg_bg = style.wh_gray
|
style.hzd_fg_bg = style.wh_gray
|
||||||
style.dis_colors = cpair(colors.white, colors.lightGray)
|
style.dis_colors = cpair(colors.white, colors.lightGray)
|
||||||
|
|
||||||
@@ -82,15 +144,6 @@ style.lg_gray = cpair(colors.lightGray, colors.gray)
|
|||||||
style.lg_white = cpair(colors.lightGray, colors.white)
|
style.lg_white = cpair(colors.lightGray, colors.white)
|
||||||
style.gray_white = cpair(colors.gray, colors.white)
|
style.gray_white = cpair(colors.gray, colors.white)
|
||||||
|
|
||||||
style.ind_grn = cpair(colors.green, colors.gray)
|
|
||||||
style.ind_yel = cpair(colors.yellow, colors.gray)
|
|
||||||
style.ind_red = cpair(colors.red, colors.gray)
|
|
||||||
style.ind_wht = style.wh_gray
|
|
||||||
|
|
||||||
style.fp_text = cpair(colors.black, colors.ivory)
|
|
||||||
style.fp_label = cpair(colors.lightGray, colors.ivory)
|
|
||||||
style.led_grn = cpair(colors.green, colors.green_off)
|
|
||||||
|
|
||||||
-- UI COMPONENTS --
|
-- UI COMPONENTS --
|
||||||
|
|
||||||
style.reactor = {
|
style.reactor = {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ local flasher = require("graphics.flasher")
|
|||||||
|
|
||||||
local core = {}
|
local core = {}
|
||||||
|
|
||||||
core.version = "2.1.1"
|
core.version = "2.2.4"
|
||||||
|
|
||||||
core.flasher = flasher
|
core.flasher = flasher
|
||||||
core.events = events
|
core.events = events
|
||||||
@@ -61,6 +61,9 @@ end
|
|||||||
---@field blit_fgd string
|
---@field blit_fgd string
|
||||||
---@field blit_bkg string
|
---@field blit_bkg string
|
||||||
|
|
||||||
|
-- add inherited flag, 3 isn't a pure color so it wouldn't be used
|
||||||
|
colors._INHERIT = 3
|
||||||
|
|
||||||
-- create a new color pair definition
|
-- create a new color pair definition
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param a color
|
---@param a color
|
||||||
|
|||||||
@@ -49,9 +49,11 @@ local element = {}
|
|||||||
---|indicator_light_args
|
---|indicator_light_args
|
||||||
---|power_indicator_args
|
---|power_indicator_args
|
||||||
---|rad_indicator_args
|
---|rad_indicator_args
|
||||||
|
---|signal_bar_args
|
||||||
---|state_indicator_args
|
---|state_indicator_args
|
||||||
---|tristate_indicator_light_args
|
---|tristate_indicator_light_args
|
||||||
---|vbar_args
|
---|vbar_args
|
||||||
|
---|app_multipane_args
|
||||||
---|colormap_args
|
---|colormap_args
|
||||||
---|displaybox_args
|
---|displaybox_args
|
||||||
---|div_args
|
---|div_args
|
||||||
@@ -196,6 +198,9 @@ function element.new(args, child_offset_x, child_offset_y)
|
|||||||
---@param offset_y integer y offset for mouse events
|
---@param offset_y integer y offset for mouse events
|
||||||
---@param next_y integer next line if no y was provided
|
---@param next_y integer next line if no y was provided
|
||||||
function protected.prepare_template(offset_x, offset_y, next_y)
|
function protected.prepare_template(offset_x, offset_y, next_y)
|
||||||
|
-- don't auto incrememnt y if inheriting height, that would cause an assertion
|
||||||
|
next_y = util.trinary(args.height == nil, 1, next_y)
|
||||||
|
|
||||||
-- record offsets in case there is a reposition
|
-- record offsets in case there is a reposition
|
||||||
self.offset_x = offset_x
|
self.offset_x = offset_x
|
||||||
self.offset_y = offset_y
|
self.offset_y = offset_y
|
||||||
@@ -234,11 +239,24 @@ function element.new(args, child_offset_x, child_offset_y)
|
|||||||
|
|
||||||
-- init colors
|
-- init colors
|
||||||
if args.fg_bg ~= nil then
|
if args.fg_bg ~= nil then
|
||||||
protected.fg_bg = args.fg_bg
|
protected.fg_bg = core.cpair(args.fg_bg.fgd, args.fg_bg.bkg)
|
||||||
elseif args.parent ~= nil then
|
|
||||||
protected.fg_bg = args.parent.get_fg_bg()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if args.parent ~= nil then
|
||||||
|
local p_fg_bg = args.parent.get_fg_bg()
|
||||||
|
|
||||||
|
if args.fg_bg == nil then
|
||||||
|
protected.fg_bg = core.cpair(p_fg_bg.fgd, p_fg_bg.bkg)
|
||||||
|
else
|
||||||
|
if protected.fg_bg.fgd == colors._INHERIT then protected.fg_bg = core.cpair(p_fg_bg.fgd, protected.fg_bg.bkg) end
|
||||||
|
if protected.fg_bg.bkg == colors._INHERIT then protected.fg_bg = core.cpair(protected.fg_bg.fgd, p_fg_bg.bkg) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check colors
|
||||||
|
element.assert(protected.fg_bg.fgd ~= colors._INHERIT, "could not determine foreground color to inherit")
|
||||||
|
element.assert(protected.fg_bg.bkg ~= colors._INHERIT, "could not determine background color to inherit")
|
||||||
|
|
||||||
-- set colors
|
-- set colors
|
||||||
protected.window.setBackgroundColor(protected.fg_bg.bkg)
|
protected.window.setBackgroundColor(protected.fg_bg.bkg)
|
||||||
protected.window.setTextColor(protected.fg_bg.fgd)
|
protected.window.setTextColor(protected.fg_bg.fgd)
|
||||||
|
|||||||
109
graphics/elements/appmultipane.lua
Normal file
109
graphics/elements/appmultipane.lua
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
-- App Page Multi-Pane Display Graphics Element
|
||||||
|
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
local element = require("graphics.element")
|
||||||
|
local events = require("graphics.events")
|
||||||
|
|
||||||
|
local MOUSE_CLICK = core.events.MOUSE_CLICK
|
||||||
|
|
||||||
|
---@class app_multipane_args
|
||||||
|
---@field panes table panes to swap between
|
||||||
|
---@field nav_colors cpair on/off colors (a/b respectively) for page navigator
|
||||||
|
---@field scroll_nav boolean? true to allow scrolling to change the active pane
|
||||||
|
---@field drag_nav boolean? true to allow mouse dragging to change the active pane (on mouse up)
|
||||||
|
---@field callback function? function to call when pane is changed by mouse interaction
|
||||||
|
---@field parent graphics_element
|
||||||
|
---@field id? string element id
|
||||||
|
---@field x? integer 1 if omitted
|
||||||
|
---@field y? integer auto incremented if omitted
|
||||||
|
---@field width? integer parent width if omitted
|
||||||
|
---@field height? integer parent height if omitted
|
||||||
|
---@field gframe? graphics_frame frame instead of x/y/width/height
|
||||||
|
---@field fg_bg? cpair foreground/background colors
|
||||||
|
---@field hidden? boolean true to hide on initial draw
|
||||||
|
|
||||||
|
-- new app multipane element
|
||||||
|
---@nodiscard
|
||||||
|
---@param args app_multipane_args
|
||||||
|
---@return graphics_element element, element_id id
|
||||||
|
local function multipane(args)
|
||||||
|
element.assert(type(args.panes) == "table", "panes is a required field")
|
||||||
|
|
||||||
|
-- create new graphics element base object
|
||||||
|
local e = element.new(args)
|
||||||
|
|
||||||
|
e.value = 1
|
||||||
|
|
||||||
|
local nav_x_start = math.floor((e.frame.w / 2) - (#args.panes / 2)) + 1
|
||||||
|
local nav_x_end = math.floor((e.frame.w / 2) - (#args.panes / 2)) + #args.panes
|
||||||
|
|
||||||
|
-- show the selected pane
|
||||||
|
function e.redraw()
|
||||||
|
for i = 1, #args.panes do args.panes[i].hide() end
|
||||||
|
args.panes[e.value].show()
|
||||||
|
|
||||||
|
-- draw page indicator dots
|
||||||
|
for i = 1, #args.panes do
|
||||||
|
e.w_set_cur(nav_x_start + (i - 1), e.frame.h)
|
||||||
|
e.w_set_fgd(util.trinary(i == e.value, args.nav_colors.color_a, args.nav_colors.color_b))
|
||||||
|
e.w_write("\x07")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- handle mouse interaction
|
||||||
|
---@param event mouse_interaction mouse event
|
||||||
|
function e.handle_mouse(event)
|
||||||
|
local initial = e.value
|
||||||
|
|
||||||
|
if e.enabled then
|
||||||
|
if event.current.y == e.frame.h and event.current.x >= nav_x_start and event.current.x <= nav_x_end then
|
||||||
|
local id = event.current.x - nav_x_start + 1
|
||||||
|
|
||||||
|
if event.type == MOUSE_CLICK.TAP then
|
||||||
|
e.set_value(id)
|
||||||
|
elseif event.type == MOUSE_CLICK.UP then
|
||||||
|
e.set_value(id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.scroll_nav then
|
||||||
|
if event.type == events.MOUSE_CLICK.SCROLL_DOWN then
|
||||||
|
e.set_value(e.value + 1)
|
||||||
|
elseif event.type == events.MOUSE_CLICK.SCROLL_UP then
|
||||||
|
e.set_value(e.value - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if args.drag_nav then
|
||||||
|
local x1, x2 = event.initial.x, event.current.x
|
||||||
|
if event.type == events.MOUSE_CLICK.UP and e.in_frame_bounds(x1, event.initial.y) and e.in_frame_bounds(x1, event.current.y) then
|
||||||
|
if x2 > x1 then
|
||||||
|
e.set_value(e.value - 1)
|
||||||
|
elseif x2 < x1 then
|
||||||
|
e.set_value(e.value + 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if e.value ~= initial and type(args.callback) == "function" then args.callback(e.value) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- select which pane is shown
|
||||||
|
---@param value integer pane to show
|
||||||
|
function e.set_value(value)
|
||||||
|
if (e.value ~= value) and (value > 0) and (value <= #args.panes) then
|
||||||
|
e.value = value
|
||||||
|
e.redraw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initial draw
|
||||||
|
e.redraw()
|
||||||
|
|
||||||
|
return e.complete()
|
||||||
|
end
|
||||||
|
|
||||||
|
return multipane
|
||||||
@@ -30,7 +30,7 @@ local function app_button(args)
|
|||||||
element.assert(type(args.app_fg_bg) == "table", "app_fg_bg is a required field")
|
element.assert(type(args.app_fg_bg) == "table", "app_fg_bg is a required field")
|
||||||
|
|
||||||
args.height = 4
|
args.height = 4
|
||||||
args.width = 5
|
args.width = 7
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- create new graphics element base object
|
||||||
local e = element.new(args)
|
local e = element.new(args)
|
||||||
@@ -46,7 +46,7 @@ local function app_button(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- draw icon
|
-- draw icon
|
||||||
e.w_set_cur(1, 1)
|
e.w_set_cur(2, 1)
|
||||||
e.w_set_fgd(fgd)
|
e.w_set_fgd(fgd)
|
||||||
e.w_set_bkg(bkg)
|
e.w_set_bkg(bkg)
|
||||||
e.w_write("\x9f\x83\x83\x83")
|
e.w_write("\x9f\x83\x83\x83")
|
||||||
@@ -55,16 +55,16 @@ local function app_button(args)
|
|||||||
e.w_write("\x90")
|
e.w_write("\x90")
|
||||||
e.w_set_fgd(fgd)
|
e.w_set_fgd(fgd)
|
||||||
e.w_set_bkg(bkg)
|
e.w_set_bkg(bkg)
|
||||||
e.w_set_cur(1, 2)
|
e.w_set_cur(2, 2)
|
||||||
e.w_write("\x95 ")
|
e.w_write("\x95 ")
|
||||||
e.w_set_fgd(bkg)
|
e.w_set_fgd(bkg)
|
||||||
e.w_set_bkg(fgd)
|
e.w_set_bkg(fgd)
|
||||||
e.w_write("\x95")
|
e.w_write("\x95")
|
||||||
e.w_set_cur(1, 3)
|
e.w_set_cur(2, 3)
|
||||||
e.w_write("\x82\x8f\x8f\x8f\x81")
|
e.w_write("\x82\x8f\x8f\x8f\x81")
|
||||||
|
|
||||||
-- write the icon text
|
-- write the icon text
|
||||||
e.w_set_cur(3, 2)
|
e.w_set_cur(4, 2)
|
||||||
e.w_set_fgd(fgd)
|
e.w_set_fgd(fgd)
|
||||||
e.w_set_bkg(bkg)
|
e.w_set_bkg(bkg)
|
||||||
e.w_write(args.text)
|
e.w_write(args.text)
|
||||||
|
|||||||
@@ -138,23 +138,21 @@ local function hazard_button(args)
|
|||||||
-- handle mouse interaction
|
-- handle mouse interaction
|
||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
if e.enabled then
|
if e.enabled and core.events.was_clicked(event.type) and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
if core.events.was_clicked(event.type) then
|
-- change text color to indicate clicked
|
||||||
-- change text color to indicate clicked
|
e.w_set_fgd(args.accent)
|
||||||
e.w_set_fgd(args.accent)
|
e.w_set_cur(3, 2)
|
||||||
e.w_set_cur(3, 2)
|
e.w_write(args.text)
|
||||||
e.w_write(args.text)
|
|
||||||
|
|
||||||
-- abort any other callbacks
|
-- abort any other callbacks
|
||||||
tcd.abort(on_timeout)
|
tcd.abort(on_timeout)
|
||||||
tcd.abort(on_success)
|
tcd.abort(on_success)
|
||||||
tcd.abort(on_failure)
|
tcd.abort(on_failure)
|
||||||
|
|
||||||
-- 1.5 second timeout
|
-- 1.5 second timeout
|
||||||
tcd.dispatch(1.5, on_timeout)
|
tcd.dispatch(1.5, on_timeout)
|
||||||
|
|
||||||
args.callback()
|
args.callback()
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,8 @@ local function radio_button(args)
|
|||||||
-- handle mouse interaction
|
-- handle mouse interaction
|
||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
if e.enabled and core.events.was_clicked(event.type) and (event.initial.y == event.current.y) then
|
if e.enabled and core.events.was_clicked(event.type) and
|
||||||
|
(event.initial.y == event.current.y) and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
-- determine what was pressed
|
-- determine what was pressed
|
||||||
if args.options[event.current.y] ~= nil then
|
if args.options[event.current.y] ~= nil then
|
||||||
e.value = event.current.y
|
e.value = event.current.y
|
||||||
|
|||||||
@@ -8,13 +8,7 @@ local element = require("graphics.element")
|
|||||||
|
|
||||||
local MOUSE_CLICK = core.events.MOUSE_CLICK
|
local MOUSE_CLICK = core.events.MOUSE_CLICK
|
||||||
|
|
||||||
---@class sidebar_tab
|
|
||||||
---@field char string character identifier
|
|
||||||
---@field color cpair tab colors (fg/bg)
|
|
||||||
|
|
||||||
---@class sidebar_args
|
---@class sidebar_args
|
||||||
---@field tabs table sidebar tab options
|
|
||||||
---@field callback function function to call on tab change
|
|
||||||
---@field parent graphics_element
|
---@field parent graphics_element
|
||||||
---@field id? string element id
|
---@field id? string element id
|
||||||
---@field x? integer 1 if omitted
|
---@field x? integer 1 if omitted
|
||||||
@@ -27,21 +21,16 @@ local MOUSE_CLICK = core.events.MOUSE_CLICK
|
|||||||
---@param args sidebar_args
|
---@param args sidebar_args
|
||||||
---@return graphics_element element, element_id id
|
---@return graphics_element element, element_id id
|
||||||
local function sidebar(args)
|
local function sidebar(args)
|
||||||
element.assert(type(args.tabs) == "table", "tabs is a required field")
|
|
||||||
element.assert(#args.tabs > 0, "at least one tab is required")
|
|
||||||
element.assert(type(args.callback) == "function", "callback is a required field")
|
|
||||||
|
|
||||||
args.width = 3
|
args.width = 3
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- create new graphics element base object
|
||||||
local e = element.new(args)
|
local e = element.new(args)
|
||||||
|
|
||||||
element.assert(e.frame.h >= (#args.tabs * 3), "height insufficent to display all tabs")
|
|
||||||
|
|
||||||
-- default to 1st tab
|
-- default to 1st tab
|
||||||
e.value = 1
|
e.value = 1
|
||||||
|
|
||||||
local was_pressed = false
|
local was_pressed = false
|
||||||
|
local tabs = {}
|
||||||
|
|
||||||
-- show the button state
|
-- show the button state
|
||||||
---@param pressed? boolean if the currently selected tab should appear as actively pressed
|
---@param pressed? boolean if the currently selected tab should appear as actively pressed
|
||||||
@@ -51,10 +40,18 @@ local function sidebar(args)
|
|||||||
was_pressed = pressed
|
was_pressed = pressed
|
||||||
pressed_idx = pressed_idx or e.value
|
pressed_idx = pressed_idx or e.value
|
||||||
|
|
||||||
for i = 1, #args.tabs do
|
-- clear
|
||||||
local tab = args.tabs[i] ---@type sidebar_tab
|
e.w_set_fgd(e.fg_bg.fgd)
|
||||||
|
e.w_set_bkg(e.fg_bg.bkg)
|
||||||
|
for y = 1, e.frame.h do
|
||||||
|
e.w_set_cur(1, y)
|
||||||
|
e.w_write(" ")
|
||||||
|
end
|
||||||
|
|
||||||
local y = ((i - 1) * 3) + 1
|
-- draw tabs
|
||||||
|
for i = 1, #tabs do
|
||||||
|
local tab = tabs[i] ---@type sidebar_tab
|
||||||
|
local y = tab.y_start
|
||||||
|
|
||||||
e.w_set_cur(1, y)
|
e.w_set_cur(1, y)
|
||||||
|
|
||||||
@@ -66,13 +63,29 @@ local function sidebar(args)
|
|||||||
e.w_set_bkg(tab.color.bkg)
|
e.w_set_bkg(tab.color.bkg)
|
||||||
end
|
end
|
||||||
|
|
||||||
e.w_write(" ")
|
if tab.tall then
|
||||||
e.w_set_cur(1, y + 1)
|
e.w_write(" ")
|
||||||
if e.value == i then
|
e.w_set_cur(1, y + 1)
|
||||||
e.w_write(" " .. tab.char .. "\x10")
|
end
|
||||||
else e.w_write(" " .. tab.char .. " ") end
|
|
||||||
e.w_set_cur(1, y + 2)
|
e.w_write(tab.label)
|
||||||
e.w_write(" ")
|
|
||||||
|
if tab.tall then
|
||||||
|
e.w_set_cur(1, y + 2)
|
||||||
|
e.w_write(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- determine which tab was pressed
|
||||||
|
---@param y integer y coordinate
|
||||||
|
local function find_tab(y)
|
||||||
|
for i = 1, #tabs do
|
||||||
|
local tab = tabs[i] ---@type sidebar_tab
|
||||||
|
|
||||||
|
if y >= tab.y_start and y <= tab.y_end then
|
||||||
|
return i
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -81,23 +94,25 @@ local function sidebar(args)
|
|||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
-- determine what was pressed
|
-- determine what was pressed
|
||||||
if e.enabled then
|
if e.enabled then
|
||||||
local cur_idx = math.ceil(event.current.y / 3)
|
local cur_idx = find_tab(event.current.y)
|
||||||
local ini_idx = math.ceil(event.initial.y / 3)
|
local ini_idx = find_tab(event.initial.y)
|
||||||
|
local tab = tabs[cur_idx]
|
||||||
|
|
||||||
if args.tabs[cur_idx] ~= nil then
|
-- handle press if a callback was provided
|
||||||
|
if tab ~= nil and type(tab.callback) == "function" then
|
||||||
if event.type == MOUSE_CLICK.TAP then
|
if event.type == MOUSE_CLICK.TAP then
|
||||||
e.value = cur_idx
|
e.value = cur_idx
|
||||||
draw(true)
|
draw(true)
|
||||||
-- show as unpressed in 0.25 seconds
|
-- show as unpressed in 0.25 seconds
|
||||||
tcd.dispatch(0.25, function () draw(false) end)
|
tcd.dispatch(0.25, function () draw(false) end)
|
||||||
args.callback(e.value)
|
tab.callback()
|
||||||
elseif event.type == MOUSE_CLICK.DOWN then
|
elseif event.type == MOUSE_CLICK.DOWN then
|
||||||
draw(true, cur_idx)
|
draw(true, cur_idx)
|
||||||
elseif event.type == MOUSE_CLICK.UP then
|
elseif event.type == MOUSE_CLICK.UP then
|
||||||
if cur_idx == ini_idx and e.in_frame_bounds(event.current.x, event.current.y) then
|
if cur_idx == ini_idx and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
e.value = cur_idx
|
e.value = cur_idx
|
||||||
draw(false)
|
draw(false)
|
||||||
args.callback(e.value)
|
tab.callback()
|
||||||
else draw(false) end
|
else draw(false) end
|
||||||
end
|
end
|
||||||
elseif event.type == MOUSE_CLICK.UP then
|
elseif event.type == MOUSE_CLICK.UP then
|
||||||
@@ -113,6 +128,35 @@ local function sidebar(args)
|
|||||||
draw(false)
|
draw(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- update the sidebar navigation options
|
||||||
|
---@param items table sidebar entries
|
||||||
|
function e.on_update(items)
|
||||||
|
local next_y = 1
|
||||||
|
|
||||||
|
tabs = {}
|
||||||
|
|
||||||
|
for i = 1, #items do
|
||||||
|
local item = items[i]
|
||||||
|
local height = util.trinary(item.tall, 3, 1)
|
||||||
|
|
||||||
|
---@class sidebar_tab
|
||||||
|
local entry = {
|
||||||
|
y_start = next_y, ---@type integer
|
||||||
|
y_end = next_y + height - 1, ---@type integer
|
||||||
|
tall = item.tall, ---@type boolean
|
||||||
|
label = item.label, ---@type string
|
||||||
|
color = item.color, ---@type cpair
|
||||||
|
callback = item.callback ---@type function|nil
|
||||||
|
}
|
||||||
|
|
||||||
|
next_y = next_y + height
|
||||||
|
|
||||||
|
tabs[i] = entry
|
||||||
|
end
|
||||||
|
|
||||||
|
draw()
|
||||||
|
end
|
||||||
|
|
||||||
-- element redraw
|
-- element redraw
|
||||||
e.redraw = draw
|
e.redraw = draw
|
||||||
|
|
||||||
|
|||||||
@@ -127,20 +127,19 @@ local function spinbox(args)
|
|||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
-- only handle if on an increment or decrement arrow
|
-- only handle if on an increment or decrement arrow
|
||||||
if e.enabled and core.events.was_clicked(event.type) and
|
if e.enabled and core.events.was_clicked(event.type) and e.in_frame_bounds(event.current.x, event.current.y) and
|
||||||
(event.current.x ~= dec_point_x) and (event.current.y ~= 2) then
|
(event.current.x ~= dec_point_x) and (event.current.y ~= 2) and
|
||||||
if event.current.x == event.initial.x and event.current.y == event.initial.y then
|
(event.current.x == event.initial.x) and (event.current.y == event.initial.y) then
|
||||||
local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x)
|
local idx = util.trinary(event.current.x > dec_point_x, event.current.x - 1, event.current.x)
|
||||||
if digits[idx] ~= nil then
|
if digits[idx] ~= nil then
|
||||||
if event.current.y == 1 then
|
if event.current.y == 1 then
|
||||||
digits[idx] = digits[idx] + 1
|
digits[idx] = digits[idx] + 1
|
||||||
elseif event.current.y == 3 then
|
elseif event.current.y == 3 then
|
||||||
digits[idx] = digits[idx] - 1
|
digits[idx] = digits[idx] - 1
|
||||||
end
|
|
||||||
|
|
||||||
update_value()
|
|
||||||
show_num()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
update_value()
|
||||||
|
show_num()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -58,14 +58,14 @@ local function switch_button(args)
|
|||||||
-- handle mouse interaction
|
-- handle mouse interaction
|
||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
if e.enabled and core.events.was_clicked(event.type) then
|
if e.enabled and core.events.was_clicked(event.type) and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
e.value = not e.value
|
e.value = not e.value
|
||||||
e.redraw()
|
e.redraw()
|
||||||
args.callback(e.value)
|
args.callback(e.value)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- set the value
|
-- set the value (does not call the callback)
|
||||||
---@param val boolean new value
|
---@param val boolean new value
|
||||||
function e.set_value(val)
|
function e.set_value(val)
|
||||||
e.value = val
|
e.value = val
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ local function tabbar(args)
|
|||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
-- determine what was pressed
|
-- determine what was pressed
|
||||||
if e.enabled and core.events.was_clicked(event.type) then
|
if e.enabled and core.events.was_clicked(event.type) and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
-- a button may have been pressed, which one was it?
|
-- a button may have been pressed, which one was it?
|
||||||
local tab_ini = which_tab(event.initial.x)
|
local tab_ini = which_tab(event.initial.x)
|
||||||
local tab_cur = which_tab(event.current.x)
|
local tab_cur = which_tab(event.current.x)
|
||||||
|
|||||||
@@ -53,11 +53,11 @@ local function number_field(args)
|
|||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
-- only handle if on an increment or decrement arrow
|
-- only handle if on an increment or decrement arrow
|
||||||
if e.enabled then
|
if e.enabled and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
if core.events.was_clicked(event.type) then
|
if core.events.was_clicked(event.type) then
|
||||||
e.take_focus()
|
e.take_focus()
|
||||||
|
|
||||||
if event.type == MOUSE_CLICK.UP and e.in_frame_bounds(event.current.x, event.current.y) then
|
if event.type == MOUSE_CLICK.UP then
|
||||||
ifield.move_cursor(event.current.x)
|
ifield.move_cursor(event.current.x)
|
||||||
end
|
end
|
||||||
elseif event.type == MOUSE_CLICK.DOUBLE_CLICK then
|
elseif event.type == MOUSE_CLICK.DOUBLE_CLICK then
|
||||||
|
|||||||
@@ -41,11 +41,11 @@ local function text_field(args)
|
|||||||
---@param event mouse_interaction mouse event
|
---@param event mouse_interaction mouse event
|
||||||
function e.handle_mouse(event)
|
function e.handle_mouse(event)
|
||||||
-- only handle if on an increment or decrement arrow
|
-- only handle if on an increment or decrement arrow
|
||||||
if e.enabled then
|
if e.enabled and e.in_frame_bounds(event.current.x, event.current.y) then
|
||||||
if core.events.was_clicked(event.type) then
|
if core.events.was_clicked(event.type) then
|
||||||
e.take_focus()
|
e.take_focus()
|
||||||
|
|
||||||
if event.type == MOUSE_CLICK.UP and e.in_frame_bounds(event.current.x, event.current.y) then
|
if event.type == MOUSE_CLICK.UP then
|
||||||
ifield.move_cursor(event.current.x)
|
ifield.move_cursor(event.current.x)
|
||||||
end
|
end
|
||||||
elseif event.type == MOUSE_CLICK.DOUBLE_CLICK then
|
elseif event.type == MOUSE_CLICK.DOUBLE_CLICK then
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ local element = require("graphics.element")
|
|||||||
---@class icon_indicator_args
|
---@class icon_indicator_args
|
||||||
---@field label string indicator label
|
---@field label string indicator label
|
||||||
---@field states table state color and symbol table
|
---@field states table state color and symbol table
|
||||||
---@field value? integer default state, defaults to 1
|
---@field value? integer|boolean default state, defaults to 1 (true = 2, false = 1)
|
||||||
---@field min_label_width? integer label length if omitted
|
---@field min_label_width? integer label length if omitted
|
||||||
---@field parent graphics_element
|
---@field parent graphics_element
|
||||||
---@field id? string element id
|
---@field id? string element id
|
||||||
@@ -33,6 +33,7 @@ local function icon(args)
|
|||||||
local e = element.new(args)
|
local e = element.new(args)
|
||||||
|
|
||||||
e.value = args.value or 1
|
e.value = args.value or 1
|
||||||
|
if e.value == true then e.value = 2 end
|
||||||
|
|
||||||
-- state blit strings
|
-- state blit strings
|
||||||
local state_blit_cmds = {}
|
local state_blit_cmds = {}
|
||||||
@@ -47,8 +48,11 @@ local function icon(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- on state change
|
-- on state change
|
||||||
---@param new_state integer indicator state
|
---@param new_state integer|boolean indicator state
|
||||||
function e.on_update(new_state)
|
function e.on_update(new_state)
|
||||||
|
new_state = new_state or 1
|
||||||
|
if new_state == true then new_state = 2 end
|
||||||
|
|
||||||
local blit_cmd = state_blit_cmds[new_state]
|
local blit_cmd = state_blit_cmds[new_state]
|
||||||
e.value = new_state
|
e.value = new_state
|
||||||
e.w_set_cur(1, 1)
|
e.w_set_cur(1, 1)
|
||||||
@@ -56,7 +60,7 @@ local function icon(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- set indicator state
|
-- set indicator state
|
||||||
---@param val integer indicator state
|
---@param val integer|boolean indicator state
|
||||||
function e.set_value(val) e.on_update(val) end
|
function e.set_value(val) e.on_update(val) end
|
||||||
|
|
||||||
-- element redraw
|
-- element redraw
|
||||||
|
|||||||
85
graphics/elements/indicators/signal.lua
Normal file
85
graphics/elements/indicators/signal.lua
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
-- Signal Bars Graphics Element
|
||||||
|
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local element = require("graphics.element")
|
||||||
|
|
||||||
|
---@class signal_bar_args
|
||||||
|
---@field compact? boolean true to use a single character (works better against edges that extend out colors)
|
||||||
|
---@field colors_low_med? cpair color a for low signal quality, color b for medium signal quality
|
||||||
|
---@field disconnect_color? color color for the 'x' on disconnect
|
||||||
|
---@field parent graphics_element
|
||||||
|
---@field id? string element id
|
||||||
|
---@field x? integer 1 if omitted
|
||||||
|
---@field y? integer auto incremented if omitted
|
||||||
|
---@field fg_bg? cpair foreground/background colors (foreground is used for high signal quality)
|
||||||
|
---@field hidden? boolean true to hide on initial draw
|
||||||
|
|
||||||
|
-- new signal bar
|
||||||
|
---@nodiscard
|
||||||
|
---@param args signal_bar_args
|
||||||
|
---@return graphics_element element, element_id id
|
||||||
|
local function signal_bar(args)
|
||||||
|
args.height = 1
|
||||||
|
args.width = util.trinary(args.compact, 1, 2)
|
||||||
|
|
||||||
|
-- create new graphics element base object
|
||||||
|
local e = element.new(args)
|
||||||
|
|
||||||
|
e.value = 0
|
||||||
|
|
||||||
|
local blit_bkg = args.fg_bg.blit_bkg
|
||||||
|
local blit_0, blit_1, blit_2, blit_3 = args.fg_bg.blit_fgd, args.fg_bg.blit_fgd, args.fg_bg.blit_fgd, args.fg_bg.blit_fgd
|
||||||
|
|
||||||
|
if type(args.colors_low_med) == "table" then
|
||||||
|
blit_1 = args.colors_low_med.blit_a or blit_1
|
||||||
|
blit_2 = args.colors_low_med.blit_b or blit_2
|
||||||
|
end
|
||||||
|
|
||||||
|
if util.is_int(args.disconnect_color) then blit_0 = colors.toBlit(args.disconnect_color) end
|
||||||
|
|
||||||
|
-- on state change (0 = offline, 1 through 3 = low to high signal)
|
||||||
|
---@param new_state integer signal state
|
||||||
|
function e.on_update(new_state)
|
||||||
|
e.value = new_state
|
||||||
|
e.redraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- set signal state (0 = offline, 1 through 3 = low to high signal)
|
||||||
|
---@param val integer signal state
|
||||||
|
function e.set_value(val) e.on_update(val) end
|
||||||
|
|
||||||
|
-- draw label and signal bar
|
||||||
|
function e.redraw()
|
||||||
|
e.w_set_cur(1, 1)
|
||||||
|
|
||||||
|
if args.compact then
|
||||||
|
if e.value == 1 then
|
||||||
|
e.w_blit("\x90", blit_1, blit_bkg)
|
||||||
|
elseif e.value == 2 then
|
||||||
|
e.w_blit("\x94", blit_2, blit_bkg)
|
||||||
|
elseif e.value == 3 then
|
||||||
|
e.w_blit("\x95", blit_3, blit_bkg)
|
||||||
|
else
|
||||||
|
e.w_blit("x", blit_0, blit_bkg)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if e.value == 1 then
|
||||||
|
e.w_blit("\x9f ", blit_bkg .. blit_bkg, blit_1 .. blit_bkg)
|
||||||
|
elseif e.value == 2 then
|
||||||
|
e.w_blit("\x9f\x94", blit_bkg .. blit_2, blit_2 .. blit_bkg)
|
||||||
|
elseif e.value == 3 then
|
||||||
|
e.w_blit("\x9f\x81", blit_bkg .. blit_bkg, blit_3 .. blit_3)
|
||||||
|
else
|
||||||
|
e.w_blit(" x", blit_0 .. blit_0, blit_bkg .. blit_bkg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- initial draw
|
||||||
|
e.redraw()
|
||||||
|
|
||||||
|
return e.complete()
|
||||||
|
end
|
||||||
|
|
||||||
|
return signal_bar
|
||||||
418
graphics/themes.lua
Normal file
418
graphics/themes.lua
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
--
|
||||||
|
-- Graphics Themes
|
||||||
|
--
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
---@class graphics_themes
|
||||||
|
local themes = {}
|
||||||
|
|
||||||
|
-- add color mappings for front panels
|
||||||
|
colors.ivory = colors.pink
|
||||||
|
colors.green_hc = colors.cyan
|
||||||
|
colors.yellow_hc = colors.purple
|
||||||
|
colors.red_off = colors.brown
|
||||||
|
colors.yellow_off = colors.magenta
|
||||||
|
colors.green_off = colors.lime
|
||||||
|
|
||||||
|
--#region Types
|
||||||
|
|
||||||
|
---@enum UI_THEME
|
||||||
|
themes.UI_THEME = { SMOOTH_STONE = 1, DEEPSLATE = 2 }
|
||||||
|
themes.UI_THEME_NAMES = { "Smooth Stone", "Deepslate" }
|
||||||
|
|
||||||
|
-- attempts to get the string name of a main ui theme
|
||||||
|
---@nodiscard
|
||||||
|
---@param id any
|
||||||
|
---@return string|nil
|
||||||
|
function themes.ui_theme_name(id)
|
||||||
|
if id == themes.UI_THEME.SMOOTH_STONE or
|
||||||
|
id == themes.UI_THEME.DEEPSLATE then
|
||||||
|
return themes.UI_THEME_NAMES[id]
|
||||||
|
else return nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@enum FP_THEME
|
||||||
|
themes.FP_THEME = { SANDSTONE = 1, BASALT = 2 }
|
||||||
|
themes.FP_THEME_NAMES = { "Sandstone", "Basalt" }
|
||||||
|
|
||||||
|
-- attempts to get the string name of a front panel theme
|
||||||
|
---@nodiscard
|
||||||
|
---@param id any
|
||||||
|
---@return string|nil
|
||||||
|
function themes.fp_theme_name(id)
|
||||||
|
if id == themes.FP_THEME.SANDSTONE or
|
||||||
|
id == themes.FP_THEME.BASALT then
|
||||||
|
return themes.FP_THEME_NAMES[id]
|
||||||
|
else return nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@enum COLOR_MODE
|
||||||
|
themes.COLOR_MODE = {
|
||||||
|
STANDARD = 1,
|
||||||
|
DEUTERANOPIA = 2,
|
||||||
|
PROTANOPIA = 3,
|
||||||
|
TRITANOPIA = 4,
|
||||||
|
BLUE_IND = 5,
|
||||||
|
STD_ON_BLACK = 6,
|
||||||
|
BLUE_ON_BLACK = 7,
|
||||||
|
NUM_MODES = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
themes.COLOR_MODE_NAMES = {
|
||||||
|
"Standard",
|
||||||
|
"Deuteranopia",
|
||||||
|
"Protanopia",
|
||||||
|
"Tritanopia",
|
||||||
|
"Blue for 'Good'",
|
||||||
|
"Standard + Black",
|
||||||
|
"Blue + Black"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- attempts to get the string name of a color mode
|
||||||
|
---@nodiscard
|
||||||
|
---@param id any
|
||||||
|
---@return string|nil
|
||||||
|
function themes.color_mode_name(id)
|
||||||
|
if id == themes.COLOR_MODE.STANDARD or
|
||||||
|
id == themes.COLOR_MODE.DEUTERANOPIA or
|
||||||
|
id == themes.COLOR_MODE.PROTANOPIA or
|
||||||
|
id == themes.COLOR_MODE.TRITANOPIA or
|
||||||
|
id == themes.COLOR_MODE.BLUE_IND or
|
||||||
|
id == themes.COLOR_MODE.STD_ON_BLACK or
|
||||||
|
id == themes.COLOR_MODE.BLUE_ON_BLACK then
|
||||||
|
return themes.COLOR_MODE_NAMES[id]
|
||||||
|
else return nil end
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Front Panel Themes
|
||||||
|
|
||||||
|
---@class fp_theme
|
||||||
|
themes.sandstone = {
|
||||||
|
text = colors.black,
|
||||||
|
label = colors.lightGray,
|
||||||
|
label_dark = colors.gray,
|
||||||
|
disabled = colors.lightGray,
|
||||||
|
bg = colors.ivory,
|
||||||
|
|
||||||
|
header = cpair(colors.black, colors.lightGray),
|
||||||
|
|
||||||
|
highlight_box = cpair(colors.black, colors.lightGray),
|
||||||
|
highlight_box_bright = cpair(colors.black, colors.white),
|
||||||
|
field_box = cpair(colors.gray, colors.white),
|
||||||
|
|
||||||
|
colors = {
|
||||||
|
{ c = colors.red, hex = 0xdf4949 },
|
||||||
|
{ c = colors.orange, hex = 0xffb659 },
|
||||||
|
{ c = colors.yellow, hex = 0xf9fb53 },
|
||||||
|
{ c = colors.green_off, hex = 0x16665a },
|
||||||
|
{ c = colors.green, hex = 0x6be551 },
|
||||||
|
{ c = colors.green_hc, hex = 0x6be551 },
|
||||||
|
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
||||||
|
{ c = colors.blue, hex = 0x0096ff },
|
||||||
|
{ c = colors.yellow_hc, hex = 0xe3bc2a },
|
||||||
|
{ c = colors.ivory, hex = 0xdcd9ca },
|
||||||
|
{ c = colors.yellow_off, hex = 0x85862c },
|
||||||
|
{ c = colors.white, hex = 0xf0f0f0 },
|
||||||
|
{ c = colors.lightGray, hex = 0xb1b8b3 },
|
||||||
|
{ c = colors.gray, hex = 0x575757 },
|
||||||
|
{ c = colors.black, hex = 0x191919 },
|
||||||
|
{ c = colors.red_off, hex = 0x672223 }
|
||||||
|
},
|
||||||
|
|
||||||
|
-- color re-mappings for assistive modes
|
||||||
|
color_modes = {
|
||||||
|
-- standard
|
||||||
|
{},
|
||||||
|
-- deuteranopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_hc, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_off, hex = 0x141414 },
|
||||||
|
{ c = colors.yellow, hex = 0xf7c311 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x141414 },
|
||||||
|
{ c = colors.red, hex = 0xfb5615 },
|
||||||
|
{ c = colors.red_off, hex = 0x141414 }
|
||||||
|
},
|
||||||
|
-- protanopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_hc, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_off, hex = 0x141414 },
|
||||||
|
{ c = colors.yellow, hex = 0xf5e633 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x141414 },
|
||||||
|
{ c = colors.red, hex = 0xff521a },
|
||||||
|
{ c = colors.red_off, hex = 0x141414 }
|
||||||
|
},
|
||||||
|
-- tritanopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x40cbd7 },
|
||||||
|
{ c = colors.green_hc, hex = 0x40cbd7 },
|
||||||
|
{ c = colors.green_off, hex = 0x141414 },
|
||||||
|
{ c = colors.yellow, hex = 0xffbc00 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x141414 },
|
||||||
|
{ c = colors.red, hex = 0xff0000 },
|
||||||
|
{ c = colors.red_off, hex = 0x141414 }
|
||||||
|
},
|
||||||
|
-- blue indicators
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_hc, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_off, hex = 0x053466 },
|
||||||
|
},
|
||||||
|
-- standard, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.green_off, hex = 0x141414 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x141414 },
|
||||||
|
{ c = colors.red_off, hex = 0x141414 }
|
||||||
|
},
|
||||||
|
-- blue indicators, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_hc, hex = 0x1081ff },
|
||||||
|
{ c = colors.green_off, hex = 0x141414 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x141414 },
|
||||||
|
{ c = colors.red_off, hex = 0x141414 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---@type fp_theme
|
||||||
|
themes.basalt = {
|
||||||
|
text = colors.white,
|
||||||
|
label = colors.gray,
|
||||||
|
label_dark = colors.ivory,
|
||||||
|
disabled = colors.lightGray,
|
||||||
|
bg = colors.ivory,
|
||||||
|
|
||||||
|
header = cpair(colors.white, colors.gray),
|
||||||
|
|
||||||
|
highlight_box = cpair(colors.white, colors.gray),
|
||||||
|
highlight_box_bright = cpair(colors.black, colors.lightGray),
|
||||||
|
field_box = cpair(colors.white, colors.gray),
|
||||||
|
|
||||||
|
colors = {
|
||||||
|
{ c = colors.red, hex = 0xf18486 },
|
||||||
|
{ c = colors.orange, hex = 0xffb659 },
|
||||||
|
{ c = colors.yellow, hex = 0xefe37c },
|
||||||
|
{ c = colors.green_off, hex = 0x436b41 },
|
||||||
|
{ c = colors.green, hex = 0x7ae175 },
|
||||||
|
{ c = colors.green_hc, hex = 0x7ae175 },
|
||||||
|
{ c = colors.lightBlue, hex = 0x7dc6f2 },
|
||||||
|
{ c = colors.blue, hex = 0x56aae6 },
|
||||||
|
{ c = colors.yellow_hc, hex = 0xe9cd68 },
|
||||||
|
{ c = colors.ivory, hex = 0x4d4e52 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x757040 },
|
||||||
|
{ c = colors.white, hex = 0xbfbfbf },
|
||||||
|
{ c = colors.lightGray, hex = 0x848794 },
|
||||||
|
{ c = colors.gray, hex = 0x5c5f68 },
|
||||||
|
{ c = colors.black, hex = 0x333333 },
|
||||||
|
{ c = colors.red_off, hex = 0x512d2d }
|
||||||
|
},
|
||||||
|
|
||||||
|
color_modes = {
|
||||||
|
-- standard
|
||||||
|
{},
|
||||||
|
-- deuteranopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x65aeff },
|
||||||
|
{ c = colors.green_hc, hex = 0x99c9ff },
|
||||||
|
{ c = colors.green_off, hex = 0x333333 },
|
||||||
|
{ c = colors.yellow, hex = 0xf7c311 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x333333 },
|
||||||
|
{ c = colors.red, hex = 0xf18486 },
|
||||||
|
{ c = colors.red_off, hex = 0x333333 }
|
||||||
|
},
|
||||||
|
-- protanopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x65aeff },
|
||||||
|
{ c = colors.green_hc, hex = 0x99c9ff },
|
||||||
|
{ c = colors.green_off, hex = 0x333333 },
|
||||||
|
{ c = colors.yellow, hex = 0xf5e633 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x333333 },
|
||||||
|
{ c = colors.red, hex = 0xff8058 },
|
||||||
|
{ c = colors.red_off, hex = 0x333333 }
|
||||||
|
},
|
||||||
|
-- tritanopia
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x00ecff },
|
||||||
|
{ c = colors.green_hc, hex = 0x00ecff },
|
||||||
|
{ c = colors.green_off, hex = 0x333333 },
|
||||||
|
{ c = colors.yellow, hex = 0xffbc00 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x333333 },
|
||||||
|
{ c = colors.red, hex = 0xdf4949 },
|
||||||
|
{ c = colors.red_off, hex = 0x333333 }
|
||||||
|
},
|
||||||
|
-- blue indicators
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x65aeff },
|
||||||
|
{ c = colors.green_hc, hex = 0x99c9ff },
|
||||||
|
{ c = colors.green_off, hex = 0x365e8a },
|
||||||
|
},
|
||||||
|
-- standard, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.green_off, hex = 0x333333 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x333333 },
|
||||||
|
{ c = colors.red_off, hex = 0x333333 }
|
||||||
|
},
|
||||||
|
-- blue indicators, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.green, hex = 0x65aeff },
|
||||||
|
{ c = colors.green_hc, hex = 0x99c9ff },
|
||||||
|
{ c = colors.green_off, hex = 0x333333 },
|
||||||
|
{ c = colors.yellow_off, hex = 0x333333 },
|
||||||
|
{ c = colors.red_off, hex = 0x333333 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- get style fields for a front panel based on the provided theme
|
||||||
|
---@param theme fp_theme
|
||||||
|
function themes.get_fp_style(theme)
|
||||||
|
---@class fp_style
|
||||||
|
local style = {
|
||||||
|
root = cpair(theme.text, theme.bg),
|
||||||
|
|
||||||
|
text = cpair(theme.text, theme.bg),
|
||||||
|
text_fg = cpair(theme.text, colors._INHERIT),
|
||||||
|
|
||||||
|
label_fg = cpair(theme.label, colors._INHERIT),
|
||||||
|
label_d_fg = cpair(theme.label_dark, colors._INHERIT),
|
||||||
|
|
||||||
|
disabled_fg = cpair(theme.disabled, colors._INHERIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Main UI Color Palettes
|
||||||
|
|
||||||
|
---@class ui_palette
|
||||||
|
themes.smooth_stone = {
|
||||||
|
colors = {
|
||||||
|
{ c = colors.red, hex = 0xdf4949 },
|
||||||
|
{ c = colors.orange, hex = 0xffb659 },
|
||||||
|
{ c = colors.yellow, hex = 0xfffc79 },
|
||||||
|
{ c = colors.lime, hex = 0x80ff80 },
|
||||||
|
{ c = colors.green, hex = 0x4aee8a },
|
||||||
|
{ c = colors.cyan, hex = 0x34bac8 },
|
||||||
|
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
||||||
|
{ c = colors.blue, hex = 0x0096ff },
|
||||||
|
{ c = colors.purple, hex = 0xb156ee },
|
||||||
|
{ c = colors.pink, hex = 0xf26ba2 },
|
||||||
|
{ c = colors.magenta, hex = 0xf9488a },
|
||||||
|
{ c = colors.white, hex = 0xf0f0f0 },
|
||||||
|
{ c = colors.lightGray, hex = 0xcacaca },
|
||||||
|
{ c = colors.gray, hex = 0x575757 },
|
||||||
|
{ c = colors.black, hex = 0x191919 },
|
||||||
|
{ c = colors.brown, hex = 0x7f664c }
|
||||||
|
},
|
||||||
|
|
||||||
|
-- color re-mappings for assistive modes
|
||||||
|
color_modes = {
|
||||||
|
-- standard
|
||||||
|
{},
|
||||||
|
-- deuteranopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x1081ff },
|
||||||
|
{ c = colors.yellow, hex = 0xf7c311 },
|
||||||
|
{ c = colors.red, hex = 0xfb5615 }
|
||||||
|
},
|
||||||
|
-- protanopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x1081ff },
|
||||||
|
{ c = colors.yellow, hex = 0xf5e633 },
|
||||||
|
{ c = colors.red, hex = 0xff521a }
|
||||||
|
},
|
||||||
|
-- tritanopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x40cbd7 },
|
||||||
|
{ c = colors.yellow, hex = 0xffbc00 },
|
||||||
|
{ c = colors.red, hex = 0xff0000 }
|
||||||
|
},
|
||||||
|
-- blue indicators
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x1081ff },
|
||||||
|
{ c = colors.yellow, hex = 0xfffc79 },
|
||||||
|
{ c = colors.red, hex = 0xdf4949 }
|
||||||
|
},
|
||||||
|
-- standard, black backgrounds
|
||||||
|
{},
|
||||||
|
-- blue indicators, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x1081ff },
|
||||||
|
{ c = colors.yellow, hex = 0xfffc79 },
|
||||||
|
{ c = colors.red, hex = 0xdf4949 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
---@type ui_palette
|
||||||
|
themes.deepslate = {
|
||||||
|
colors = {
|
||||||
|
{ c = colors.red, hex = 0xeb6a6c },
|
||||||
|
{ c = colors.orange, hex = 0xf2b86c },
|
||||||
|
{ c = colors.yellow, hex = 0xd9cf81 },
|
||||||
|
{ c = colors.lime, hex = 0x80ff80 },
|
||||||
|
{ c = colors.green, hex = 0x70e19b },
|
||||||
|
{ c = colors.cyan, hex = 0x7ccdd0 },
|
||||||
|
{ c = colors.lightBlue, hex = 0x99ceef },
|
||||||
|
{ c = colors.blue, hex = 0x60bcff },
|
||||||
|
{ c = colors.purple, hex = 0xc38aea },
|
||||||
|
{ c = colors.pink, hex = 0xff7fb8 },
|
||||||
|
{ c = colors.magenta, hex = 0xf980dd },
|
||||||
|
{ c = colors.white, hex = 0xd9d9d9 },
|
||||||
|
{ c = colors.lightGray, hex = 0x949494 },
|
||||||
|
{ c = colors.gray, hex = 0x575757 },
|
||||||
|
{ c = colors.black, hex = 0x262626 },
|
||||||
|
{ c = colors.brown, hex = 0xb18f6a }
|
||||||
|
},
|
||||||
|
|
||||||
|
-- color re-mappings for assistive modes
|
||||||
|
color_modes = {
|
||||||
|
-- standard
|
||||||
|
{},
|
||||||
|
-- deuteranopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x65aeff },
|
||||||
|
{ c = colors.yellow, hex = 0xf7c311 },
|
||||||
|
{ c = colors.red, hex = 0xfb5615 }
|
||||||
|
},
|
||||||
|
-- protanopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x65aeff },
|
||||||
|
{ c = colors.yellow, hex = 0xf5e633 },
|
||||||
|
{ c = colors.red, hex = 0xff8058 }
|
||||||
|
},
|
||||||
|
-- tritanopia
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x00ecff },
|
||||||
|
{ c = colors.yellow, hex = 0xffbc00 },
|
||||||
|
{ c = colors.red, hex = 0xdf4949 }
|
||||||
|
},
|
||||||
|
-- blue indicators
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x65aeff },
|
||||||
|
{ c = colors.yellow, hex = 0xd9cf81 },
|
||||||
|
{ c = colors.red, hex = 0xeb6a6c }
|
||||||
|
},
|
||||||
|
-- standard, black backgrounds
|
||||||
|
{},
|
||||||
|
-- blue indicators, black backgrounds
|
||||||
|
{
|
||||||
|
{ c = colors.blue, hex = 0x65aeff },
|
||||||
|
{ c = colors.yellow, hex = 0xd9cf81 },
|
||||||
|
{ c = colors.red, hex = 0xeb6a6c }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
return themes
|
||||||
@@ -7,6 +7,7 @@ local tcd = require("scada-common.tcd")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
@@ -41,21 +42,7 @@ 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.colors = {
|
style.colors = themes.smooth_stone.colors
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
@@ -112,7 +99,7 @@ local fields = {
|
|||||||
{ "AuthKey", "Facility Auth Key" , ""},
|
{ "AuthKey", "Facility Auth Key" , ""},
|
||||||
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
||||||
{ "LogPath", "Log Path", "/log.txt" },
|
{ "LogPath", "Log Path", "/log.txt" },
|
||||||
{ "LogDebug","Log Debug Messages", false }
|
{ "LogDebug", "Log Debug Messages", false }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- load data from the settings file
|
-- load data from the settings file
|
||||||
|
|||||||
@@ -2,19 +2,20 @@
|
|||||||
-- I/O Control for Pocket Integration with Supervisor & Coordinator
|
-- I/O Control for Pocket Integration with Supervisor & Coordinator
|
||||||
--
|
--
|
||||||
|
|
||||||
local psil = require("scada-common.psil")
|
local log = require("scada-common.log")
|
||||||
|
local psil = require("scada-common.psil")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
|
local ALARM_STATE = types.ALARM_STATE
|
||||||
|
|
||||||
|
---@todo nominal trip time is ping (0ms to 10ms usually)
|
||||||
|
local WARN_TT = 40
|
||||||
|
local HIGH_TT = 80
|
||||||
|
|
||||||
local iocontrol = {}
|
local iocontrol = {}
|
||||||
|
|
||||||
---@class pocket_ioctl
|
|
||||||
local io = {
|
|
||||||
ps = psil.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
---@enum POCKET_LINK_STATE
|
---@enum POCKET_LINK_STATE
|
||||||
local LINK_STATE = {
|
local LINK_STATE = {
|
||||||
UNLINKED = 0,
|
UNLINKED = 0,
|
||||||
@@ -23,23 +24,203 @@ local LINK_STATE = {
|
|||||||
LINKED = 3
|
LINKED = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum NAV_PAGE
|
iocontrol.LINK_STATE = LINK_STATE
|
||||||
local NAV_PAGE = {
|
|
||||||
HOME = 1,
|
---@enum POCKET_APP_ID
|
||||||
|
local APP_ID = {
|
||||||
|
ROOT = 1,
|
||||||
|
-- main app page
|
||||||
UNITS = 2,
|
UNITS = 2,
|
||||||
REACTORS = 3,
|
ABOUT = 3,
|
||||||
BOILERS = 4,
|
-- diag app page
|
||||||
TURBINES = 5,
|
ALARMS = 4,
|
||||||
DIAG = 6,
|
-- other
|
||||||
D_ALARMS = 7
|
DUMMY = 5,
|
||||||
|
NUM_APPS = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
iocontrol.LINK_STATE = LINK_STATE
|
iocontrol.APP_ID = APP_ID
|
||||||
iocontrol.NAV_PAGE = NAV_PAGE
|
|
||||||
|
---@class pocket_ioctl
|
||||||
|
local io = {
|
||||||
|
version = "unknown",
|
||||||
|
ps = psil.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
---@class nav_tree_page
|
||||||
|
---@field _p nav_tree_page|nil page's parent
|
||||||
|
---@field _c table page's children
|
||||||
|
---@field nav_to function function to navigate to this page
|
||||||
|
---@field switcher function|nil function to switch between children
|
||||||
|
---@field tasks table tasks to run while viewing this page
|
||||||
|
|
||||||
|
-- allocate the page navigation system
|
||||||
|
function iocontrol.alloc_nav()
|
||||||
|
local self = {
|
||||||
|
pane = nil, ---@type graphics_element
|
||||||
|
apps = {},
|
||||||
|
containers = {},
|
||||||
|
cur_app = APP_ID.ROOT
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cur_page = self.root
|
||||||
|
|
||||||
|
---@class pocket_nav
|
||||||
|
io.nav = {}
|
||||||
|
|
||||||
|
-- set the root pane element to switch between apps with
|
||||||
|
---@param root_pane graphics_element
|
||||||
|
function io.nav.set_pane(root_pane)
|
||||||
|
self.pane = root_pane
|
||||||
|
end
|
||||||
|
|
||||||
|
function io.nav.set_sidebar(sidebar)
|
||||||
|
self.sidebar = sidebar
|
||||||
|
end
|
||||||
|
|
||||||
|
-- register an app
|
||||||
|
---@param app_id POCKET_APP_ID app ID
|
||||||
|
---@param container graphics_element element that contains this app (usually a Div)
|
||||||
|
---@param pane graphics_element? multipane if this is a simple paned app, then nav_to must be a number
|
||||||
|
function io.nav.register_app(app_id, container, pane)
|
||||||
|
---@class pocket_app
|
||||||
|
local app = {
|
||||||
|
loaded = false,
|
||||||
|
load = nil,
|
||||||
|
cur_page = nil, ---@type nav_tree_page
|
||||||
|
pane = pane,
|
||||||
|
paned_pages = {},
|
||||||
|
sidebar_items = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.load = function () app.loaded = true end
|
||||||
|
|
||||||
|
-- delayed set of the pane if it wasn't ready at the start
|
||||||
|
---@param root_pane graphics_element multipane
|
||||||
|
function app.set_root_pane(root_pane)
|
||||||
|
app.pane = root_pane
|
||||||
|
end
|
||||||
|
|
||||||
|
function app.set_sidebar(items)
|
||||||
|
app.sidebar_items = items
|
||||||
|
if self.sidebar then self.sidebar.update(items) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- function to run on initial load into memory
|
||||||
|
---@param on_load function callback
|
||||||
|
function app.set_on_load(on_load)
|
||||||
|
app.load = function ()
|
||||||
|
on_load()
|
||||||
|
app.loaded = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if a pane was provided, this will switch between numbered pages
|
||||||
|
---@param idx integer page index
|
||||||
|
function app.switcher(idx)
|
||||||
|
if app.paned_pages[idx] then
|
||||||
|
app.paned_pages[idx].nav_to()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create a new page entry in the app's page navigation tree
|
||||||
|
---@param parent nav_tree_page? a parent page or nil to set this as the root
|
||||||
|
---@param nav_to function|integer function to navigate to this page or pane index
|
||||||
|
---@return nav_tree_page new_page this new page
|
||||||
|
function app.new_page(parent, nav_to)
|
||||||
|
---@type nav_tree_page
|
||||||
|
local page = { _p = parent, _c = {}, nav_to = function () end, switcher = function () end, tasks = {} }
|
||||||
|
|
||||||
|
if parent == nil and app.cur_page == nil then
|
||||||
|
app.cur_page = page
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(nav_to) == "number" then
|
||||||
|
app.paned_pages[nav_to] = page
|
||||||
|
|
||||||
|
function page.nav_to()
|
||||||
|
app.cur_page = page
|
||||||
|
if app.pane then app.pane.set_value(nav_to) end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
function page.nav_to()
|
||||||
|
app.cur_page = page
|
||||||
|
nav_to()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- switch between children
|
||||||
|
---@param id integer child ID
|
||||||
|
function page.switcher(id) if page._c[id] then page._c[id].nav_to() end end
|
||||||
|
|
||||||
|
if parent ~= nil then
|
||||||
|
table.insert(page._p._c, page)
|
||||||
|
end
|
||||||
|
|
||||||
|
return page
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the currently active page
|
||||||
|
function app.get_current_page() return app.cur_page end
|
||||||
|
|
||||||
|
-- attempt to navigate up the tree
|
||||||
|
---@return boolean success true if successfully navigated up
|
||||||
|
function app.nav_up()
|
||||||
|
local parent = app.cur_page._p
|
||||||
|
if parent then parent.nav_to() end
|
||||||
|
return parent ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
self.apps[app_id] = app
|
||||||
|
self.containers[app_id] = container
|
||||||
|
|
||||||
|
return app
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get a list of the app containers (usually Div elements)
|
||||||
|
function io.nav.get_containers() return self.containers end
|
||||||
|
|
||||||
|
-- open a given app
|
||||||
|
---@param app_id POCKET_APP_ID
|
||||||
|
function io.nav.open_app(app_id)
|
||||||
|
local app = self.apps[app_id] ---@type pocket_app
|
||||||
|
if app then
|
||||||
|
if not app.loaded then app.load() end
|
||||||
|
|
||||||
|
self.cur_app = app_id
|
||||||
|
self.pane.set_value(app_id)
|
||||||
|
|
||||||
|
if #app.sidebar_items > 0 then
|
||||||
|
self.sidebar.update(app.sidebar_items)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("tried to open unknown app")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get the currently active page
|
||||||
|
---@return nav_tree_page
|
||||||
|
function io.nav.get_current_page()
|
||||||
|
return self.apps[self.cur_app].get_current_page()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- attempt to navigate up
|
||||||
|
function io.nav.nav_up()
|
||||||
|
local app = self.apps[self.cur_app] ---@type pocket_app
|
||||||
|
log.debug("attempting app nav up for app " .. self.cur_app)
|
||||||
|
|
||||||
|
if not app.nav_up() then
|
||||||
|
log.debug("internal app nav up failed, going to home screen")
|
||||||
|
io.nav.open_app(APP_ID.ROOT)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- initialize facility-independent components of pocket iocontrol
|
-- initialize facility-independent components of pocket iocontrol
|
||||||
---@param comms pocket_comms
|
---@param comms pocket_comms
|
||||||
function iocontrol.init_core(comms)
|
function iocontrol.init_core(comms)
|
||||||
|
iocontrol.alloc_nav()
|
||||||
|
|
||||||
---@class pocket_ioctl_diag
|
---@class pocket_ioctl_diag
|
||||||
io.diag = {}
|
io.diag = {}
|
||||||
|
|
||||||
@@ -77,28 +258,514 @@ function iocontrol.init_core(comms)
|
|||||||
tone_indicators = {} -- indicators to update from supervisor tone states
|
tone_indicators = {} -- indicators to update from supervisor tone states
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class pocket_nav
|
-- API access
|
||||||
io.nav = {
|
---@class pocket_ioctl_api
|
||||||
page = NAV_PAGE.HOME, ---@type NAV_PAGE
|
io.api = {
|
||||||
sub_pages = { NAV_PAGE.HOME, NAV_PAGE.UNITS, NAV_PAGE.REACTORS, NAV_PAGE.BOILERS, NAV_PAGE.TURBINES, NAV_PAGE.DIAG },
|
get_unit = function (unit) comms.api__get_unit(unit) end
|
||||||
tasks = {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- add a task to be performed periodically while on a given page
|
|
||||||
---@param page NAV_PAGE page to add task to
|
|
||||||
---@param task function function to execute
|
|
||||||
function io.nav.register_task(page, task)
|
|
||||||
if io.nav.tasks[page] == nil then io.nav.tasks[page] = {} end
|
|
||||||
table.insert(io.nav.tasks[page], task)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- initialize facility-dependent components of pocket iocontrol
|
-- initialize facility-dependent components of pocket iocontrol
|
||||||
function iocontrol.init_fac() end
|
---@param conf facility_conf configuration
|
||||||
|
---@param temp_scale 1|2|3|4 temperature unit (1 = K, 2 = C, 3 = F, 4 = R)
|
||||||
|
function iocontrol.init_fac(conf, temp_scale)
|
||||||
|
-- temperature unit label and conversion function (from Kelvin)
|
||||||
|
if temp_scale == 2 then
|
||||||
|
io.temp_label = "\xb0C"
|
||||||
|
io.temp_convert = function (t) return t - 273.15 end
|
||||||
|
elseif temp_scale == 3 then
|
||||||
|
io.temp_label = "\xb0F"
|
||||||
|
io.temp_convert = function (t) return (1.8 * (t - 273.15)) + 32 end
|
||||||
|
elseif temp_scale == 4 then
|
||||||
|
io.temp_label = "\xb0R"
|
||||||
|
io.temp_convert = function (t) return 1.8 * t end
|
||||||
|
else
|
||||||
|
io.temp_label = "K"
|
||||||
|
io.temp_convert = function (t) return t end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- facility data structure
|
||||||
|
---@class pioctl_facility
|
||||||
|
io.facility = {
|
||||||
|
num_units = conf.num_units,
|
||||||
|
tank_mode = conf.cooling.fac_tank_mode,
|
||||||
|
tank_defs = conf.cooling.fac_tank_defs,
|
||||||
|
all_sys_ok = false,
|
||||||
|
rtu_count = 0,
|
||||||
|
|
||||||
|
auto_ready = false,
|
||||||
|
auto_active = false,
|
||||||
|
auto_ramping = false,
|
||||||
|
auto_saturated = false,
|
||||||
|
|
||||||
|
auto_scram = false,
|
||||||
|
---@type ascram_status
|
||||||
|
ascram_status = {
|
||||||
|
matrix_dc = false,
|
||||||
|
matrix_fill = false,
|
||||||
|
crit_alarm = false,
|
||||||
|
radiation = false,
|
||||||
|
gen_fault = false
|
||||||
|
},
|
||||||
|
|
||||||
|
---@type WASTE_PRODUCT
|
||||||
|
auto_current_waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
|
auto_pu_fallback_active = false,
|
||||||
|
|
||||||
|
radiation = types.new_zero_radiation_reading(),
|
||||||
|
|
||||||
|
ps = psil.create(),
|
||||||
|
|
||||||
|
induction_ps_tbl = {},
|
||||||
|
induction_data_tbl = {},
|
||||||
|
|
||||||
|
sps_ps_tbl = {},
|
||||||
|
sps_data_tbl = {},
|
||||||
|
|
||||||
|
tank_ps_tbl = {},
|
||||||
|
tank_data_tbl = {},
|
||||||
|
|
||||||
|
env_d_ps = psil.create(),
|
||||||
|
env_d_data = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- create induction and SPS tables (currently only 1 of each is supported)
|
||||||
|
table.insert(io.facility.induction_ps_tbl, psil.create())
|
||||||
|
table.insert(io.facility.induction_data_tbl, {})
|
||||||
|
table.insert(io.facility.sps_ps_tbl, psil.create())
|
||||||
|
table.insert(io.facility.sps_data_tbl, {})
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
for i = 1, conf.num_units do
|
||||||
|
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) }
|
||||||
|
else
|
||||||
|
-- decode the layout of tanks from the connections definitions
|
||||||
|
local tank_mode = io.facility.tank_mode
|
||||||
|
local tank_defs = io.facility.tank_defs
|
||||||
|
local tank_list = { table.unpack(tank_defs) }
|
||||||
|
|
||||||
|
local function calc_fdef(start_idx, end_idx)
|
||||||
|
local first = 4
|
||||||
|
for i = start_idx, end_idx do
|
||||||
|
if io.facility.tank_defs[i] == 2 then
|
||||||
|
if i < first then first = i end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return first
|
||||||
|
end
|
||||||
|
|
||||||
|
if tank_mode == 1 then
|
||||||
|
-- (1) 1 total facility tank (A A A A)
|
||||||
|
local first_fdef = calc_fdef(1, #tank_defs)
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if i > first_fdef and tank_defs[i] == 2 then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 2 then
|
||||||
|
-- (2) 2 total facility tanks (A A A B)
|
||||||
|
local first_fdef = calc_fdef(1, math.min(3, #tank_defs))
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if (i ~= 4) and (i > first_fdef) and (tank_defs[i] == 2) then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 3 then
|
||||||
|
-- (3) 2 total facility tanks (A A B B)
|
||||||
|
for _, a in pairs({ 1, 3 }) do
|
||||||
|
local b = a + 1
|
||||||
|
if (tank_defs[a] == 2) and (tank_defs[b] == 2) then
|
||||||
|
tank_list[b] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 4 then
|
||||||
|
-- (4) 2 total facility tanks (A B B B)
|
||||||
|
local first_fdef = calc_fdef(2, #tank_defs)
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if (i ~= 1) and (i > first_fdef) and (tank_defs[i] == 2) then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 5 then
|
||||||
|
-- (5) 3 total facility tanks (A A B C)
|
||||||
|
local first_fdef = calc_fdef(1, math.min(2, #tank_defs))
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if (not (i == 3 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 6 then
|
||||||
|
-- (6) 3 total facility tanks (A B B C)
|
||||||
|
local first_fdef = calc_fdef(2, math.min(3, #tank_defs))
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if (not (i == 1 or i == 4)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif tank_mode == 7 then
|
||||||
|
-- (7) 3 total facility tanks (A B C C)
|
||||||
|
local first_fdef = calc_fdef(3, #tank_defs)
|
||||||
|
for i = 1, #tank_defs do
|
||||||
|
if (not (i == 1 or i == 2)) and (i > first_fdef) and (tank_defs[i] == 2) then
|
||||||
|
tank_list[i] = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
io.facility.tank_list = tank_list
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create facility tank tables
|
||||||
|
for i = 1, #io.facility.tank_list do
|
||||||
|
if io.facility.tank_list[i] == 2 then
|
||||||
|
table.insert(io.facility.tank_ps_tbl, psil.create())
|
||||||
|
table.insert(io.facility.tank_data_tbl, {})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create unit data structures
|
||||||
|
io.units = {}
|
||||||
|
for i = 1, conf.num_units do
|
||||||
|
---@class pioctl_unit
|
||||||
|
local entry = {
|
||||||
|
unit_id = i,
|
||||||
|
connected = false,
|
||||||
|
rtu_hw = {},
|
||||||
|
|
||||||
|
num_boilers = 0,
|
||||||
|
num_turbines = 0,
|
||||||
|
num_snas = 0,
|
||||||
|
has_tank = conf.cooling.r_cool[i].TankConnection,
|
||||||
|
|
||||||
|
control_state = false,
|
||||||
|
burn_rate_cmd = 0.0,
|
||||||
|
radiation = types.new_zero_radiation_reading(),
|
||||||
|
|
||||||
|
sna_peak_rate = 0.0,
|
||||||
|
sna_max_rate = 0.0,
|
||||||
|
sna_out_rate = 0.0,
|
||||||
|
|
||||||
|
waste_mode = types.WASTE_MODE.MANUAL_PLUTONIUM,
|
||||||
|
waste_product = types.WASTE_PRODUCT.PLUTONIUM,
|
||||||
|
|
||||||
|
-- auto control group
|
||||||
|
a_group = 0,
|
||||||
|
|
||||||
|
---@type alarms
|
||||||
|
alarms = { ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE, ALARM_STATE.INACTIVE },
|
||||||
|
|
||||||
|
annunciator = {}, ---@type annunciator
|
||||||
|
|
||||||
|
unit_ps = psil.create(),
|
||||||
|
reactor_data = {}, ---@type reactor_db
|
||||||
|
|
||||||
|
boiler_ps_tbl = {},
|
||||||
|
boiler_data_tbl = {},
|
||||||
|
|
||||||
|
turbine_ps_tbl = {},
|
||||||
|
turbine_data_tbl = {},
|
||||||
|
|
||||||
|
tank_ps_tbl = {},
|
||||||
|
tank_data_tbl = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- on other facility modes, overwrite unit TANK option with facility tank defs
|
||||||
|
if io.facility.tank_mode ~= 0 then
|
||||||
|
entry.has_tank = conf.cooling.fac_tank_defs[i] > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create boiler tables
|
||||||
|
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].TurbineCount do
|
||||||
|
table.insert(entry.turbine_ps_tbl, psil.create())
|
||||||
|
table.insert(entry.turbine_data_tbl, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- create tank tables
|
||||||
|
if io.facility.tank_defs[i] == 1 then
|
||||||
|
table.insert(entry.tank_ps_tbl, psil.create())
|
||||||
|
table.insert(entry.tank_data_tbl, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
entry.num_boilers = #entry.boiler_data_tbl
|
||||||
|
entry.num_turbines = #entry.turbine_data_tbl
|
||||||
|
|
||||||
|
table.insert(io.units, entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- set network link state
|
-- set network link state
|
||||||
---@param state POCKET_LINK_STATE
|
---@param state POCKET_LINK_STATE
|
||||||
function iocontrol.report_link_state(state) io.ps.publish("link_state", state) end
|
---@param sv_addr integer? supervisor address if linked
|
||||||
|
---@param api_addr integer? coordinator address if linked
|
||||||
|
function iocontrol.report_link_state(state, sv_addr, api_addr)
|
||||||
|
io.ps.publish("link_state", state)
|
||||||
|
|
||||||
|
if state == LINK_STATE.API_LINK_ONLY or state == LINK_STATE.UNLINKED then
|
||||||
|
io.ps.publish("svr_conn_quality", 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == LINK_STATE.SV_LINK_ONLY or state == LINK_STATE.UNLINKED then
|
||||||
|
io.ps.publish("crd_conn_quality", 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
if state == LINK_STATE.LINKED then
|
||||||
|
io.ps.publish("sv_addr", sv_addr)
|
||||||
|
io.ps.publish("api_addr", api_addr)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- determine supervisor connection quality (trip time)
|
||||||
|
---@param trip_time integer
|
||||||
|
function iocontrol.report_svr_tt(trip_time)
|
||||||
|
local state = 3
|
||||||
|
if trip_time > HIGH_TT then
|
||||||
|
state = 1
|
||||||
|
elseif trip_time > WARN_TT then
|
||||||
|
state = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
io.ps.publish("svr_conn_quality", state)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- determine coordinator connection quality (trip time)
|
||||||
|
---@param trip_time integer
|
||||||
|
function iocontrol.report_crd_tt(trip_time)
|
||||||
|
local state = 3
|
||||||
|
if trip_time > HIGH_TT then
|
||||||
|
state = 1
|
||||||
|
elseif trip_time > WARN_TT then
|
||||||
|
state = 2
|
||||||
|
end
|
||||||
|
|
||||||
|
io.ps.publish("crd_conn_quality", state)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- populate facility data from API_GET_FAC
|
||||||
|
---@param data table
|
||||||
|
---@return boolean valid
|
||||||
|
function iocontrol.record_facility_data(data)
|
||||||
|
local valid = true
|
||||||
|
|
||||||
|
local fac = io.facility
|
||||||
|
|
||||||
|
fac.all_sys_ok = data[1]
|
||||||
|
fac.rtu_count = data[2]
|
||||||
|
fac.radiation = data[3]
|
||||||
|
|
||||||
|
-- auto control
|
||||||
|
if type(data[4]) == "table" and #data[4] == 4 then
|
||||||
|
fac.auto_ready = data[4][1]
|
||||||
|
fac.auto_active = data[4][2]
|
||||||
|
fac.auto_ramping = data[4][3]
|
||||||
|
fac.auto_saturated = data[4][4]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- waste
|
||||||
|
if type(data[5]) == "table" and #data[5] == 2 then
|
||||||
|
fac.auto_current_waste_product = data[5][1]
|
||||||
|
fac.auto_pu_fallback_active = data[5][2]
|
||||||
|
end
|
||||||
|
|
||||||
|
fac.num_tanks = data[6]
|
||||||
|
fac.has_imatrix = data[7]
|
||||||
|
fac.has_sps = data[8]
|
||||||
|
|
||||||
|
return valid
|
||||||
|
end
|
||||||
|
|
||||||
|
-- update unit status data from API_GET_UNIT
|
||||||
|
---@param data table
|
||||||
|
function iocontrol.record_unit_data(data)
|
||||||
|
if type(data[1]) == "number" and io.units[data[1]] then
|
||||||
|
local unit = io.units[data[1]] ---@type pioctl_unit
|
||||||
|
|
||||||
|
unit.connected = data[2]
|
||||||
|
unit.rtu_hw = data[3]
|
||||||
|
unit.alarms = data[4]
|
||||||
|
|
||||||
|
--#region Annunciator
|
||||||
|
|
||||||
|
unit.annunciator = data[5]
|
||||||
|
|
||||||
|
local rcs_disconn, rcs_warn, rcs_hazard = false, false, false
|
||||||
|
|
||||||
|
for key, val in pairs(unit.annunciator) do
|
||||||
|
if key == "BoilerOnline" or key == "TurbineOnline" then
|
||||||
|
-- split up online arrays
|
||||||
|
local every = true
|
||||||
|
for id = 1, #val do
|
||||||
|
every = every and val[id]
|
||||||
|
unit.boiler_ps_tbl[id].publish(key, val[id])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not every then rcs_disconn = true end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_" .. key, every)
|
||||||
|
elseif key == "HeatingRateLow" or key == "WaterLevelLow" then
|
||||||
|
-- split up array for all boilers
|
||||||
|
local any = false
|
||||||
|
for id = 1, #val do
|
||||||
|
any = any or val[id]
|
||||||
|
unit.boiler_ps_tbl[id].publish(key, val[id])
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == "HeatingRateLow" and any then
|
||||||
|
rcs_warn = true
|
||||||
|
elseif key == "WaterLevelLow" and any then
|
||||||
|
rcs_hazard = true
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_" .. key, any)
|
||||||
|
elseif key == "SteamDumpOpen" or key == "TurbineOverSpeed" or key == "GeneratorTrip" or key == "TurbineTrip" then
|
||||||
|
-- split up array for all turbines
|
||||||
|
local any = false
|
||||||
|
for id = 1, #val do
|
||||||
|
any = any or val[id]
|
||||||
|
unit.turbine_ps_tbl[id].publish(key, val[id])
|
||||||
|
end
|
||||||
|
|
||||||
|
if key == "GeneratorTrip" and any then
|
||||||
|
rcs_warn = true
|
||||||
|
elseif (key == "TurbineOverSpeed" or key == "TurbineTrip") and any then
|
||||||
|
rcs_hazard = true
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_" .. key, any)
|
||||||
|
else
|
||||||
|
-- non-table fields
|
||||||
|
unit.unit_ps.publish(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local anc = unit.annunciator
|
||||||
|
rcs_hazard = rcs_hazard or anc.RCPTrip
|
||||||
|
rcs_warn = rcs_warn or anc.RCSFlowLow or anc.CoolantLevelLow or anc.RCSFault or anc.MaxWaterReturnFeed or
|
||||||
|
anc.CoolantFeedMismatch or anc.BoilRateMismatch or anc.SteamFeedMismatch or anc.MaxWaterReturnFeed
|
||||||
|
|
||||||
|
local rcs_status = 4
|
||||||
|
if rcs_hazard then
|
||||||
|
rcs_status = 2
|
||||||
|
elseif rcs_warn then
|
||||||
|
rcs_status = 3
|
||||||
|
elseif rcs_disconn then
|
||||||
|
rcs_status = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_RCS", rcs_status)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Reactor Data
|
||||||
|
|
||||||
|
unit.reactor_data = data[6]
|
||||||
|
|
||||||
|
local control_status = 1
|
||||||
|
local reactor_status = 1
|
||||||
|
local rps_status = 1
|
||||||
|
|
||||||
|
if unit.connected then
|
||||||
|
-- update RPS status
|
||||||
|
if unit.reactor_data.rps_tripped then
|
||||||
|
control_status = 2
|
||||||
|
rps_status = util.trinary(unit.reactor_data.rps_trip_cause == "manual", 3, 2)
|
||||||
|
else rps_status = 4 end
|
||||||
|
|
||||||
|
-- update reactor/control status
|
||||||
|
if unit.reactor_data.mek_status.status then
|
||||||
|
reactor_status = 4
|
||||||
|
control_status = util.trinary(unit.annunciator.AutoControl, 4, 3)
|
||||||
|
else
|
||||||
|
if unit.reactor_data.no_reactor then
|
||||||
|
reactor_status = 2
|
||||||
|
elseif not unit.reactor_data.formed or unit.reactor_data.rps_status.force_dis then
|
||||||
|
reactor_status = 3
|
||||||
|
else
|
||||||
|
reactor_status = 4
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for key, val in pairs(unit.reactor_data) do
|
||||||
|
if key ~= "rps_status" and key ~= "mek_struct" and key ~= "mek_status" then
|
||||||
|
unit.unit_ps.publish(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(unit.reactor_data.rps_status) == "table" then
|
||||||
|
for key, val in pairs(unit.reactor_data.rps_status) do
|
||||||
|
unit.unit_ps.publish(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(unit.reactor_data.mek_status) == "table" then
|
||||||
|
for key, val in pairs(unit.reactor_data.mek_status) do
|
||||||
|
unit.unit_ps.publish(key, val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.unit_ps.publish("U_ControlStatus", control_status)
|
||||||
|
unit.unit_ps.publish("U_ReactorStatus", reactor_status)
|
||||||
|
unit.unit_ps.publish("U_RPS", rps_status)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
unit.boiler_data_tbl = data[7]
|
||||||
|
|
||||||
|
for id = 1, #unit.boiler_data_tbl do
|
||||||
|
local boiler = unit.boiler_data_tbl[id] ---@type boilerv_session_db
|
||||||
|
local ps = unit.boiler_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
|
local boiler_status = 1
|
||||||
|
|
||||||
|
if unit.rtu_hw.boilers[id].connected then
|
||||||
|
if unit.rtu_hw.boilers[id].faulted then
|
||||||
|
boiler_status = 3
|
||||||
|
elseif boiler.formed then
|
||||||
|
boiler_status = 4
|
||||||
|
else
|
||||||
|
boiler_status = 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ps.publish("BoilerStatus", boiler_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.turbine_data_tbl = data[8]
|
||||||
|
|
||||||
|
for id = 1, #unit.turbine_data_tbl do
|
||||||
|
local turbine = unit.turbine_data_tbl[id] ---@type turbinev_session_db
|
||||||
|
local ps = unit.turbine_ps_tbl[id] ---@type psil
|
||||||
|
|
||||||
|
local turbine_status = 1
|
||||||
|
|
||||||
|
if unit.rtu_hw.turbines[id].connected then
|
||||||
|
if unit.rtu_hw.turbines[id].faulted then
|
||||||
|
turbine_status = 3
|
||||||
|
elseif turbine.formed then
|
||||||
|
turbine_status = 4
|
||||||
|
else
|
||||||
|
turbine_status = 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ps.publish("TurbineStatus", turbine_status)
|
||||||
|
end
|
||||||
|
|
||||||
|
unit.tank_data_tbl = data[9]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- get the IO controller database
|
-- get the IO controller database
|
||||||
function iocontrol.get_db() return io end
|
function iocontrol.get_db() return io end
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local PROTOCOL = comms.PROTOCOL
|
|||||||
local DEVICE_TYPE = comms.DEVICE_TYPE
|
local DEVICE_TYPE = comms.DEVICE_TYPE
|
||||||
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
||||||
local MGMT_TYPE = comms.MGMT_TYPE
|
local MGMT_TYPE = comms.MGMT_TYPE
|
||||||
|
local CRDN_TYPE = comms.CRDN_TYPE
|
||||||
|
|
||||||
local LINK_STATE = iocontrol.LINK_STATE
|
local LINK_STATE = iocontrol.LINK_STATE
|
||||||
|
|
||||||
@@ -118,6 +119,20 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.api.seq_num = self.api.seq_num + 1
|
self.api.seq_num = self.api.seq_num + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- send an API packet to the coordinator
|
||||||
|
---@param msg_type CRDN_TYPE
|
||||||
|
---@param msg table
|
||||||
|
local function _send_api(msg_type, msg)
|
||||||
|
local s_pkt = comms.scada_packet()
|
||||||
|
local pkt = comms.crdn_packet()
|
||||||
|
|
||||||
|
pkt.make(msg_type, msg)
|
||||||
|
s_pkt.make(self.api.addr, self.api.seq_num, PROTOCOL.SCADA_CRDN, pkt.raw_sendable())
|
||||||
|
|
||||||
|
nic.transmit(config.CRD_Channel, config.PKT_Channel, s_pkt)
|
||||||
|
self.api.seq_num = self.api.seq_num + 1
|
||||||
|
end
|
||||||
|
|
||||||
-- attempt supervisor connection establishment
|
-- attempt supervisor connection establishment
|
||||||
local function _send_sv_establish()
|
local function _send_sv_establish()
|
||||||
_send_sv(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
|
_send_sv(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
|
||||||
@@ -125,7 +140,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
|
|
||||||
-- attempt coordinator API connection establishment
|
-- attempt coordinator API connection establishment
|
||||||
local function _send_api_establish()
|
local function _send_api_establish()
|
||||||
_send_crd(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT })
|
_send_crd(MGMT_TYPE.ESTABLISH, { comms.version, version, DEVICE_TYPE.PKT, comms.api_version })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- keep alive ack to supervisor
|
-- keep alive ack to supervisor
|
||||||
@@ -191,7 +206,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- linked, all good!
|
-- linked, all good!
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED)
|
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -214,6 +229,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_ALARM_SET, { id, state }) end
|
if self.sv.linked then _send_sv(MGMT_TYPE.DIAG_ALARM_SET, { id, state }) end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- coordinator get unit data
|
||||||
|
function public.api__get_unit(unit)
|
||||||
|
if self.api.linked then _send_api(CRDN_TYPE.API_GET_UNIT, { unit }) end
|
||||||
|
end
|
||||||
|
|
||||||
-- parse a packet
|
-- parse a packet
|
||||||
---@param side string
|
---@param side string
|
||||||
---@param sender integer
|
---@param sender integer
|
||||||
@@ -246,6 +266,25 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
return pkt
|
return pkt
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param packet mgmt_frame|crdn_frame
|
||||||
|
---@param length integer
|
||||||
|
---@param max integer?
|
||||||
|
---@return boolean
|
||||||
|
local function _check_length(packet, length, max)
|
||||||
|
local ok = util.trinary(max == nil, packet.length == length, packet.length >= length and packet.length <= (max or 0))
|
||||||
|
if not ok then
|
||||||
|
local fmt = "[comms] RX_PACKET{r_chan=%d,proto=%d,type=%d}: packet length mismatch -> expect %d != actual %d"
|
||||||
|
log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type))
|
||||||
|
end
|
||||||
|
return ok
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param packet mgmt_frame|crdn_frame
|
||||||
|
local function _fail_type(packet)
|
||||||
|
local fmt = "[comms] RX_PACKET{r_chan=%d,proto=%d,type=%d}: unrecognized packet type"
|
||||||
|
log.debug(util.sprintf(fmt, packet.scada_frame.remote_channel(), packet.scada_frame.protocol(), packet.type))
|
||||||
|
end
|
||||||
|
|
||||||
-- handle a packet
|
-- handle a packet
|
||||||
---@param packet mgmt_frame|crdn_frame|nil
|
---@param packet mgmt_frame|crdn_frame|nil
|
||||||
function public.handle_packet(packet)
|
function public.handle_packet(packet)
|
||||||
@@ -268,7 +307,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
return
|
return
|
||||||
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
elseif self.api.linked and (src_addr ~= self.api.addr) then
|
||||||
log.debug("received packet from unknown computer " .. src_addr .. " while linked (API expected " .. self.api.addr ..
|
log.debug("received packet from unknown computer " .. src_addr .. " while linked (API expected " .. self.api.addr ..
|
||||||
"); channel in use by another system?")
|
"); channel in use by another system?")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
self.api.r_seq_num = packet.scada_frame.seq_num()
|
self.api.r_seq_num = packet.scada_frame.seq_num()
|
||||||
@@ -277,12 +316,27 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
-- feed watchdog on valid sequence number
|
-- feed watchdog on valid sequence number
|
||||||
api_watchdog.feed()
|
api_watchdog.feed()
|
||||||
|
|
||||||
if protocol == PROTOCOL.SCADA_MGMT then
|
if protocol == PROTOCOL.SCADA_CRDN then
|
||||||
|
---@cast packet crdn_frame
|
||||||
|
if self.api.linked then
|
||||||
|
if packet.type == CRDN_TYPE.API_GET_FAC then
|
||||||
|
if _check_length(packet, 11) then
|
||||||
|
iocontrol.record_facility_data(packet.data)
|
||||||
|
end
|
||||||
|
elseif packet.type == CRDN_TYPE.API_GET_UNIT then
|
||||||
|
if _check_length(packet, 9) then
|
||||||
|
iocontrol.record_unit_data(packet.data)
|
||||||
|
end
|
||||||
|
else _fail_type(packet) end
|
||||||
|
else
|
||||||
|
log.debug("discarding coordinator SCADA_CRDN packet before linked")
|
||||||
|
end
|
||||||
|
elseif protocol == PROTOCOL.SCADA_MGMT then
|
||||||
---@cast packet mgmt_frame
|
---@cast packet mgmt_frame
|
||||||
if self.api.linked then
|
if self.api.linked then
|
||||||
if packet.type == MGMT_TYPE.KEEP_ALIVE then
|
if packet.type == MGMT_TYPE.KEEP_ALIVE then
|
||||||
-- keep alive request received, echo back
|
-- keep alive request received, echo back
|
||||||
if packet.length == 1 then
|
if _check_length(packet, 1) then
|
||||||
local timestamp = packet.data[1]
|
local timestamp = packet.data[1]
|
||||||
local trip_time = util.time() - timestamp
|
local trip_time = util.time() - timestamp
|
||||||
|
|
||||||
@@ -290,11 +344,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
log.warning("pocket coordinator KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)")
|
log.warning("pocket coordinator KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- log.debug("pocket coordinator RTT = " .. trip_time .. "ms")
|
-- log.debug("pocket coordinator TT = " .. trip_time .. "ms")
|
||||||
|
|
||||||
_send_api_keep_alive_ack(timestamp)
|
_send_api_keep_alive_ack(timestamp)
|
||||||
else
|
|
||||||
log.debug("coordinator SCADA keep alive packet length mismatch")
|
iocontrol.report_crd_tt(trip_time)
|
||||||
end
|
end
|
||||||
elseif packet.type == MGMT_TYPE.CLOSE then
|
elseif packet.type == MGMT_TYPE.CLOSE then
|
||||||
-- handle session close
|
-- handle session close
|
||||||
@@ -303,24 +357,38 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.api.r_seq_num = nil
|
self.api.r_seq_num = nil
|
||||||
self.api.addr = comms.BROADCAST
|
self.api.addr = comms.BROADCAST
|
||||||
log.info("coordinator server connection closed by remote host")
|
log.info("coordinator server connection closed by remote host")
|
||||||
else
|
else _fail_type(packet) end
|
||||||
log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from coordinator")
|
|
||||||
end
|
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
-- connection with coordinator established
|
-- connection with coordinator established
|
||||||
if packet.length == 1 then
|
if _check_length(packet, 1, 2) then
|
||||||
local est_ack = packet.data[1]
|
local est_ack = packet.data[1]
|
||||||
|
|
||||||
if est_ack == ESTABLISH_ACK.ALLOW then
|
if est_ack == ESTABLISH_ACK.ALLOW then
|
||||||
log.info("coordinator connection established")
|
if packet.length == 2 then
|
||||||
self.establish_delay_counter = 0
|
local fac_config = packet.data[2]
|
||||||
self.api.linked = true
|
|
||||||
self.api.addr = src_addr
|
|
||||||
|
|
||||||
if self.sv.linked then
|
if type(fac_config) == "table" and #fac_config == 2 then
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED)
|
-- get configuration
|
||||||
|
local conf = { num_units = fac_config[1], cooling = fac_config[2] }
|
||||||
|
|
||||||
|
---@todo unit options
|
||||||
|
iocontrol.init_fac(conf, 1)
|
||||||
|
|
||||||
|
log.info("coordinator connection established")
|
||||||
|
self.establish_delay_counter = 0
|
||||||
|
self.api.linked = true
|
||||||
|
self.api.addr = src_addr
|
||||||
|
|
||||||
|
if self.sv.linked then
|
||||||
|
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
||||||
|
else
|
||||||
|
iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug("invalid facility configuration table received from coordinator, establish failed")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
iocontrol.report_link_state(LINK_STATE.API_LINK_ONLY)
|
log.debug("received coordinator establish allow without facility configuration")
|
||||||
end
|
end
|
||||||
elseif est_ack == ESTABLISH_ACK.DENY then
|
elseif est_ack == ESTABLISH_ACK.DENY then
|
||||||
if self.api.last_est_ack ~= est_ack then
|
if self.api.last_est_ack ~= est_ack then
|
||||||
@@ -334,13 +402,15 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
if self.api.last_est_ack ~= est_ack then
|
if self.api.last_est_ack ~= est_ack then
|
||||||
log.info("coordinator comms version mismatch")
|
log.info("coordinator comms version mismatch")
|
||||||
end
|
end
|
||||||
|
elseif est_ack == ESTABLISH_ACK.BAD_API_VERSION then
|
||||||
|
if self.api.last_est_ack ~= est_ack then
|
||||||
|
log.info("coordinator api version mismatch")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug("coordinator SCADA_MGMT establish packet reply unsupported")
|
log.debug("coordinator SCADA_MGMT establish packet reply unsupported")
|
||||||
end
|
end
|
||||||
|
|
||||||
self.api.last_est_ack = est_ack
|
self.api.last_est_ack = est_ack
|
||||||
else
|
|
||||||
log.debug("coordinator SCADA_MGMT establish packet length mismatch")
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("discarding coordinator non-link SCADA_MGMT packet before linked")
|
log.debug("discarding coordinator non-link SCADA_MGMT packet before linked")
|
||||||
@@ -372,7 +442,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
if self.sv.linked then
|
if self.sv.linked then
|
||||||
if packet.type == MGMT_TYPE.KEEP_ALIVE then
|
if packet.type == MGMT_TYPE.KEEP_ALIVE then
|
||||||
-- keep alive request received, echo back
|
-- keep alive request received, echo back
|
||||||
if packet.length == 1 then
|
if _check_length(packet, 1) then
|
||||||
local timestamp = packet.data[1]
|
local timestamp = packet.data[1]
|
||||||
local trip_time = util.time() - timestamp
|
local trip_time = util.time() - timestamp
|
||||||
|
|
||||||
@@ -380,11 +450,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
log.warning("pocket supervisor KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)")
|
log.warning("pocket supervisor KEEP_ALIVE trip time > 750ms (" .. trip_time .. "ms)")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- log.debug("pocket supervisor RTT = " .. trip_time .. "ms")
|
-- log.debug("pocket supervisor TT = " .. trip_time .. "ms")
|
||||||
|
|
||||||
_send_sv_keep_alive_ack(timestamp)
|
_send_sv_keep_alive_ack(timestamp)
|
||||||
else
|
|
||||||
log.debug("supervisor SCADA keep alive packet length mismatch")
|
iocontrol.report_svr_tt(trip_time)
|
||||||
end
|
end
|
||||||
elseif packet.type == MGMT_TYPE.CLOSE then
|
elseif packet.type == MGMT_TYPE.CLOSE then
|
||||||
-- handle session close
|
-- handle session close
|
||||||
@@ -394,12 +464,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.sv.addr = comms.BROADCAST
|
self.sv.addr = comms.BROADCAST
|
||||||
log.info("supervisor server connection closed by remote host")
|
log.info("supervisor server connection closed by remote host")
|
||||||
elseif packet.type == MGMT_TYPE.DIAG_TONE_GET then
|
elseif packet.type == MGMT_TYPE.DIAG_TONE_GET then
|
||||||
if packet.length == 8 then
|
if _check_length(packet, 8) then
|
||||||
for i = 1, #packet.data do
|
for i = 1, #packet.data do
|
||||||
diag.tone_test.tone_indicators[i].update(packet.data[i] == true)
|
diag.tone_test.tone_indicators[i].update(packet.data[i] == true)
|
||||||
end
|
end
|
||||||
else
|
|
||||||
log.debug("supervisor SCADA diag alarm states packet length mismatch")
|
|
||||||
end
|
end
|
||||||
elseif packet.type == MGMT_TYPE.DIAG_TONE_SET then
|
elseif packet.type == MGMT_TYPE.DIAG_TONE_SET then
|
||||||
if packet.length == 1 and packet.data[1] == false then
|
if packet.length == 1 and packet.data[1] == false then
|
||||||
@@ -438,12 +506,10 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
else
|
else
|
||||||
log.debug("supervisor SCADA diag alarm set packet length/type mismatch")
|
log.debug("supervisor SCADA diag alarm set packet length/type mismatch")
|
||||||
end
|
end
|
||||||
else
|
else _fail_type(packet) end
|
||||||
log.debug("received unknown SCADA_MGMT packet type " .. packet.type .. " from supervisor")
|
|
||||||
end
|
|
||||||
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
elseif packet.type == MGMT_TYPE.ESTABLISH then
|
||||||
-- connection with supervisor established
|
-- connection with supervisor established
|
||||||
if packet.length == 1 then
|
if _check_length(packet, 1) then
|
||||||
local est_ack = packet.data[1]
|
local est_ack = packet.data[1]
|
||||||
|
|
||||||
if est_ack == ESTABLISH_ACK.ALLOW then
|
if est_ack == ESTABLISH_ACK.ALLOW then
|
||||||
@@ -453,7 +519,7 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
self.sv.addr = src_addr
|
self.sv.addr = src_addr
|
||||||
|
|
||||||
if self.api.linked then
|
if self.api.linked then
|
||||||
iocontrol.report_link_state(LINK_STATE.LINKED)
|
iocontrol.report_link_state(LINK_STATE.LINKED, self.sv.addr, self.api.addr)
|
||||||
else
|
else
|
||||||
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY)
|
iocontrol.report_link_state(LINK_STATE.SV_LINK_ONLY)
|
||||||
end
|
end
|
||||||
@@ -474,15 +540,11 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.sv.last_est_ack = est_ack
|
self.sv.last_est_ack = est_ack
|
||||||
else
|
|
||||||
log.debug("supervisor SCADA_MGMT establish packet length mismatch")
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
log.debug("discarding supervisor non-link SCADA_MGMT packet before linked")
|
log.debug("discarding supervisor non-link SCADA_MGMT packet before linked")
|
||||||
end
|
end
|
||||||
else
|
else _fail_type(packet) end
|
||||||
log.debug("illegal packet type " .. protocol .. " from supervisor", true)
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
log.debug("received packet from unconfigured channel " .. r_chan, true)
|
log.debug("received packet from unconfigured channel " .. r_chan, true)
|
||||||
end
|
end
|
||||||
@@ -500,5 +562,4 @@ function pocket.comms(version, nic, sv_watchdog, api_watchdog)
|
|||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
return pocket
|
return pocket
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ local iocontrol = require("pocket.iocontrol")
|
|||||||
local pocket = require("pocket.pocket")
|
local pocket = require("pocket.pocket")
|
||||||
local renderer = require("pocket.renderer")
|
local renderer = require("pocket.renderer")
|
||||||
|
|
||||||
local POCKET_VERSION = "v0.7.0-alpha"
|
local POCKET_VERSION = "v0.9.0-alpha"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@@ -31,9 +31,13 @@ if not pocket.load_config() then
|
|||||||
-- try to reconfigure (user action)
|
-- try to reconfigure (user action)
|
||||||
local success, error = configure.configure(true)
|
local success, error = configure.configure(true)
|
||||||
if success then
|
if success then
|
||||||
assert(pocket.load_config(), "failed to load valid configuration")
|
if not pocket.load_config() then
|
||||||
|
println("failed to load a valid configuration, please reconfigure")
|
||||||
|
return
|
||||||
|
end
|
||||||
else
|
else
|
||||||
assert(success, "pocket configuration error: " .. error)
|
println("configuration error: " .. error)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -50,6 +54,7 @@ log.info("BOOTING pocket.startup " .. POCKET_VERSION)
|
|||||||
log.info("========================================")
|
log.info("========================================")
|
||||||
|
|
||||||
crash.set_env("pocket", POCKET_VERSION)
|
crash.set_env("pocket", POCKET_VERSION)
|
||||||
|
crash.dbg_log_env()
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main application
|
-- main application
|
||||||
@@ -63,6 +68,9 @@ local function main()
|
|||||||
-- mount connected devices
|
-- mount connected devices
|
||||||
ppm.mount_all()
|
ppm.mount_all()
|
||||||
|
|
||||||
|
-- record version for GUI
|
||||||
|
iocontrol.get_db().version = POCKET_VERSION
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- setup communications & clocks
|
-- setup communications & clocks
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
@@ -126,7 +134,7 @@ local function main()
|
|||||||
-- start connection watchdogs
|
-- start connection watchdogs
|
||||||
conn_wd.sv.feed()
|
conn_wd.sv.feed()
|
||||||
conn_wd.api.feed()
|
conn_wd.api.feed()
|
||||||
log.debug("startup> conn watchdog started")
|
log.debug("startup> conn watchdogs started")
|
||||||
|
|
||||||
local io_db = iocontrol.get_db()
|
local io_db = iocontrol.get_db()
|
||||||
local nav = io_db.nav
|
local nav = io_db.nav
|
||||||
@@ -144,11 +152,8 @@ local function main()
|
|||||||
pocket_comms.link_update()
|
pocket_comms.link_update()
|
||||||
|
|
||||||
-- update any tasks for the active page
|
-- update any tasks for the active page
|
||||||
if (type(nav.tasks[nav.page]) == "table") then
|
local page_tasks = nav.get_current_page().tasks
|
||||||
for i = 1, #nav.tasks[nav.page] do
|
for i = 1, #page_tasks do page_tasks[i]() end
|
||||||
nav.tasks[nav.page][i]()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
loop_clock.start()
|
loop_clock.start()
|
||||||
elseif conn_wd.sv.is_timer(param1) then
|
elseif conn_wd.sv.is_timer(param1) then
|
||||||
|
|||||||
@@ -1,58 +1,39 @@
|
|||||||
|
--
|
||||||
|
-- Diagnostic Apps
|
||||||
|
--
|
||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
local MultiPane = require("graphics.elements.multipane")
|
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
local IndicatorLight = require("graphics.elements.indicators.light")
|
local IndicatorLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
local App = require("graphics.elements.controls.app")
|
|
||||||
local Checkbox = require("graphics.elements.controls.checkbox")
|
local Checkbox = require("graphics.elements.controls.checkbox")
|
||||||
local PushButton = require("graphics.elements.controls.push_button")
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
local SwitchButton = require("graphics.elements.controls.switch_button")
|
local SwitchButton = require("graphics.elements.controls.switch_button")
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local NAV_PAGE = iocontrol.NAV_PAGE
|
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
-- new diagnostics page view
|
-- create diagnostic app pages
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
local function new_view(root)
|
local function create_pages(root)
|
||||||
local db = iocontrol.get_db()
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
|
||||||
|
|
||||||
local diag_home = Div{parent=main,x=1,y=1}
|
|
||||||
|
|
||||||
TextBox{parent=diag_home,text="Diagnostic Apps",x=1,y=2,height=1,alignment=ALIGN.CENTER}
|
|
||||||
|
|
||||||
local alarm_test = Div{parent=main,x=1,y=1}
|
|
||||||
|
|
||||||
local panes = { diag_home, alarm_test }
|
|
||||||
|
|
||||||
local page_pane = MultiPane{parent=main,x=1,y=1,panes=panes}
|
|
||||||
|
|
||||||
local function navigate_diag()
|
|
||||||
page_pane.set_value(1)
|
|
||||||
db.nav.page = NAV_PAGE.DIAG
|
|
||||||
db.nav.sub_pages[NAV_PAGE.DIAG] = NAV_PAGE.DIAG
|
|
||||||
end
|
|
||||||
|
|
||||||
local function navigate_alarm()
|
|
||||||
page_pane.set_value(2)
|
|
||||||
db.nav.page = NAV_PAGE.D_ALARMS
|
|
||||||
db.nav.sub_pages[NAV_PAGE.DIAG] = NAV_PAGE.D_ALARMS
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
-- Alarm Testing Page --
|
-- Alarm Testing Page --
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
db.nav.register_task(NAV_PAGE.D_ALARMS, db.diag.tone_test.get_tone_states)
|
local alarm_test = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
local alarm_app = db.nav.register_app(iocontrol.APP_ID.ALARMS, alarm_test)
|
||||||
|
|
||||||
|
local page = alarm_app.new_page(nil, function () end)
|
||||||
|
page.tasks = { db.diag.tone_test.get_tone_states }
|
||||||
|
|
||||||
local ttest = db.diag.tone_test
|
local ttest = db.diag.tone_test
|
||||||
|
|
||||||
@@ -67,8 +48,6 @@ local function new_view(root)
|
|||||||
|
|
||||||
ttest.ready_warn = TextBox{parent=audio,y=2,text="",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.yellow,colors.black)}
|
ttest.ready_warn = TextBox{parent=audio,y=2,text="",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
PushButton{parent=audio,x=13,y=18,text="\x11 BACK",min_width=8,fg_bg=cpair(colors.black,colors.lightGray),active_fg_bg=c_wht_gray,callback=navigate_diag}
|
|
||||||
|
|
||||||
local tones = Div{parent=audio,x=2,y=3,height=10,width=8,fg_bg=cpair(colors.black,colors.yellow)}
|
local tones = Div{parent=audio,x=2,y=3,height=10,width=8,fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
TextBox{parent=tones,text="Tones",height=1,alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
TextBox{parent=tones,text="Tones",height=1,alignment=ALIGN.CENTER,fg_bg=audio.get_fg_bg()}
|
||||||
@@ -132,16 +111,6 @@ local function new_view(root)
|
|||||||
local t_8 = IndicatorLight{parent=states,x=6,label="8",colors=c_blue_gray}
|
local t_8 = IndicatorLight{parent=states,x=6,label="8",colors=c_blue_gray}
|
||||||
|
|
||||||
ttest.tone_indicators = { t_1, t_2, t_3, t_4, t_5, t_6, t_7, t_8 }
|
ttest.tone_indicators = { t_1, t_2, t_3, t_4, t_5, t_6, t_7, t_8 }
|
||||||
|
|
||||||
--------------
|
|
||||||
-- App List --
|
|
||||||
--------------
|
|
||||||
|
|
||||||
App{parent=diag_home,x=3,y=4,text="\x0f",title="Alarm",callback=navigate_alarm,app_fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.white,colors.gray)}
|
|
||||||
App{parent=diag_home,x=10,y=4,text="\x1e",title="LoopT",callback=function()end,app_fg_bg=cpair(colors.black,colors.cyan)}
|
|
||||||
App{parent=diag_home,x=17,y=4,text="@",title="Comps",callback=function()end,app_fg_bg=cpair(colors.black,colors.orange)}
|
|
||||||
|
|
||||||
return main
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return new_view
|
return create_pages
|
||||||
26
pocket/ui/apps/dummy_app.lua
Normal file
26
pocket/ui/apps/dummy_app.lua
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
--
|
||||||
|
-- Placeholder App
|
||||||
|
--
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
-- create placeholder app page
|
||||||
|
---@param root graphics_element parent
|
||||||
|
local function create_pages(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
local main = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
db.nav.register_app(iocontrol.APP_ID.DUMMY, main).new_page(nil, function () end)
|
||||||
|
|
||||||
|
TextBox{parent=main,text="This app is not implemented yet.",x=1,y=2,alignment=core.ALIGN.CENTER}
|
||||||
|
|
||||||
|
TextBox{parent=main,text=" pretend something cool is here \x03",x=1,y=10,alignment=core.ALIGN.CENTER,fg_bg=core.cpair(colors.gray,colors.black)}
|
||||||
|
end
|
||||||
|
|
||||||
|
return create_pages
|
||||||
145
pocket/ui/apps/sys_apps.lua
Normal file
145
pocket/ui/apps/sys_apps.lua
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
--
|
||||||
|
-- System Apps
|
||||||
|
--
|
||||||
|
|
||||||
|
local comms = require("scada-common.comms")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local lockbox = require("lockbox")
|
||||||
|
|
||||||
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
local pocket = require("pocket.pocket")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local ListBox = require("graphics.elements.listbox")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
|
-- create system app pages
|
||||||
|
---@param root graphics_element parent
|
||||||
|
local function create_pages(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
|
----------------
|
||||||
|
-- About Page --
|
||||||
|
----------------
|
||||||
|
|
||||||
|
local about_root = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
|
local about_app = db.nav.register_app(iocontrol.APP_ID.ABOUT, about_root)
|
||||||
|
|
||||||
|
local about_page = about_app.new_page(nil, 1)
|
||||||
|
local nt_page = about_app.new_page(about_page, 2)
|
||||||
|
local fw_page = about_app.new_page(about_page, 3)
|
||||||
|
local hw_page = about_app.new_page(about_page, 4)
|
||||||
|
|
||||||
|
local about = Div{parent=about_root,x=1,y=2}
|
||||||
|
|
||||||
|
TextBox{parent=about,y=1,text="System Information",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local btn_fg_bg = cpair(colors.lightBlue, colors.black)
|
||||||
|
local btn_active = cpair(colors.white, colors.black)
|
||||||
|
local label = cpair(colors.lightGray, colors.black)
|
||||||
|
|
||||||
|
PushButton{parent=about,x=2,y=3,text="Network >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=nt_page.nav_to}
|
||||||
|
PushButton{parent=about,x=2,y=4,text="Firmware >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=fw_page.nav_to}
|
||||||
|
PushButton{parent=about,x=2,y=5,text="Host Details >",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=hw_page.nav_to}
|
||||||
|
|
||||||
|
--#region Network Details
|
||||||
|
|
||||||
|
local config = pocket.config
|
||||||
|
|
||||||
|
local nt_div = Div{parent=about_root,x=1,y=2}
|
||||||
|
TextBox{parent=nt_div,y=1,text="Network Details",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
PushButton{parent=nt_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
|
TextBox{parent=nt_div,x=2,y=3,text="Pocket Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
---@diagnostic disable-next-line: undefined-field
|
||||||
|
TextBox{parent=nt_div,x=2,text=util.c(os.getComputerID(),":",config.PKT_Channel),height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
nt_div.line_break()
|
||||||
|
TextBox{parent=nt_div,x=2,text="Supervisor Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
local sv = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
nt_div.line_break()
|
||||||
|
TextBox{parent=nt_div,x=2,text="Coordinator Address",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
local coord = TextBox{parent=nt_div,x=2,text="",height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
sv.register(db.ps, "sv_addr", function (addr) sv.set_value(util.c(addr, ":", config.SVR_Channel)) end)
|
||||||
|
coord.register(db.ps, "api_addr", function (addr) coord.set_value(util.c(addr, ":", config.CRD_Channel)) end)
|
||||||
|
|
||||||
|
nt_div.line_break()
|
||||||
|
TextBox{parent=nt_div,x=2,text="Message Authentication",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
local auth = util.trinary(type(config.AuthKey) == "string" and string.len(config.AuthKey) > 0, "HMAC-MD5", "None")
|
||||||
|
TextBox{parent=nt_div,x=2,text=auth,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Firmware Versions
|
||||||
|
|
||||||
|
local fw_div = Div{parent=about_root,x=1,y=2}
|
||||||
|
TextBox{parent=fw_div,y=1,text="Firmware Versions",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
PushButton{parent=fw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
|
local fw_list_box = ListBox{parent=fw_div,x=1,y=3,scroll_height=100,nav_fg_bg=cpair(colors.lightGray,colors.gray),nav_active=cpair(colors.white,colors.gray)}
|
||||||
|
|
||||||
|
local fw_list = Div{parent=fw_list_box,x=1,y=2,height=18}
|
||||||
|
|
||||||
|
TextBox{parent=fw_list,x=2,text="Pocket Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=db.version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
fw_list.line_break()
|
||||||
|
TextBox{parent=fw_list,x=2,text="Comms Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=comms.version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
fw_list.line_break()
|
||||||
|
TextBox{parent=fw_list,x=2,text="API Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=comms.api_version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
fw_list.line_break()
|
||||||
|
TextBox{parent=fw_list,x=2,text="Common Lib Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=util.version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
fw_list.line_break()
|
||||||
|
TextBox{parent=fw_list,x=2,text="Graphics Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=core.version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
fw_list.line_break()
|
||||||
|
TextBox{parent=fw_list,x=2,text="Lockbox Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=fw_list,x=2,text=lockbox.version,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Host Versions
|
||||||
|
|
||||||
|
local hw_div = Div{parent=about_root,x=1,y=2}
|
||||||
|
TextBox{parent=hw_div,y=1,text="Host Versions",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
PushButton{parent=hw_div,x=2,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=about_page.nav_to}
|
||||||
|
|
||||||
|
hw_div.line_break()
|
||||||
|
TextBox{parent=hw_div,x=2,text="Lua Version",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=hw_div,x=2,text=_VERSION,height=1,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
hw_div.line_break()
|
||||||
|
TextBox{parent=hw_div,x=2,text="Environment",height=1,alignment=ALIGN.LEFT,fg_bg=label}
|
||||||
|
TextBox{parent=hw_div,x=2,text=_HOST,height=6,alignment=ALIGN.LEFT}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
local root_pane = MultiPane{parent=about_root,x=1,y=1,panes={about,nt_div,fw_div,hw_div}}
|
||||||
|
|
||||||
|
about_app.set_root_pane(root_pane)
|
||||||
|
end
|
||||||
|
|
||||||
|
return create_pages
|
||||||
@@ -4,27 +4,29 @@
|
|||||||
|
|
||||||
local iocontrol = require("pocket.iocontrol")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
local style = require("pocket.ui.style")
|
local diag_apps = require("pocket.ui.apps.diag_apps")
|
||||||
|
local dummy_app = require("pocket.ui.apps.dummy_app")
|
||||||
|
local sys_apps = require("pocket.ui.apps.sys_apps")
|
||||||
|
|
||||||
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
local conn_waiting = require("pocket.ui.components.conn_waiting")
|
||||||
|
|
||||||
local boiler_page = require("pocket.ui.pages.boiler_page")
|
|
||||||
local diag_page = require("pocket.ui.pages.diag_page")
|
|
||||||
local home_page = require("pocket.ui.pages.home_page")
|
local home_page = require("pocket.ui.pages.home_page")
|
||||||
local reactor_page = require("pocket.ui.pages.reactor_page")
|
|
||||||
local turbine_page = require("pocket.ui.pages.turbine_page")
|
|
||||||
local unit_page = require("pocket.ui.pages.unit_page")
|
local unit_page = require("pocket.ui.pages.unit_page")
|
||||||
|
|
||||||
|
local style = require("pocket.ui.style")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
local MultiPane = require("graphics.elements.multipane")
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
local Sidebar = require("graphics.elements.controls.sidebar")
|
local Sidebar = require("graphics.elements.controls.sidebar")
|
||||||
|
|
||||||
|
local SignalBar = require("graphics.elements.indicators.signal")
|
||||||
|
|
||||||
local LINK_STATE = iocontrol.LINK_STATE
|
local LINK_STATE = iocontrol.LINK_STATE
|
||||||
local NAV_PAGE = iocontrol.NAV_PAGE
|
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
@@ -33,26 +35,27 @@ local cpair = core.cpair
|
|||||||
-- create new main view
|
-- create new main view
|
||||||
---@param main graphics_element main displaybox
|
---@param main graphics_element main displaybox
|
||||||
local function init(main)
|
local function init(main)
|
||||||
local nav = iocontrol.get_db().nav
|
local db = iocontrol.get_db()
|
||||||
local ps = iocontrol.get_db().ps
|
|
||||||
|
|
||||||
-- window header message
|
-- window header message
|
||||||
TextBox{parent=main,y=1,text="",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
TextBox{parent=main,y=1,text="WIP ALPHA APP S C ",alignment=ALIGN.LEFT,height=1,fg_bg=style.header}
|
||||||
|
local svr_conn = SignalBar{parent=main,y=1,x=22,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
|
local crd_conn = SignalBar{parent=main,y=1,x=26,compact=true,colors_low_med=cpair(colors.red,colors.yellow),disconnect_color=colors.lightGray,fg_bg=cpair(colors.green,colors.gray)}
|
||||||
|
|
||||||
--
|
db.ps.subscribe("svr_conn_quality", svr_conn.set_value)
|
||||||
-- root panel panes (connection screens + main screen)
|
db.ps.subscribe("crd_conn_quality", crd_conn.set_value)
|
||||||
--
|
|
||||||
|
--#region root panel panes (connection screens + main screen)
|
||||||
|
|
||||||
local root_pane_div = Div{parent=main,x=1,y=2}
|
local root_pane_div = Div{parent=main,x=1,y=2}
|
||||||
|
|
||||||
local conn_sv_wait = conn_waiting(root_pane_div, 6, false)
|
local conn_sv_wait = conn_waiting(root_pane_div, 6, false)
|
||||||
local conn_api_wait = conn_waiting(root_pane_div, 6, true)
|
local conn_api_wait = conn_waiting(root_pane_div, 6, true)
|
||||||
local main_pane = Div{parent=main,x=1,y=2}
|
local main_pane = Div{parent=main,x=1,y=2}
|
||||||
local root_panes = { conn_sv_wait, conn_api_wait, main_pane }
|
|
||||||
|
|
||||||
local root_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes=root_panes}
|
local root_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={conn_sv_wait,conn_api_wait,main_pane}}
|
||||||
|
|
||||||
root_pane.register(ps, "link_state", function (state)
|
root_pane.register(db.ps, "link_state", function (state)
|
||||||
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
if state == LINK_STATE.UNLINKED or state == LINK_STATE.API_LINK_ONLY then
|
||||||
root_pane.set_value(1)
|
root_pane.set_value(1)
|
||||||
elseif state == LINK_STATE.SV_LINK_ONLY then
|
elseif state == LINK_STATE.SV_LINK_ONLY then
|
||||||
@@ -62,62 +65,29 @@ local function init(main)
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--
|
--#endregion
|
||||||
-- main page panel panes & sidebar
|
|
||||||
--
|
--#region main page panel panes & sidebar
|
||||||
|
|
||||||
local page_div = Div{parent=main_pane,x=4,y=1}
|
local page_div = Div{parent=main_pane,x=4,y=1}
|
||||||
|
|
||||||
local sidebar_tabs = {
|
home_page(page_div)
|
||||||
{
|
unit_page(page_div)
|
||||||
char = "#",
|
|
||||||
color = cpair(colors.black,colors.green)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
char = "U",
|
|
||||||
color = cpair(colors.black,colors.yellow)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
char = "R",
|
|
||||||
color = cpair(colors.black,colors.cyan)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
char = "B",
|
|
||||||
color = cpair(colors.black,colors.lightGray)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
char = "T",
|
|
||||||
color = cpair(colors.black,colors.white)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
char = "D",
|
|
||||||
color = cpair(colors.black,colors.orange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local panes = { home_page(page_div), unit_page(page_div), reactor_page(page_div), boiler_page(page_div), turbine_page(page_div), diag_page(page_div) }
|
diag_apps(page_div)
|
||||||
|
sys_apps(page_div)
|
||||||
|
dummy_app(page_div)
|
||||||
|
|
||||||
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
assert(#db.nav.get_containers() == iocontrol.APP_ID.NUM_APPS, "app IDs were not sequential or some apps weren't registered")
|
||||||
|
|
||||||
local function navigate_sidebar(page)
|
db.nav.set_pane(MultiPane{parent=page_div,x=1,y=1,panes=db.nav.get_containers()})
|
||||||
if page == 1 then
|
db.nav.set_sidebar(Sidebar{parent=main_pane,x=1,y=1,height=18,fg_bg=cpair(colors.white,colors.gray)})
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.HOME]
|
|
||||||
elseif page == 2 then
|
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.UNITS]
|
|
||||||
elseif page == 3 then
|
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.REACTORS]
|
|
||||||
elseif page == 4 then
|
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.BOILERS]
|
|
||||||
elseif page == 5 then
|
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.TURBINES]
|
|
||||||
elseif page == 6 then
|
|
||||||
nav.page = nav.sub_pages[NAV_PAGE.DIAG]
|
|
||||||
end
|
|
||||||
|
|
||||||
page_pane.set_value(page)
|
PushButton{parent=main_pane,x=1,y=19,text="\x1b",min_width=3,fg_bg=cpair(colors.white,colors.gray),active_fg_bg=cpair(colors.gray,colors.black),callback=db.nav.nav_up}
|
||||||
end
|
|
||||||
|
|
||||||
Sidebar{parent=main_pane,x=1,y=1,tabs=sidebar_tabs,fg_bg=cpair(colors.white,colors.gray),callback=navigate_sidebar}
|
db.nav.open_app(iocontrol.APP_ID.ROOT)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
end
|
end
|
||||||
|
|
||||||
return init
|
return init
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
-- local style = require("pocket.ui.style")
|
|
||||||
|
|
||||||
local core = require("graphics.core")
|
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
|
||||||
local TextBox = require("graphics.elements.textbox")
|
|
||||||
|
|
||||||
-- local cpair = core.cpair
|
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
|
||||||
|
|
||||||
-- new boiler page view
|
|
||||||
---@param root graphics_element parent
|
|
||||||
local function new_view(root)
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
|
||||||
|
|
||||||
TextBox{parent=main,text="BOILERS",x=1,y=1,height=1,alignment=ALIGN.CENTER}
|
|
||||||
|
|
||||||
return main
|
|
||||||
end
|
|
||||||
|
|
||||||
return new_view
|
|
||||||
@@ -1,21 +1,64 @@
|
|||||||
local core = require("graphics.core")
|
--
|
||||||
|
-- Main Home Page
|
||||||
|
--
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
|
|
||||||
local App = require("graphics.elements.controls.app")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local AppMultiPane = require("graphics.elements.appmultipane")
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local App = require("graphics.elements.controls.app")
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local APP_ID = iocontrol.APP_ID
|
||||||
|
|
||||||
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
-- new home page view
|
-- new home page view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
local function new_view(root)
|
local function new_view(root)
|
||||||
local main = Div{parent=root,x=1,y=1}
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
App{parent=main,x=3,y=2,text="\x17",title="PRC",callback=function()end,app_fg_bg=cpair(colors.black,colors.purple)}
|
local main = Div{parent=root,x=1,y=1,height=19}
|
||||||
App{parent=main,x=10,y=2,text="\x15",title="CTL",callback=function()end,app_fg_bg=cpair(colors.black,colors.green)}
|
|
||||||
App{parent=main,x=17,y=2,text="\x08",title="DEV",callback=function()end,app_fg_bg=cpair(colors.black,colors.lightGray)}
|
local app = db.nav.register_app(iocontrol.APP_ID.ROOT, main)
|
||||||
App{parent=main,x=3,y=7,text="\x7f",title="Waste",callback=function()end,app_fg_bg=cpair(colors.black,colors.brown)}
|
|
||||||
App{parent=main,x=10,y=7,text="\xb6",title="Guide",callback=function()end,app_fg_bg=cpair(colors.black,colors.cyan)}
|
local apps_1 = Div{parent=main,x=1,y=1,height=15}
|
||||||
|
local apps_2 = Div{parent=main,x=1,y=1,height=15}
|
||||||
|
|
||||||
|
local panes = { apps_1, apps_2 }
|
||||||
|
|
||||||
|
local app_pane = AppMultiPane{parent=main,x=1,y=1,height=18,panes=panes,active_color=colors.lightGray,nav_colors=cpair(colors.lightGray,colors.gray),scroll_nav=true,drag_nav=true,callback=app.switcher}
|
||||||
|
|
||||||
|
app.set_root_pane(app_pane)
|
||||||
|
app.new_page(app.new_page(nil, 1), 2)
|
||||||
|
|
||||||
|
local function open(id) db.nav.open_app(id) end
|
||||||
|
|
||||||
|
app.set_sidebar({
|
||||||
|
{ label = " #\x10", tall = true, color = core.cpair(colors.black, colors.green), callback = function () open(APP_ID.ROOT) end }
|
||||||
|
})
|
||||||
|
|
||||||
|
local active_fg_bg = cpair(colors.white,colors.gray)
|
||||||
|
|
||||||
|
App{parent=apps_1,x=2,y=2,text="U",title="Units",callback=function()open(APP_ID.UNITS)end,app_fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=9,y=2,text="F",title="Facil",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.orange),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=16,y=2,text="\x15",title="Control",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.green),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=2,y=7,text="\x17",title="Process",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.purple),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=9,y=7,text="\x7f",title="Waste",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.brown),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=16,y=7,text="\x08",title="Devices",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.lightGray),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=2,y=12,text="\xb6",title="Guide",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_1,x=9,y=12,text="?",title="About",callback=function()open(APP_ID.ABOUT)end,app_fg_bg=cpair(colors.black,colors.white),active_fg_bg=active_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=apps_2,text="Diagnostic Apps",x=1,y=2,height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
App{parent=apps_2,x=2,y=4,text="\x0f",title="Alarm",callback=function()open(APP_ID.ALARMS)end,app_fg_bg=cpair(colors.black,colors.red),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_2,x=9,y=4,text="\x1e",title="LoopT",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.cyan),active_fg_bg=active_fg_bg}
|
||||||
|
App{parent=apps_2,x=16,y=4,text="@",title="Comps",callback=function()open(APP_ID.DUMMY)end,app_fg_bg=cpair(colors.black,colors.orange),active_fg_bg=active_fg_bg}
|
||||||
|
|
||||||
return main
|
return main
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
-- local style = require("pocket.ui.style")
|
|
||||||
|
|
||||||
local core = require("graphics.core")
|
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
|
||||||
local TextBox = require("graphics.elements.textbox")
|
|
||||||
|
|
||||||
-- local cpair = core.cpair
|
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
|
||||||
|
|
||||||
-- new reactor page view
|
|
||||||
---@param root graphics_element parent
|
|
||||||
local function new_view(root)
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
|
||||||
|
|
||||||
TextBox{parent=main,text="REACTOR",x=1,y=1,height=1,alignment=ALIGN.CENTER}
|
|
||||||
|
|
||||||
return main
|
|
||||||
end
|
|
||||||
|
|
||||||
return new_view
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
-- local style = require("pocket.ui.style")
|
|
||||||
|
|
||||||
local core = require("graphics.core")
|
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
|
||||||
local TextBox = require("graphics.elements.textbox")
|
|
||||||
|
|
||||||
-- local cpair = core.cpair
|
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
|
||||||
|
|
||||||
-- new turbine page view
|
|
||||||
---@param root graphics_element parent
|
|
||||||
local function new_view(root)
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
|
||||||
|
|
||||||
TextBox{parent=main,text="TURBINES",x=1,y=1,height=1,alignment=ALIGN.CENTER}
|
|
||||||
|
|
||||||
return main
|
|
||||||
end
|
|
||||||
|
|
||||||
return new_view
|
|
||||||
@@ -1,20 +1,319 @@
|
|||||||
-- local style = require("pocket.ui.style")
|
--
|
||||||
|
-- Unit Overview Page
|
||||||
|
--
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local util = require("scada-common.util")
|
||||||
|
-- local log = require("scada-common.log")
|
||||||
|
|
||||||
local Div = require("graphics.elements.div")
|
local iocontrol = require("pocket.iocontrol")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
|
||||||
|
|
||||||
-- local cpair = core.cpair
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local MultiPane = require("graphics.elements.multipane")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
|
local IconIndicator = require("graphics.elements.indicators.icon")
|
||||||
|
-- local RadIndicator = require("graphics.elements.indicators.rad")
|
||||||
|
-- local VerticalBar = require("graphics.elements.indicators.vbar")
|
||||||
|
|
||||||
|
local PushButton = require("graphics.elements.controls.push_button")
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
local cpair = core.cpair
|
||||||
|
|
||||||
|
local basic_states = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.yellow), symbol = "\x1e" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
||||||
|
}
|
||||||
|
|
||||||
|
local mode_states = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.purple), symbol = "A" }
|
||||||
|
}
|
||||||
|
|
||||||
|
local emc_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.gray), symbol = "-" },
|
||||||
|
{ color = cpair(colors.black, colors.white), symbol = "\x07" },
|
||||||
|
{ color = cpair(colors.black, colors.green), symbol = "+" }
|
||||||
|
}
|
||||||
|
|
||||||
|
local red_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.red), symbol = "-" }
|
||||||
|
}
|
||||||
|
|
||||||
|
local yel_ind_s = {
|
||||||
|
{ color = cpair(colors.black, colors.lightGray), symbol = "+" },
|
||||||
|
{ color = cpair(colors.black, colors.yellow), symbol = "-" }
|
||||||
|
}
|
||||||
|
|
||||||
-- new unit page view
|
-- new unit page view
|
||||||
---@param root graphics_element parent
|
---@param root graphics_element parent
|
||||||
local function new_view(root)
|
local function new_view(root)
|
||||||
|
local db = iocontrol.get_db()
|
||||||
|
|
||||||
local main = Div{parent=root,x=1,y=1}
|
local main = Div{parent=root,x=1,y=1}
|
||||||
|
|
||||||
TextBox{parent=main,text="UNITS",x=1,y=1,height=1,alignment=ALIGN.CENTER}
|
local app = db.nav.register_app(iocontrol.APP_ID.UNITS, main)
|
||||||
|
|
||||||
|
TextBox{parent=main,y=2,text="Units App",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
TextBox{parent=main,y=4,text="Loading...",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local btn_fg_bg = cpair(colors.yellow, colors.black)
|
||||||
|
local btn_active = cpair(colors.white, colors.black)
|
||||||
|
-- local label = cpair(colors.lightGray, colors.black)
|
||||||
|
|
||||||
|
local nav_links = {}
|
||||||
|
|
||||||
|
local function set_sidebar(id)
|
||||||
|
-- local unit = db.units[id] ---@type pioctl_unit
|
||||||
|
|
||||||
|
local list = {
|
||||||
|
{ label = " # ", tall = true, color = core.cpair(colors.black, colors.green), callback = function () db.nav.open_app(iocontrol.APP_ID.ROOT) end },
|
||||||
|
{ label = "U-" .. id, color = core.cpair(colors.black, colors.yellow), callback = function () app.switcher(id) end },
|
||||||
|
{ label = " \x13 ", color = core.cpair(colors.black, colors.red), callback = nav_links[id].alarm },
|
||||||
|
{ label = "RPS", tall = true, color = core.cpair(colors.black, colors.cyan), callback = nav_links[id].rps },
|
||||||
|
-- { label = " R ", color = core.cpair(colors.black, colors.lightGray), callback = function () end },
|
||||||
|
{ label = "RCS", tall = true, color = core.cpair(colors.black, colors.blue), callback = nav_links[id].rcs },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- for i = 1, unit.num_boilers do
|
||||||
|
-- table.insert(list, { label = "B-" .. i, color = core.cpair(colors.black, colors.lightBlue), callback = function () end })
|
||||||
|
-- end
|
||||||
|
|
||||||
|
-- for i = 1, unit.num_turbines do
|
||||||
|
-- table.insert(list, { label = "T-" .. i, color = core.cpair(colors.black, colors.white), callback = function () end })
|
||||||
|
-- end
|
||||||
|
|
||||||
|
app.set_sidebar(list)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function load()
|
||||||
|
local page_div = Div{parent=main,x=2,y=2,width=main.get_width()-2}
|
||||||
|
|
||||||
|
local panes = {}
|
||||||
|
|
||||||
|
local active_unit = 1
|
||||||
|
|
||||||
|
-- create all page divs
|
||||||
|
for _ = 1, db.facility.num_units do
|
||||||
|
local div = Div{parent=page_div}
|
||||||
|
table.insert(panes, div)
|
||||||
|
table.insert(nav_links, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- previous unit
|
||||||
|
local function prev(x)
|
||||||
|
active_unit = util.trinary(x == 1, db.facility.num_units, x - 1)
|
||||||
|
app.switcher(active_unit)
|
||||||
|
set_sidebar(active_unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- next unit
|
||||||
|
local function next(x)
|
||||||
|
active_unit = util.trinary(x == db.facility.num_units, 1, x + 1)
|
||||||
|
app.switcher(active_unit)
|
||||||
|
set_sidebar(active_unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, db.facility.num_units do
|
||||||
|
local u_div = panes[i] ---@type graphics_element
|
||||||
|
local unit = db.units[i] ---@type pioctl_unit
|
||||||
|
local u_ps = unit.unit_ps
|
||||||
|
|
||||||
|
-- refresh data callback, every 500ms it will re-send the query
|
||||||
|
local last_update = 0
|
||||||
|
local function update()
|
||||||
|
if util.time_ms() - last_update >= 500 then
|
||||||
|
db.api.get_unit(i)
|
||||||
|
last_update = util.time_ms()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--#region Main Unit Overview
|
||||||
|
|
||||||
|
local u_page = app.new_page(nil, i)
|
||||||
|
u_page.tasks = { update }
|
||||||
|
|
||||||
|
TextBox{parent=u_div,y=1,text="Reactor Unit #"..i,height=1,alignment=ALIGN.CENTER}
|
||||||
|
PushButton{parent=u_div,x=1,y=1,text="<",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()prev(i)end}
|
||||||
|
PushButton{parent=u_div,x=21,y=1,text=">",fg_bg=btn_fg_bg,active_fg_bg=btn_active,callback=function()next(i)end}
|
||||||
|
|
||||||
|
local type = util.trinary(unit.num_boilers > 0, "Sodium Cooled Reactor", "Boiling Water Reactor")
|
||||||
|
TextBox{parent=u_div,y=3,text=type,height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
|
||||||
|
local lu_col = cpair(colors.lightGray, colors.lightGray)
|
||||||
|
local text_fg = cpair(colors.white, colors._INHERIT)
|
||||||
|
|
||||||
|
local rate = DataIndicator{parent=u_div,y=5,lu_colors=lu_col,label="Rate",unit="mB/t",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
|
local temp = DataIndicator{parent=u_div,lu_colors=lu_col,label="Temp",unit="K",format="%10.2f",value=0,commas=true,width=26,fg_bg=text_fg}
|
||||||
|
|
||||||
|
local ctrl = IconIndicator{parent=u_div,x=1,y=8,label="Control State",states=mode_states}
|
||||||
|
|
||||||
|
rate.register(u_ps, "act_burn_rate", rate.update)
|
||||||
|
temp.register(u_ps, "temp", temp.update)
|
||||||
|
ctrl.register(u_ps, "U_ControlStatus", ctrl.update)
|
||||||
|
|
||||||
|
u_div.line_break()
|
||||||
|
|
||||||
|
local rct = IconIndicator{parent=u_div,x=1,label="Fission Reactor",states=basic_states}
|
||||||
|
local rps = IconIndicator{parent=u_div,x=1,label="Protection System",states=basic_states}
|
||||||
|
|
||||||
|
rct.register(u_ps, "U_ReactorStatus", rct.update)
|
||||||
|
rps.register(u_ps, "U_RPS", rps.update)
|
||||||
|
|
||||||
|
u_div.line_break()
|
||||||
|
|
||||||
|
local rcs = IconIndicator{parent=u_div,x=1,label="Coolant System",states=basic_states}
|
||||||
|
rcs.register(u_ps, "U_RCS", rcs.update)
|
||||||
|
|
||||||
|
for b = 1, unit.num_boilers do
|
||||||
|
local blr = IconIndicator{parent=u_div,x=1,label="Boiler "..b,states=basic_states}
|
||||||
|
blr.register(unit.boiler_ps_tbl[b], "BoilerStatus", blr.update)
|
||||||
|
end
|
||||||
|
|
||||||
|
for t = 1, unit.num_turbines do
|
||||||
|
local tbn = IconIndicator{parent=u_div,x=1,label="Turbine "..t,states=basic_states}
|
||||||
|
tbn.register(unit.turbine_ps_tbl[t], "TurbineStatus", tbn.update)
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Alarms Tab
|
||||||
|
|
||||||
|
local alm_div = Div{parent=page_div}
|
||||||
|
table.insert(panes, alm_div)
|
||||||
|
|
||||||
|
local alm_page = app.new_page(u_page, #panes)
|
||||||
|
alm_page.tasks = { update }
|
||||||
|
|
||||||
|
nav_links[i].alarm = alm_page.nav_to
|
||||||
|
|
||||||
|
TextBox{parent=alm_div,y=1,text="Unit Alarms",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
TextBox{parent=alm_div,y=3,text="work in progress",height=1,alignment=ALIGN.CENTER,fg_bg=cpair(colors.gray,colors.black)}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region RPS Tab
|
||||||
|
|
||||||
|
local rps_div = Div{parent=page_div}
|
||||||
|
table.insert(panes, rps_div)
|
||||||
|
|
||||||
|
local rps_page = app.new_page(u_page, #panes)
|
||||||
|
rps_page.tasks = { update }
|
||||||
|
|
||||||
|
nav_links[i].rps = rps_page.nav_to
|
||||||
|
|
||||||
|
TextBox{parent=rps_div,y=1,text="Protection System",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local r_trip = IconIndicator{parent=rps_div,y=3,label="RPS Trip",states=basic_states}
|
||||||
|
r_trip.register(u_ps, "U_RPS", r_trip.update)
|
||||||
|
|
||||||
|
local r_mscrm = IconIndicator{parent=rps_div,y=5,label="Manual SCRAM",states=red_ind_s}
|
||||||
|
local r_ascrm = IconIndicator{parent=rps_div,label="Automatic SCRAM",states=red_ind_s}
|
||||||
|
local rps_tmo = IconIndicator{parent=rps_div,label="Timeout",states=yel_ind_s}
|
||||||
|
local rps_flt = IconIndicator{parent=rps_div,label="PPM Fault",states=yel_ind_s}
|
||||||
|
local rps_sfl = IconIndicator{parent=rps_div,label="Not Formed",states=red_ind_s}
|
||||||
|
|
||||||
|
r_mscrm.register(u_ps, "manual", r_mscrm.update)
|
||||||
|
r_ascrm.register(u_ps, "automatic", r_ascrm.update)
|
||||||
|
rps_tmo.register(u_ps, "timeout", rps_tmo.update)
|
||||||
|
rps_flt.register(u_ps, "fault", rps_flt.update)
|
||||||
|
rps_sfl.register(u_ps, "sys_fail", rps_sfl.update)
|
||||||
|
|
||||||
|
rps_div.line_break()
|
||||||
|
local rps_dmg = IconIndicator{parent=rps_div,label="Reactor Damage Hi",states=red_ind_s}
|
||||||
|
local rps_tmp = IconIndicator{parent=rps_div,label="Temp. Critical",states=red_ind_s}
|
||||||
|
local rps_nof = IconIndicator{parent=rps_div,label="Fuel Level Lo",states=yel_ind_s}
|
||||||
|
local rps_exw = IconIndicator{parent=rps_div,label="Waste Level Hi",states=yel_ind_s}
|
||||||
|
local rps_loc = IconIndicator{parent=rps_div,label="Coolant Lo Lo",states=yel_ind_s}
|
||||||
|
local rps_exh = IconIndicator{parent=rps_div,label="Heated Coolant Hi",states=yel_ind_s}
|
||||||
|
|
||||||
|
rps_dmg.register(u_ps, "high_dmg", rps_dmg.update)
|
||||||
|
rps_tmp.register(u_ps, "high_temp", rps_tmp.update)
|
||||||
|
rps_nof.register(u_ps, "no_fuel", rps_nof.update)
|
||||||
|
rps_exw.register(u_ps, "ex_waste", rps_exw.update)
|
||||||
|
rps_loc.register(u_ps, "low_cool", rps_loc.update)
|
||||||
|
rps_exh.register(u_ps, "ex_hcool", rps_exh.update)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region RCS Tab
|
||||||
|
|
||||||
|
local rcs_div = Div{parent=page_div}
|
||||||
|
table.insert(panes, rcs_div)
|
||||||
|
|
||||||
|
local rcs_page = app.new_page(u_page, #panes)
|
||||||
|
rcs_page.tasks = { update }
|
||||||
|
|
||||||
|
nav_links[i].rcs = rcs_page.nav_to
|
||||||
|
|
||||||
|
TextBox{parent=rcs_div,y=1,text="Coolant System",height=1,alignment=ALIGN.CENTER}
|
||||||
|
|
||||||
|
local r_rtrip = IconIndicator{parent=rcs_div,y=3,label="RCP Trip",states=red_ind_s}
|
||||||
|
local r_cflow = IconIndicator{parent=rcs_div,label="RCS Flow Lo",states=yel_ind_s}
|
||||||
|
local r_clow = IconIndicator{parent=rcs_div,label="Coolant Level Lo",states=yel_ind_s}
|
||||||
|
|
||||||
|
r_rtrip.register(u_ps, "RCPTrip", r_rtrip.update)
|
||||||
|
r_cflow.register(u_ps, "RCSFlowLow", r_cflow.update)
|
||||||
|
r_clow.register(u_ps, "CoolantLevelLow", r_clow.update)
|
||||||
|
|
||||||
|
local c_flt = IconIndicator{parent=rcs_div,label="RCS HW Fault",states=yel_ind_s}
|
||||||
|
local c_emg = IconIndicator{parent=rcs_div,label="Emergency Coolant",states=emc_ind_s}
|
||||||
|
local c_mwrf = IconIndicator{parent=rcs_div,label="Max Water Return",states=yel_ind_s}
|
||||||
|
|
||||||
|
c_flt.register(u_ps, "RCSFault", c_flt.update)
|
||||||
|
c_emg.register(u_ps, "EmergencyCoolant", c_emg.update)
|
||||||
|
c_mwrf.register(u_ps, "MaxWaterReturnFeed", c_mwrf.update)
|
||||||
|
|
||||||
|
-- rcs_div.line_break()
|
||||||
|
-- TextBox{parent=rcs_div,text="Mismatches",height=1,alignment=ALIGN.CENTER,fg_bg=label}
|
||||||
|
local c_cfm = IconIndicator{parent=rcs_div,label="Coolant Feed",states=yel_ind_s}
|
||||||
|
local c_brm = IconIndicator{parent=rcs_div,label="Boil Rate",states=yel_ind_s}
|
||||||
|
local c_sfm = IconIndicator{parent=rcs_div,label="Steam Feed",states=yel_ind_s}
|
||||||
|
|
||||||
|
c_cfm.register(u_ps, "CoolantFeedMismatch", c_cfm.update)
|
||||||
|
c_brm.register(u_ps, "BoilRateMismatch", c_brm.update)
|
||||||
|
c_sfm.register(u_ps, "SteamFeedMismatch", c_sfm.update)
|
||||||
|
|
||||||
|
rcs_div.line_break()
|
||||||
|
-- TextBox{parent=rcs_div,text="Aggregate Checks",height=1,alignment=ALIGN.CENTER,fg_bg=label}
|
||||||
|
|
||||||
|
if unit.num_boilers > 0 then
|
||||||
|
local wll = IconIndicator{parent=rcs_div,label="Boiler Water Lo",states=red_ind_s}
|
||||||
|
local hrl = IconIndicator{parent=rcs_div,label="Heating Rate Lo",states=yel_ind_s}
|
||||||
|
|
||||||
|
wll.register(u_ps, "U_WaterLevelLow", wll.update)
|
||||||
|
hrl.register(u_ps, "U_HeatingRateLow", hrl.update)
|
||||||
|
end
|
||||||
|
|
||||||
|
local tospd = IconIndicator{parent=rcs_div,label="TRB Over Speed",states=red_ind_s}
|
||||||
|
local gtrip = IconIndicator{parent=rcs_div,label="Generator Trip",states=yel_ind_s}
|
||||||
|
local ttrip = IconIndicator{parent=rcs_div,label="Turbine Trip",states=red_ind_s}
|
||||||
|
|
||||||
|
tospd.register(u_ps, "U_TurbineOverSpeed", tospd.update)
|
||||||
|
gtrip.register(u_ps, "U_GeneratorTrip", gtrip.update)
|
||||||
|
ttrip.register(u_ps, "U_TurbineTrip", ttrip.update)
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
end
|
||||||
|
|
||||||
|
-- setup multipane
|
||||||
|
local u_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
||||||
|
app.set_root_pane(u_pane)
|
||||||
|
|
||||||
|
set_sidebar(active_unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
app.set_on_load(load)
|
||||||
|
|
||||||
return main
|
return main
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local tcd = require("scada-common.tcd")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
@@ -23,6 +24,8 @@ local RadioButton = require("graphics.elements.controls.radio_button")
|
|||||||
local NumberField = require("graphics.elements.form.number_field")
|
local NumberField = require("graphics.elements.form.number_field")
|
||||||
local TextField = require("graphics.elements.form.text_field")
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local IndLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local tri = util.trinary
|
local tri = util.trinary
|
||||||
|
|
||||||
@@ -34,8 +37,10 @@ local RIGHT = core.ALIGN.RIGHT
|
|||||||
|
|
||||||
-- changes to the config data/format to let the user know
|
-- changes to the config data/format to let the user know
|
||||||
local changes = {
|
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" } }
|
{ "v1.6.8", { "ConnTimeout can now have a fractional part" } },
|
||||||
|
{ "v1.6.15", { "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
|
{ "v1.7.3", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class plc_configurator
|
---@class plc_configurator
|
||||||
@@ -46,34 +51,25 @@ 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.colors = {
|
style.colors = themes.smooth_stone.colors
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
local nav_fg_bg = bw_fg_bg
|
local nav_fg_bg = bw_fg_bg
|
||||||
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
||||||
|
|
||||||
|
---@class _plc_cfg_tool_ctl
|
||||||
local tool_ctl = {
|
local tool_ctl = {
|
||||||
ask_config = false,
|
ask_config = false,
|
||||||
has_config = false,
|
has_config = false,
|
||||||
viewing_config = false,
|
viewing_config = false,
|
||||||
importing_legacy = false,
|
importing_legacy = false,
|
||||||
|
jumped_to_color = false,
|
||||||
|
|
||||||
view_cfg = nil, ---@type graphics_element
|
view_cfg = nil, ---@type graphics_element
|
||||||
|
color_cfg = nil, ---@type graphics_element
|
||||||
|
color_next = nil, ---@type graphics_element
|
||||||
|
color_apply = nil, ---@type graphics_element
|
||||||
settings_apply = nil, ---@type graphics_element
|
settings_apply = nil, ---@type graphics_element
|
||||||
|
|
||||||
set_networked = nil, ---@type function
|
set_networked = nil, ---@type function
|
||||||
@@ -103,6 +99,8 @@ local tmp_cfg = {
|
|||||||
LogMode = 0,
|
LogMode = 0,
|
||||||
LogPath = "",
|
LogPath = "",
|
||||||
LogDebug = false,
|
LogDebug = false,
|
||||||
|
FrontPanelTheme = 1,
|
||||||
|
ColorMode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class plc_config
|
---@class plc_config
|
||||||
@@ -124,7 +122,9 @@ local fields = {
|
|||||||
{ "AuthKey", "Facility Auth Key" , ""},
|
{ "AuthKey", "Facility Auth Key" , ""},
|
||||||
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
||||||
{ "LogPath", "Log Path", "/log.txt" },
|
{ "LogPath", "Log Path", "/log.txt" },
|
||||||
{ "LogDebug","Log Debug Messages", false }
|
{ "LogDebug", "Log Debug Messages", false },
|
||||||
|
{ "FrontPanelTheme", "Front Panel Theme", themes.FP_THEME.SANDSTONE },
|
||||||
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
||||||
}
|
}
|
||||||
|
|
||||||
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
||||||
@@ -176,10 +176,11 @@ local function config_view(display)
|
|||||||
local plc_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local plc_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
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 clr_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 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,changelog}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,plc_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog}}
|
||||||
|
|
||||||
-- Main Page
|
-- Main Page
|
||||||
|
|
||||||
@@ -196,7 +197,7 @@ local function config_view(display)
|
|||||||
tool_ctl.viewing_config = true
|
tool_ctl.viewing_config = true
|
||||||
tool_ctl.gen_summary(settings_cfg)
|
tool_ctl.gen_summary(settings_cfg)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists("/reactor-plc/config.lua") then
|
if fs.exists("/reactor-plc/config.lua") then
|
||||||
@@ -207,10 +208,21 @@ local function config_view(display)
|
|||||||
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
|
||||||
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
local function jump_color()
|
||||||
|
tool_ctl.jumped_to_color = true
|
||||||
|
tool_ctl.color_next.hide(true)
|
||||||
|
tool_ctl.color_apply.show()
|
||||||
|
main_pane.set_value(5)
|
||||||
|
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}
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
if not tool_ctl.has_config then
|
||||||
|
tool_ctl.view_cfg.disable()
|
||||||
|
tool_ctl.color_cfg.disable()
|
||||||
|
end
|
||||||
|
|
||||||
--#region PLC
|
--#region PLC
|
||||||
|
|
||||||
@@ -419,10 +431,8 @@ local function config_view(display)
|
|||||||
tmp_cfg.LogMode = mode.get_value() - 1
|
tmp_cfg.LogMode = mode.get_value() - 1
|
||||||
tmp_cfg.LogPath = path.get_value()
|
tmp_cfg.LogPath = path.get_value()
|
||||||
tmp_cfg.LogDebug = en_dbg.get_value()
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.color_apply.hide(true)
|
||||||
tool_ctl.viewing_config = false
|
tool_ctl.color_next.show()
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(5)
|
||||||
else path_err.show() end
|
else path_err.show() end
|
||||||
end
|
end
|
||||||
@@ -436,6 +446,116 @@ local function config_view(display)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Color Options
|
||||||
|
|
||||||
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
||||||
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
||||||
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
||||||
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
||||||
|
|
||||||
|
local function recolor(value)
|
||||||
|
local c = themes.smooth_stone.color_modes[value]
|
||||||
|
|
||||||
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
b_off.hide()
|
||||||
|
g_off.show()
|
||||||
|
else
|
||||||
|
g_off.hide()
|
||||||
|
b_off.show()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #c == 0 then
|
||||||
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
||||||
|
else
|
||||||
|
term.setPaletteColor(colors.green, c[1].hex)
|
||||||
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
||||||
|
term.setPaletteColor(colors.red, c[3].hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
||||||
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function back_from_colors()
|
||||||
|
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4))
|
||||||
|
tool_ctl.jumped_to_color = false
|
||||||
|
recolor(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_access()
|
||||||
|
clr_pane.set_value(2)
|
||||||
|
recolor(c_mode.get_value())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_colors()
|
||||||
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
||||||
|
tmp_cfg.ColorMode = c_mode.get_value()
|
||||||
|
|
||||||
|
if tool_ctl.jumped_to_color then
|
||||||
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
||||||
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
||||||
|
|
||||||
|
if settings.save("/reactor-plc.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
clr_pane.set_value(3)
|
||||||
|
else
|
||||||
|
clr_pane.set_value(4)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
tool_ctl.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
main_pane.set_value(6)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
|
local function c_go_home()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
||||||
|
PushButton{parent=clr_c_3,x=1,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=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=clr_c_4,x=1,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=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Summary and Saving
|
--#region Summary and Saving
|
||||||
|
|
||||||
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
@@ -456,7 +576,7 @@ local function config_view(display)
|
|||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
tool_ctl.settings_apply.show()
|
tool_ctl.settings_apply.show()
|
||||||
else
|
else
|
||||||
main_pane.set_value(4)
|
main_pane.set_value(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -487,6 +607,8 @@ local function config_view(display)
|
|||||||
try_set(mode, ini_cfg.LogMode)
|
try_set(mode, ini_cfg.LogMode)
|
||||||
try_set(path, ini_cfg.LogPath)
|
try_set(path, ini_cfg.LogPath)
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
||||||
|
try_set(c_mode, ini_cfg.ColorMode)
|
||||||
|
|
||||||
tool_ctl.view_cfg.enable()
|
tool_ctl.view_cfg.enable()
|
||||||
|
|
||||||
@@ -511,6 +633,7 @@ local function config_view(display)
|
|||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
plc_pane.set_value(1)
|
plc_pane.set_value(1)
|
||||||
net_pane.set_value(1)
|
net_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -588,7 +711,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
tool_ctl.importing_legacy = true
|
tool_ctl.importing_legacy = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -606,7 +729,7 @@ local function config_view(display)
|
|||||||
local alternate = false
|
local alternate = false
|
||||||
local inner_width = setting_list.get_width() - 1
|
local inner_width = setting_list.get_width() - 1
|
||||||
|
|
||||||
tool_ctl.show_key_btn.enable()
|
if cfg.AuthKey then tool_ctl.show_key_btn.enable() else tool_ctl.show_key_btn.disable() end
|
||||||
tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key
|
tool_ctl.auth_key_value = cfg.AuthKey or "" -- to show auth key
|
||||||
|
|
||||||
for i = 1, #fields do
|
for i = 1, #fields do
|
||||||
@@ -617,9 +740,15 @@ local function config_view(display)
|
|||||||
local raw = cfg[f[1]]
|
local raw = cfg[f[1]]
|
||||||
local val = util.strval(raw)
|
local val = util.strval(raw)
|
||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) end
|
if f[1] == "AuthKey" and raw then val = string.rep("*", string.len(val))
|
||||||
if f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") end
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
if f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw) end
|
elseif f[1] == "EmerCoolColor" and raw ~= nil then val = rsio.color_name(raw)
|
||||||
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
|
elseif f[1] == "ColorMode" then
|
||||||
|
val = util.strval(themes.color_mode_name(raw))
|
||||||
|
end
|
||||||
|
|
||||||
if val == "nil" then val = "<not set>" end
|
if val == "nil" then val = "<not set>" end
|
||||||
|
|
||||||
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
||||||
|
|||||||
@@ -70,9 +70,9 @@ function databus.tx_link_state(state)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- transmit reactor enable state across the bus
|
-- transmit reactor enable state across the bus
|
||||||
---@param active boolean reactor active
|
---@param active any reactor active
|
||||||
function databus.tx_reactor_state(active)
|
function databus.tx_reactor_state(active)
|
||||||
databus.ps.publish("reactor_active", active)
|
databus.ps.publish("reactor_active", active == true)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- transmit RPS data across the bus
|
-- transmit RPS data across the bus
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ local LED = require("graphics.elements.indicators.led")
|
|||||||
local LEDPair = require("graphics.elements.indicators.ledpair")
|
local LEDPair = require("graphics.elements.indicators.ledpair")
|
||||||
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||||
|
|
||||||
|
local LINK_STATE = types.PANEL_LINK_STATE
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
@@ -34,8 +36,12 @@ local ind_red = style.ind_red
|
|||||||
-- create new front panel view
|
-- create new front panel view
|
||||||
---@param panel graphics_element main displaybox
|
---@param panel graphics_element main displaybox
|
||||||
local function init(panel)
|
local function init(panel)
|
||||||
local header = TextBox{parent=panel,y=1,text="REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
local s_hi_box = style.theme.highlight_box
|
||||||
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("REACTOR PLC - UNIT ", id)) end)
|
|
||||||
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
|
local header = TextBox{parent=panel,y=1,text="FISSION REACTOR PLC - UNIT ?",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
|
header.register(databus.ps, "unit_id", function (id) header.set_value(util.c("FISSION REACTOR PLC - UNIT ", id)) end)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- system indicators
|
-- system indicators
|
||||||
@@ -52,13 +58,47 @@ local function init(panel)
|
|||||||
|
|
||||||
local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green}
|
local reactor = LEDPair{parent=system,label="REACTOR",off=colors.red,c1=colors.yellow,c2=colors.green}
|
||||||
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
|
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
if not style.colorblind then
|
||||||
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.ind_bkg}}
|
||||||
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
|
network.register(databus.ps, "link_state", network.update)
|
||||||
|
else
|
||||||
|
local nt_lnk = LEDPair{parent=system,label="NT LINKED",off=style.ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
local nt_ver = LEDPair{parent=system,label="NT VERSION",off=style.ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
local nt_col = LED{parent=system,label="NT COLLISION",colors=ind_red}
|
||||||
|
|
||||||
|
nt_lnk.register(databus.ps, "link_state", function (state)
|
||||||
|
local value = 2
|
||||||
|
|
||||||
|
if state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
elseif state == LINK_STATE.LINKED then
|
||||||
|
value = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_lnk.update(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
nt_ver.register(databus.ps, "link_state", function (state)
|
||||||
|
local value = 3
|
||||||
|
|
||||||
|
if state == LINK_STATE.BAD_VERSION then
|
||||||
|
value = 2
|
||||||
|
elseif state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_ver.update(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
nt_col.register(databus.ps, "link_state", function (state) nt_col.update(state == LINK_STATE.COLLISION) end)
|
||||||
|
end
|
||||||
|
|
||||||
system.line_break()
|
system.line_break()
|
||||||
|
|
||||||
reactor.register(databus.ps, "reactor_dev_state", reactor.update)
|
reactor.register(databus.ps, "reactor_dev_state", reactor.update)
|
||||||
modem.register(databus.ps, "has_modem", modem.update)
|
modem.register(databus.ps, "has_modem", modem.update)
|
||||||
network.register(databus.ps, "link_state", network.update)
|
|
||||||
|
|
||||||
local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
|
local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
|
||||||
local rt_rps = LED{parent=system,label="RT RPS",colors=ind_grn}
|
local rt_rps = LED{parent=system,label="RT RPS",colors=ind_grn}
|
||||||
@@ -75,7 +115,7 @@ local function init(panel)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=5,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)}
|
TextBox{parent=system,x=9,y=5,width=6,height=1,text=comp_id,fg_bg=disabled_fg}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- status & controls
|
-- status & controls
|
||||||
@@ -91,12 +131,12 @@ local function init(panel)
|
|||||||
emer_cool.register(databus.ps, "emer_cool", emer_cool.update)
|
emer_cool.register(databus.ps, "emer_cool", emer_cool.update)
|
||||||
end
|
end
|
||||||
|
|
||||||
local status_trip_rct = Rectangle{parent=status,width=20,height=3,x=1,border=border(1,colors.lightGray,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)}
|
local status_trip_rct = Rectangle{parent=status,width=20,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
||||||
local status_trip = Div{parent=status_trip_rct,width=18,height=1,fg_bg=cpair(colors.black,colors.lightGray)}
|
local status_trip = Div{parent=status_trip_rct,width=18,height=1,fg_bg=s_hi_box}
|
||||||
local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=ind_red,flash=true,period=flasher.PERIOD.BLINK_250_MS}
|
local scram = LED{parent=status_trip,width=10,label="RPS TRIP",colors=ind_red,flash=true,period=flasher.PERIOD.BLINK_250_MS}
|
||||||
|
|
||||||
local controls_rct = Rectangle{parent=status,width=17,height=3,x=1,border=border(1,colors.white,true),even_inner=true,fg_bg=cpair(colors.black,colors.ivory)}
|
local controls_rct = Rectangle{parent=status,width=17,height=3,x=1,border=border(1,s_hi_box.bkg,true),even_inner=true}
|
||||||
local controls = Div{parent=controls_rct,width=15,height=1,fg_bg=cpair(colors.black,colors.white)}
|
local controls = Div{parent=controls_rct,width=15,height=1,fg_bg=s_hi_box}
|
||||||
PushButton{parent=controls,x=1,y=1,min_width=7,text="SCRAM",callback=databus.rps_scram,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.black,colors.red_off)}
|
PushButton{parent=controls,x=1,y=1,min_width=7,text="SCRAM",callback=databus.rps_scram,fg_bg=cpair(colors.black,colors.red),active_fg_bg=cpair(colors.black,colors.red_off)}
|
||||||
PushButton{parent=controls,x=9,y=1,min_width=7,text="RESET",callback=databus.rps_reset,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.black,colors.yellow_off)}
|
PushButton{parent=controls,x=9,y=1,min_width=7,text="RESET",callback=databus.rps_reset,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=cpair(colors.black,colors.yellow_off)}
|
||||||
|
|
||||||
@@ -107,9 +147,9 @@ local function init(panel)
|
|||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Rectangle{parent=panel,width=32,height=3,x=2,y=16,border=border(1,colors.ivory),thin=true,fg_bg=cpair(colors.black,colors.white)}
|
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=2,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
local comms_v = TextBox{parent=about,x=17,y=1,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
|
|
||||||
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
fw_v.register(databus.ps, "version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
comms_v.register(databus.ps, "comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
@@ -118,7 +158,7 @@ local function init(panel)
|
|||||||
-- rps list
|
-- rps list
|
||||||
--
|
--
|
||||||
|
|
||||||
local rps = Rectangle{parent=panel,width=16,height=16,x=36,y=3,border=border(1,colors.lightGray),thin=true,fg_bg=cpair(colors.black,colors.lightGray)}
|
local rps = Rectangle{parent=panel,width=16,height=16,x=36,y=3,border=border(1,s_hi_box.bkg),thin=true,fg_bg=s_hi_box}
|
||||||
local rps_man = LED{parent=rps,label="MANUAL",colors=ind_red}
|
local rps_man = LED{parent=rps,label="MANUAL",colors=ind_red}
|
||||||
local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=ind_red}
|
local rps_auto = LED{parent=rps,label="AUTOMATIC",colors=ind_red}
|
||||||
local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=ind_red}
|
local rps_tmo = LED{parent=rps,label="TIMEOUT",colors=ind_red}
|
||||||
|
|||||||
@@ -2,46 +2,40 @@
|
|||||||
-- Graphics Style Options
|
-- Graphics Style Options
|
||||||
--
|
--
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
|
---@class plc_style
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
-- GLOBAL --
|
style.theme = themes.sandstone
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
-- remap global colors
|
style.colorblind = false
|
||||||
colors.ivory = colors.pink
|
|
||||||
colors.yellow_hc = colors.purple
|
|
||||||
colors.red_off = colors.brown
|
|
||||||
colors.yellow_off = colors.magenta
|
|
||||||
colors.green_off = colors.lime
|
|
||||||
|
|
||||||
style.root = cpair(colors.black, colors.ivory)
|
|
||||||
style.header = cpair(colors.black, colors.lightGray)
|
|
||||||
|
|
||||||
style.colors = {
|
|
||||||
{ c = colors.red, hex = 0xdf4949 }, -- RED ON
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xf9fb53 }, -- YELLOW ON
|
|
||||||
{ c = colors.lime, hex = 0x16665a }, -- GREEN OFF
|
|
||||||
{ c = colors.green, hex = 0x6be551 }, -- GREEN ON
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee }, -- YELLOW HIGH CONTRAST
|
|
||||||
{ c = colors.pink, hex = 0xdcd9ca }, -- IVORY
|
|
||||||
{ c = colors.magenta, hex = 0x85862c }, -- YELLOW OFF
|
|
||||||
-- { c = colors.white, hex = 0xdcd9ca },
|
|
||||||
{ c = colors.lightGray, hex = 0xb1b8b3 },
|
|
||||||
{ c = colors.gray, hex = 0x575757 },
|
|
||||||
-- { c = colors.black, hex = 0x191919 },
|
|
||||||
{ c = colors.brown, hex = 0x672223 } -- RED OFF
|
|
||||||
}
|
|
||||||
|
|
||||||
-- COMMON COLOR PAIRS --
|
|
||||||
|
|
||||||
style.ind_grn = cpair(colors.green, colors.green_off)
|
style.ind_grn = cpair(colors.green, colors.green_off)
|
||||||
style.ind_red = cpair(colors.red, colors.red_off)
|
style.ind_red = cpair(colors.red, colors.red_off)
|
||||||
|
|
||||||
|
-- set theme per configuration
|
||||||
|
---@param fp FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE the color mode to use
|
||||||
|
function style.set_theme(fp, color_mode)
|
||||||
|
if fp == themes.FP_THEME.SANDSTONE then
|
||||||
|
style.theme = themes.sandstone
|
||||||
|
elseif fp == themes.FP_THEME.BASALT then
|
||||||
|
style.theme = themes.basalt
|
||||||
|
end
|
||||||
|
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
|
|
||||||
|
style.colorblind = color_mode ~= themes.COLOR_MODE.STANDARD and color_mode ~= themes.COLOR_MODE.STD_ON_BLACK
|
||||||
|
|
||||||
|
if color_mode == themes.COLOR_MODE.STANDARD or color_mode == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
style.ind_bkg = colors.gray
|
||||||
|
else
|
||||||
|
style.ind_bkg = colors.black
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return style
|
return style
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ local rsio = require("scada-common.rsio")
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local databus = require("reactor-plc.databus")
|
local databus = require("reactor-plc.databus")
|
||||||
|
|
||||||
local plc = {}
|
local plc = {}
|
||||||
@@ -23,8 +25,8 @@ local RPS_LIMITS = const.RPS_LIMITS
|
|||||||
|
|
||||||
-- I sure hope the devs don't change this error message, not that it would have safety implications
|
-- I sure hope the devs don't change this error message, not that it would have safety implications
|
||||||
-- I wish they didn't change it to be like this
|
-- I wish they didn't change it to be like this
|
||||||
local PCALL_SCRAM_MSG = "pcall: Scram requires the reactor to be active."
|
local PCALL_SCRAM_MSG = "Scram requires the reactor to be active."
|
||||||
local PCALL_START_MSG = "pcall: Reactor is already active."
|
local PCALL_START_MSG = "Reactor is already active."
|
||||||
|
|
||||||
---@type plc_config
|
---@type plc_config
|
||||||
local config = {}
|
local config = {}
|
||||||
@@ -52,6 +54,9 @@ function plc.load_config()
|
|||||||
config.LogPath = settings.get("LogPath")
|
config.LogPath = settings.get("LogPath")
|
||||||
config.LogDebug = settings.get("LogDebug")
|
config.LogDebug = settings.get("LogDebug")
|
||||||
|
|
||||||
|
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
||||||
|
config.ColorMode = settings.get("ColorMode")
|
||||||
|
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
cfv.assert_type_bool(config.Networked)
|
cfv.assert_type_bool(config.Networked)
|
||||||
@@ -69,7 +74,7 @@ function plc.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -78,6 +83,11 @@ function plc.load_config()
|
|||||||
cfv.assert_type_str(config.LogPath)
|
cfv.assert_type_str(config.LogPath)
|
||||||
cfv.assert_type_bool(config.LogDebug)
|
cfv.assert_type_bool(config.LogDebug)
|
||||||
|
|
||||||
|
cfv.assert_type_int(config.FrontPanelTheme)
|
||||||
|
cfv.assert_range(config.FrontPanelTheme, 1, 2)
|
||||||
|
cfv.assert_type_int(config.ColorMode)
|
||||||
|
cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
||||||
|
|
||||||
-- check emergency coolant configuration if enabled
|
-- check emergency coolant configuration if enabled
|
||||||
if config.EmerCoolEnable then
|
if config.EmerCoolEnable then
|
||||||
cfv.assert_eq(rsio.is_valid_side(config.EmerCoolSide), true)
|
cfv.assert_eq(rsio.is_valid_side(config.EmerCoolSide), true)
|
||||||
@@ -129,6 +139,21 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- check if the result of a peripheral call was OK, handle the failure if not
|
||||||
|
---@nodiscard
|
||||||
|
---@param result any PPM function call result
|
||||||
|
---@return boolean succeeded if the result is OK, false if it was a PPM failure
|
||||||
|
local function _check_and_handle_ppm_call(result)
|
||||||
|
if result == ppm.ACCESS_FAULT then
|
||||||
|
_set_fault()
|
||||||
|
|
||||||
|
-- if undefined, then the reactor isn't formed
|
||||||
|
if reactor.__p_last_fault() == ppm.UNDEFINED_FIELD then self.formed = false end
|
||||||
|
else return true end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- set emergency coolant control (if configured)
|
-- set emergency coolant control (if configured)
|
||||||
---@param state boolean true to enable emergency coolant, false to disable
|
---@param state boolean true to enable emergency coolant, false to disable
|
||||||
local function _set_emer_cool(state)
|
local function _set_emer_cool(state)
|
||||||
@@ -167,25 +192,20 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check if the reactor is formed
|
-- check if the reactor is formed
|
||||||
local function _is_formed()
|
local function _is_formed()
|
||||||
local formed = reactor.isFormed()
|
local formed = reactor.isFormed()
|
||||||
if formed == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(formed) then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
else
|
|
||||||
self.formed = formed
|
self.formed = formed
|
||||||
|
end
|
||||||
|
|
||||||
if not self.state[state_keys.sys_fail] then
|
-- always update, since some ppm failures constitute not being formed
|
||||||
self.state[state_keys.sys_fail] = not formed
|
if not self.state[state_keys.sys_fail] then
|
||||||
end
|
self.state[state_keys.sys_fail] = not self.formed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if the reactor is force disabled
|
-- check if the reactor is force disabled
|
||||||
local function _is_force_disabled()
|
local function _is_force_disabled()
|
||||||
local disabled = reactor.isForceDisabled()
|
local disabled = reactor.isForceDisabled()
|
||||||
if disabled == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(disabled) then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
else
|
|
||||||
self.force_disabled = disabled
|
self.force_disabled = disabled
|
||||||
|
|
||||||
if not self.state[state_keys.force_disabled] then
|
if not self.state[state_keys.force_disabled] then
|
||||||
@@ -197,22 +217,16 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check for high damage
|
-- check for high damage
|
||||||
local function _high_damage()
|
local function _high_damage()
|
||||||
local damage_percent = reactor.getDamagePercent()
|
local damage_percent = reactor.getDamagePercent()
|
||||||
if damage_percent == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(damage_percent) and not self.state[state_keys.high_dmg] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.high_dmg] then
|
|
||||||
self.state[state_keys.high_dmg] = damage_percent >= RPS_LIMITS.MAX_DAMAGE_PERCENT
|
self.state[state_keys.high_dmg] = damage_percent >= RPS_LIMITS.MAX_DAMAGE_PERCENT
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check if the reactor is at a critically high temperature
|
-- check if the reactor is at a critically high temperature
|
||||||
local function _high_temp()
|
local function _high_temp()
|
||||||
-- mekanism: MAX_DAMAGE_TEMPERATURE = 1_200
|
-- mekanism: MAX_DAMAGE_TEMPERATURE = 1200K
|
||||||
local temp = reactor.getTemperature()
|
local temp = reactor.getTemperature()
|
||||||
if temp == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(temp) and not self.state[state_keys.high_temp] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.high_temp] then
|
|
||||||
self.state[state_keys.high_temp] = temp >= RPS_LIMITS.MAX_DAMAGE_TEMPERATURE
|
self.state[state_keys.high_temp] = temp >= RPS_LIMITS.MAX_DAMAGE_TEMPERATURE
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -220,10 +234,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check if there is very low coolant
|
-- check if there is very low coolant
|
||||||
local function _low_coolant()
|
local function _low_coolant()
|
||||||
local coolant_filled = reactor.getCoolantFilledPercentage()
|
local coolant_filled = reactor.getCoolantFilledPercentage()
|
||||||
if coolant_filled == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(coolant_filled) and not self.state[state_keys.low_coolant] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.low_coolant] then
|
|
||||||
self.state[state_keys.low_coolant] = coolant_filled < RPS_LIMITS.MIN_COOLANT_FILL
|
self.state[state_keys.low_coolant] = coolant_filled < RPS_LIMITS.MIN_COOLANT_FILL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -231,10 +242,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check for excess waste (>80% filled)
|
-- check for excess waste (>80% filled)
|
||||||
local function _excess_waste()
|
local function _excess_waste()
|
||||||
local w_filled = reactor.getWasteFilledPercentage()
|
local w_filled = reactor.getWasteFilledPercentage()
|
||||||
if w_filled == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(w_filled) and not self.state[state_keys.ex_waste] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.ex_waste] then
|
|
||||||
self.state[state_keys.ex_waste] = w_filled > RPS_LIMITS.MAX_WASTE_FILL
|
self.state[state_keys.ex_waste] = w_filled > RPS_LIMITS.MAX_WASTE_FILL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -242,10 +250,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check for heated coolant backup (>95% filled)
|
-- check for heated coolant backup (>95% filled)
|
||||||
local function _excess_heated_coolant()
|
local function _excess_heated_coolant()
|
||||||
local hc_filled = reactor.getHeatedCoolantFilledPercentage()
|
local hc_filled = reactor.getHeatedCoolantFilledPercentage()
|
||||||
if hc_filled == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(hc_filled) and not self.state[state_keys.ex_hcoolant] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.ex_hcoolant] then
|
|
||||||
self.state[state_keys.ex_hcoolant] = hc_filled > RPS_LIMITS.MAX_HEATED_COLLANT_FILL
|
self.state[state_keys.ex_hcoolant] = hc_filled > RPS_LIMITS.MAX_HEATED_COLLANT_FILL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -253,10 +258,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
-- check if there is no fuel
|
-- check if there is no fuel
|
||||||
local function _insufficient_fuel()
|
local function _insufficient_fuel()
|
||||||
local fuel = reactor.getFuelFilledPercentage()
|
local fuel = reactor.getFuelFilledPercentage()
|
||||||
if fuel == ppm.ACCESS_FAULT then
|
if _check_and_handle_ppm_call(fuel) and not self.state[state_keys.no_fuel] then
|
||||||
-- lost the peripheral or terminated, handled later
|
|
||||||
_set_fault()
|
|
||||||
elseif not self.state[state_keys.no_fuel] then
|
|
||||||
self.state[state_keys.no_fuel] = fuel <= RPS_LIMITS.NO_FUEL_FILL
|
self.state[state_keys.no_fuel] = fuel <= RPS_LIMITS.NO_FUEL_FILL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -305,7 +307,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
log.info("RPS: reactor SCRAM")
|
log.info("RPS: reactor SCRAM")
|
||||||
|
|
||||||
reactor.scram()
|
reactor.scram()
|
||||||
if reactor.__p_is_faulted() and (reactor.__p_last_fault() ~= PCALL_SCRAM_MSG) then
|
if reactor.__p_is_faulted() and not string.find(reactor.__p_last_fault(), PCALL_SCRAM_MSG) then
|
||||||
log.error("RPS: failed reactor SCRAM")
|
log.error("RPS: failed reactor SCRAM")
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
@@ -323,7 +325,7 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
log.info("RPS: reactor start")
|
log.info("RPS: reactor start")
|
||||||
|
|
||||||
reactor.activate()
|
reactor.activate()
|
||||||
if reactor.__p_is_faulted() and (reactor.__p_last_fault() ~= PCALL_START_MSG) then
|
if reactor.__p_is_faulted() and not string.find(reactor.__p_last_fault(), PCALL_START_MSG) then
|
||||||
log.error("RPS: failed reactor start")
|
log.error("RPS: failed reactor start")
|
||||||
else
|
else
|
||||||
self.reactor_enabled = true
|
self.reactor_enabled = true
|
||||||
@@ -477,13 +479,22 @@ function plc.rps_init(reactor, is_formed)
|
|||||||
self.tripped = false
|
self.tripped = false
|
||||||
self.trip_cause = RPS_TRIP_CAUSE.OK
|
self.trip_cause = RPS_TRIP_CAUSE.OK
|
||||||
|
|
||||||
for i = 1, #self.state do
|
for i = 1, #self.state do self.state[i] = false end
|
||||||
self.state[i] = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if not quiet then log.info("RPS: reset") end
|
if not quiet then log.info("RPS: reset") end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- partial RPS reset that only clears fault and sys_fail
|
||||||
|
function public.reset_formed()
|
||||||
|
self.tripped = false
|
||||||
|
self.trip_cause = RPS_TRIP_CAUSE.OK
|
||||||
|
|
||||||
|
self.state[state_keys.fault] = false
|
||||||
|
self.state[state_keys.sys_fail] = false
|
||||||
|
|
||||||
|
log.info("RPS: partial reset on formed")
|
||||||
|
end
|
||||||
|
|
||||||
-- reset the automatic and timeout trip flags, then clear trip if that was the trip cause
|
-- reset the automatic and timeout trip flags, then clear trip if that was the trip cause
|
||||||
function public.auto_reset()
|
function public.auto_reset()
|
||||||
self.state[state_keys.automatic] = false
|
self.state[state_keys.automatic] = false
|
||||||
|
|||||||
@@ -18,11 +18,16 @@ local ui = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- try to start the UI
|
-- try to start the UI
|
||||||
|
---@param theme FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE color mode
|
||||||
---@return boolean success, any error_msg
|
---@return boolean success, any error_msg
|
||||||
function renderer.try_start_ui()
|
function renderer.try_start_ui(theme, color_mode)
|
||||||
local status, msg = true, nil
|
local status, msg = true, nil
|
||||||
|
|
||||||
if ui.display == nil then
|
if ui.display == nil then
|
||||||
|
-- set theme
|
||||||
|
style.set_theme(theme, color_mode)
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
term.setTextColor(colors.white)
|
term.setTextColor(colors.white)
|
||||||
term.setBackgroundColor(colors.black)
|
term.setBackgroundColor(colors.black)
|
||||||
@@ -30,13 +35,19 @@ function renderer.try_start_ui()
|
|||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
|
|
||||||
-- set overridden colors
|
-- set overridden colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
term.setPaletteColor(style.theme.colors[i].c, style.theme.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply color mode
|
||||||
|
local c_mode_overrides = style.theme.color_modes[color_mode]
|
||||||
|
for i = 1, #c_mode_overrides do
|
||||||
|
term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init front panel view
|
-- init front panel view
|
||||||
status, msg = pcall(function ()
|
status, msg = pcall(function ()
|
||||||
ui.display = DisplayBox{window=term.current(),fg_bg=style.root}
|
ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root}
|
||||||
panel_view(ui.display)
|
panel_view(ui.display)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -64,9 +75,9 @@ function renderer.close_ui()
|
|||||||
ui.display = nil
|
ui.display = nil
|
||||||
|
|
||||||
-- restore colors
|
-- restore colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
local r, g, b = term.nativePaletteColor(style.theme.colors[i].c)
|
||||||
term.setPaletteColor(style.colors[i].c, r, g, b)
|
term.setPaletteColor(style.theme.colors[i].c, r, g, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
|
|||||||
@@ -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.11"
|
local R_PLC_VERSION = "v1.7.11"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@@ -31,9 +31,13 @@ if not plc.load_config() then
|
|||||||
-- try to reconfigure (user action)
|
-- try to reconfigure (user action)
|
||||||
local success, error = configure.configure(true)
|
local success, error = configure.configure(true)
|
||||||
if success then
|
if success then
|
||||||
assert(plc.load_config(), "failed to load valid configuration")
|
if not plc.load_config() then
|
||||||
|
println("failed to load a valid configuration, please reconfigure")
|
||||||
|
return
|
||||||
|
end
|
||||||
else
|
else
|
||||||
assert(success, "reactor PLC configuration error: " .. error)
|
println("configuration error: " .. error)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -51,6 +55,7 @@ log.info("========================================")
|
|||||||
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
|
println(">> Reactor PLC " .. R_PLC_VERSION .. " <<")
|
||||||
|
|
||||||
crash.set_env("reactor-plc", R_PLC_VERSION)
|
crash.set_env("reactor-plc", R_PLC_VERSION)
|
||||||
|
crash.dbg_log_env()
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main application
|
-- main application
|
||||||
@@ -131,13 +136,13 @@ local function main()
|
|||||||
|
|
||||||
-- we need a reactor, can at least do some things even if it isn't formed though
|
-- we need a reactor, can at least do some things even if it isn't formed though
|
||||||
if plc_state.no_reactor then
|
if plc_state.no_reactor then
|
||||||
println("init> fission reactor not found");
|
println("init> fission reactor not found")
|
||||||
log.warning("init> no reactor on startup")
|
log.warning("init> no reactor on startup")
|
||||||
|
|
||||||
plc_state.init_ok = false
|
plc_state.init_ok = false
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
elseif not smem_dev.reactor.isFormed() then
|
elseif not smem_dev.reactor.isFormed() then
|
||||||
println("init> fission reactor not formed");
|
println("init> fission reactor is not formed")
|
||||||
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
log.warning("init> reactor logic adapter present, but reactor is not formed")
|
||||||
|
|
||||||
plc_state.degraded = true
|
plc_state.degraded = true
|
||||||
@@ -172,8 +177,9 @@ local function main()
|
|||||||
-- front panel time!
|
-- front panel time!
|
||||||
if not renderer.ui_ready() then
|
if not renderer.ui_ready() then
|
||||||
local message
|
local message
|
||||||
plc_state.fp_ok, message = renderer.try_start_ui()
|
plc_state.fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
|
||||||
|
|
||||||
|
-- ...or not
|
||||||
if not plc_state.fp_ok then
|
if not plc_state.fp_ok then
|
||||||
println_ts(util.c("UI error: ", message))
|
println_ts(util.c("UI error: ", message))
|
||||||
println("init> running without front panel")
|
println("init> running without front panel")
|
||||||
|
|||||||
@@ -71,77 +71,49 @@ function threads.thread__main(smem, init)
|
|||||||
-- blink heartbeat indicator
|
-- blink heartbeat indicator
|
||||||
databus.heartbeat()
|
databus.heartbeat()
|
||||||
|
|
||||||
-- core clock tick
|
-- start next clock timer
|
||||||
if networked then
|
loop_clock.start()
|
||||||
-- start next clock timer
|
|
||||||
loop_clock.start()
|
|
||||||
|
|
||||||
-- send updated data
|
-- send updated data
|
||||||
if nic.is_connected() then
|
if networked and nic.is_connected() then
|
||||||
if plc_comms.is_linked() then
|
if plc_comms.is_linked() then
|
||||||
smem.q.mq_comms_tx.push_command(MQ__COMM_CMD.SEND_STATUS)
|
smem.q.mq_comms_tx.push_command(MQ__COMM_CMD.SEND_STATUS)
|
||||||
|
else
|
||||||
|
if ticks_to_update == 0 then
|
||||||
|
plc_comms.send_link_req()
|
||||||
|
ticks_to_update = LINK_TICKS
|
||||||
else
|
else
|
||||||
if ticks_to_update == 0 then
|
ticks_to_update = ticks_to_update - 1
|
||||||
plc_comms.send_link_req()
|
|
||||||
ticks_to_update = LINK_TICKS
|
|
||||||
else
|
|
||||||
ticks_to_update = ticks_to_update - 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- are we now formed after waiting to be formed?
|
-- check for formed state change
|
||||||
if (not plc_state.reactor_formed) and rps.is_formed() then
|
if (not plc_state.reactor_formed) and rps.is_formed() then
|
||||||
-- push a connect event and unmount it from the PPM
|
-- reactor now formed
|
||||||
local iface = ppm.get_iface(plc_dev.reactor)
|
plc_state.reactor_formed = true
|
||||||
if iface then
|
|
||||||
log.info("unmounting and remounting unformed reactor")
|
|
||||||
ppm.unmount(plc_dev.reactor)
|
|
||||||
|
|
||||||
local type, device = ppm.mount(iface)
|
println_ts("reactor is now formed.")
|
||||||
|
log.info("reactor is now formed")
|
||||||
|
|
||||||
if type == "fissionReactorLogicAdapter" and device ~= nil then
|
-- SCRAM newly formed reactor
|
||||||
-- reconnect reactor
|
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
||||||
plc_dev.reactor = device
|
|
||||||
|
|
||||||
-- we need to assume formed here as we cannot check in this main loop
|
-- determine if we are still in a degraded state
|
||||||
-- RPS will identify if it isn't and this will get set false later
|
if (not networked) or nic.is_connected() then
|
||||||
plc_state.reactor_formed = true
|
plc_state.degraded = false
|
||||||
|
|
||||||
println_ts("reactor reconnected.")
|
|
||||||
log.info("reactor reconnected")
|
|
||||||
|
|
||||||
-- SCRAM newly connected reactor
|
|
||||||
smem.q.mq_rps.push_command(MQ__RPS_CMD.SCRAM)
|
|
||||||
|
|
||||||
-- determine if we are still in a degraded state
|
|
||||||
if (not networked) or nic.is_connected() then
|
|
||||||
plc_state.degraded = false
|
|
||||||
end
|
|
||||||
|
|
||||||
rps.reconnect_reactor(plc_dev.reactor)
|
|
||||||
if networked then
|
|
||||||
plc_comms.reconnect_reactor(plc_dev.reactor)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- reset RPS for newly connected reactor
|
|
||||||
-- without this, is_formed will be out of date and cause it to think its no longer formed again
|
|
||||||
rps.reset()
|
|
||||||
else
|
|
||||||
-- fully lost the reactor now :(
|
|
||||||
println_ts("reactor lost (failed reconnect)!")
|
|
||||||
log.error("reactor lost (failed reconnect)")
|
|
||||||
|
|
||||||
plc_state.no_reactor = true
|
|
||||||
plc_state.degraded = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.error("failed to get interface of previously connected reactor", true)
|
|
||||||
end
|
end
|
||||||
elseif not rps.is_formed() then
|
|
||||||
|
-- partial reset of RPS, specific to becoming formed
|
||||||
|
-- without this, auto control can't resume on chunk load
|
||||||
|
rps.reset_formed()
|
||||||
|
elseif plc_state.reactor_formed and not rps.is_formed() then
|
||||||
-- reactor no longer formed
|
-- reactor no longer formed
|
||||||
|
println_ts("reactor is no longer formed.")
|
||||||
|
log.info("reactor is no longer formed")
|
||||||
|
|
||||||
plc_state.reactor_formed = false
|
plc_state.reactor_formed = false
|
||||||
|
plc_state.degraded = true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update indicators
|
-- update indicators
|
||||||
@@ -231,9 +203,9 @@ function threads.thread__main(smem, init)
|
|||||||
plc_comms.reconnect_reactor(plc_dev.reactor)
|
plc_comms.reconnect_reactor(plc_dev.reactor)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset RPS for newly connected reactor
|
-- partial reset of RPS, specific to becoming formed/reconnected
|
||||||
-- without this, is_formed will be out of date and cause it to think its no longer formed again
|
-- without this, auto control can't resume on chunk load
|
||||||
rps.reset()
|
rps.reset_formed()
|
||||||
end
|
end
|
||||||
elseif networked and type == "modem" then
|
elseif networked and type == "modem" then
|
||||||
-- note, check init_ok first since nic will be nil if it is false
|
-- note, check init_ok first since nic will be nil if it is false
|
||||||
@@ -370,9 +342,9 @@ function threads.thread__rps(smem)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if we are in standalone mode, continuously reset RPS
|
-- if we are in standalone mode and the front panel isn't working, continuously reset RPS
|
||||||
-- RPS will trip again if there are faults, but if it isn't cleared, the user can't re-enable
|
-- RPS will trip again if there are faults, but if it isn't cleared, the user can't re-enable
|
||||||
if not networked then rps.reset(true) end
|
if not (networked or smem.plc_state.fp_ok) then rps.reset(true) end
|
||||||
|
|
||||||
-- check safety (SCRAM occurs if tripped)
|
-- check safety (SCRAM occurs if tripped)
|
||||||
if not plc_state.no_reactor then
|
if not plc_state.no_reactor then
|
||||||
@@ -664,8 +636,9 @@ function threads.thread__setpoint_control(smem)
|
|||||||
if (type(cur_burn_rate) == "number") and (setpoints.burn_rate ~= cur_burn_rate) and rps.is_active() then
|
if (type(cur_burn_rate) == "number") and (setpoints.burn_rate ~= cur_burn_rate) and rps.is_active() then
|
||||||
last_burn_sp = setpoints.burn_rate
|
last_burn_sp = setpoints.burn_rate
|
||||||
|
|
||||||
-- update without ramp if <= 2.5 mB/t change
|
-- update without ramp if <= 2.5 mB/t increase
|
||||||
running = math.abs(setpoints.burn_rate - cur_burn_rate) > 2.5
|
-- no need to ramp down, as the ramp up poses the safety risks
|
||||||
|
running = (setpoints.burn_rate - cur_burn_rate) > 2.5
|
||||||
|
|
||||||
if running then
|
if running then
|
||||||
log.debug(util.c("SPCTL: starting burn rate ramp from ", cur_burn_rate, " mB/t to ", setpoints.burn_rate, " mB/t"))
|
log.debug(util.c("SPCTL: starting burn rate ramp from ", cur_burn_rate, " mB/t to ", setpoints.burn_rate, " mB/t"))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
-- Configuration GUI
|
-- Configuration GUI
|
||||||
--
|
--
|
||||||
|
|
||||||
|
local constants = require("scada-common.constants")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
@@ -9,6 +10,7 @@ local tcd = require("scada-common.tcd")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
@@ -24,56 +26,65 @@ local RadioButton = require("graphics.elements.controls.radio_button")
|
|||||||
local NumberField = require("graphics.elements.form.number_field")
|
local NumberField = require("graphics.elements.form.number_field")
|
||||||
local TextField = require("graphics.elements.form.text_field")
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local IndLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local tri = util.trinary
|
local tri = util.trinary
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local IO = rsio.IO
|
local IO = rsio.IO
|
||||||
|
local IO_LVL = rsio.IO_LVL
|
||||||
|
local IO_MODE = rsio.IO_MODE
|
||||||
|
|
||||||
local LEFT = core.ALIGN.LEFT
|
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
|
||||||
|
|
||||||
-- rsio port descriptions
|
-- rsio port descriptions
|
||||||
local PORT_DESC = {
|
local PORT_DESC_MAP = {
|
||||||
"Facility SCRAM",
|
{ IO.F_SCRAM, "Facility SCRAM" },
|
||||||
"Facility Acknowledge",
|
{ IO.F_ACK, "Facility Acknowledge" },
|
||||||
"Reactor SCRAM",
|
{ IO.R_SCRAM, "Reactor SCRAM" },
|
||||||
"Reactor RPS Reset",
|
{ IO.R_RESET, "Reactor RPS Reset" },
|
||||||
"Reactor Enable",
|
{ IO.R_ENABLE, "Reactor Enable" },
|
||||||
"Unit Acknowledge",
|
{ IO.U_ACK, "Unit Acknowledge" },
|
||||||
"Facility Alarm (high prio)",
|
{ IO.F_ALARM, "Facility Alarm (high prio)" },
|
||||||
"Facility Alarm (any)",
|
{ IO.F_ALARM_ANY, "Facility Alarm (any)" },
|
||||||
"Waste Plutonium Valve",
|
{ IO.F_MATRIX_LOW, "Induction Matrix < " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_LOW) .. "%" },
|
||||||
"Waste Polonium Valve",
|
{ IO.F_MATRIX_HIGH, "Induction Matrix > " .. (100 * constants.RS_THRESHOLDS.IMATRIX_CHARGE_HIGH) .. "%" },
|
||||||
"Waste Po Pellets Valve",
|
{ IO.F_MATRIX_CHG, "Induction Matrix Charge %" },
|
||||||
"Waste Antimatter Valve",
|
{ IO.WASTE_PU, "Waste Plutonium Valve" },
|
||||||
"Reactor Active",
|
{ IO.WASTE_PO, "Waste Polonium Valve" },
|
||||||
"Reactor in Auto Control",
|
{ IO.WASTE_POPL, "Waste Po Pellets Valve" },
|
||||||
"RPS Tripped",
|
{ IO.WASTE_AM, "Waste Antimatter Valve" },
|
||||||
"RPS Auto SCRAM",
|
{ IO.R_ACTIVE, "Reactor Active" },
|
||||||
"RPS High Damage",
|
{ IO.R_AUTO_CTRL, "Reactor in Auto Control" },
|
||||||
"RPS High Temperature",
|
{ IO.R_SCRAMMED, "RPS Tripped" },
|
||||||
"RPS Low Coolant",
|
{ IO.R_AUTO_SCRAM, "RPS Auto SCRAM" },
|
||||||
"RPS Excess Heated Coolant",
|
{ IO.R_HIGH_DMG, "RPS High Damage" },
|
||||||
"RPS Excess Waste",
|
{ IO.R_HIGH_TEMP, "RPS High Temperature" },
|
||||||
"RPS Insufficient Fuel",
|
{ IO.R_LOW_COOLANT, "RPS Low Coolant" },
|
||||||
"RPS PLC Fault",
|
{ IO.R_EXCESS_HC, "RPS Excess Heated Coolant" },
|
||||||
"RPS Supervisor Timeout",
|
{ IO.R_EXCESS_WS, "RPS Excess Waste" },
|
||||||
"Unit Alarm",
|
{ IO.R_INSUFF_FUEL, "RPS Insufficient Fuel" },
|
||||||
"Unit Emergency Cool. Valve"
|
{ IO.R_PLC_FAULT, "RPS PLC Fault" },
|
||||||
|
{ IO.R_PLC_TIMEOUT, "RPS Supervisor Timeout" },
|
||||||
|
{ IO.U_ALARM, "Unit Alarm" },
|
||||||
|
{ IO.U_EMER_COOL, "Unit Emergency Cool. Valve" }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- designation (0 = facility, 1 = unit)
|
-- designation (0 = facility, 1 = unit)
|
||||||
local PORT_DSGN = { [-1] = 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
|
local PORT_DSGN = { [-1] = 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }
|
||||||
|
|
||||||
assert(#PORT_DESC == rsio.NUM_PORTS)
|
assert(#PORT_DESC_MAP == rsio.NUM_PORTS)
|
||||||
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
assert(#PORT_DSGN == rsio.NUM_PORTS)
|
||||||
|
|
||||||
-- changes to the config data/format to let the user know
|
-- 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" } }
|
{ "v1.7.9", { "ConnTimeout can now have a fractional part" } },
|
||||||
|
{ "v1.7.15", { "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
|
{ "v1.9.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class rtu_rs_definition
|
---@class rtu_rs_definition
|
||||||
@@ -98,34 +109,22 @@ 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.colors = {
|
style.colors = themes.smooth_stone.colors
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
local nav_fg_bg = bw_fg_bg
|
local nav_fg_bg = bw_fg_bg
|
||||||
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
||||||
|
|
||||||
|
---@class _rtu_cfg_tool_ctl
|
||||||
local tool_ctl = {
|
local tool_ctl = {
|
||||||
ask_config = false,
|
ask_config = false,
|
||||||
has_config = false,
|
has_config = false,
|
||||||
viewing_config = false,
|
viewing_config = false,
|
||||||
importing_legacy = false,
|
importing_legacy = false,
|
||||||
importing_any_dc = false,
|
importing_any_dc = false,
|
||||||
peri_cfg_editing = false, ---@type string|false
|
jumped_to_color = false,
|
||||||
|
peri_cfg_editing = false, ---@type integer|false
|
||||||
peri_cfg_manual = false,
|
peri_cfg_manual = false,
|
||||||
rs_cfg_port = IO.F_SCRAM, ---@type IO_PORT
|
rs_cfg_port = IO.F_SCRAM, ---@type IO_PORT
|
||||||
rs_cfg_editing = false, ---@type integer|false
|
rs_cfg_editing = false, ---@type integer|false
|
||||||
@@ -133,6 +132,9 @@ local tool_ctl = {
|
|||||||
view_gw_cfg = nil, ---@type graphics_element
|
view_gw_cfg = nil, ---@type graphics_element
|
||||||
dev_cfg = nil, ---@type graphics_element
|
dev_cfg = nil, ---@type graphics_element
|
||||||
rs_cfg = nil, ---@type graphics_element
|
rs_cfg = nil, ---@type graphics_element
|
||||||
|
color_cfg = nil, ---@type graphics_element
|
||||||
|
color_next = nil, ---@type graphics_element
|
||||||
|
color_apply = nil, ---@type graphics_element
|
||||||
settings_apply = nil, ---@type graphics_element
|
settings_apply = nil, ---@type graphics_element
|
||||||
settings_confirm = nil, ---@type graphics_element
|
settings_confirm = nil, ---@type graphics_element
|
||||||
|
|
||||||
@@ -181,7 +183,9 @@ local tmp_cfg = {
|
|||||||
AuthKey = nil, ---@type string|nil
|
AuthKey = nil, ---@type string|nil
|
||||||
LogMode = 0,
|
LogMode = 0,
|
||||||
LogPath = "",
|
LogPath = "",
|
||||||
LogDebug = false
|
LogDebug = false,
|
||||||
|
FrontPanelTheme = 1,
|
||||||
|
ColorMode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class rtu_config
|
---@class rtu_config
|
||||||
@@ -198,7 +202,9 @@ local fields = {
|
|||||||
{ "AuthKey", "Facility Auth Key", "" },
|
{ "AuthKey", "Facility Auth Key", "" },
|
||||||
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
||||||
{ "LogPath", "Log Path", "/log.txt" },
|
{ "LogPath", "Log Path", "/log.txt" },
|
||||||
{ "LogDebug","Log Debug Messages", false }
|
{ "LogDebug", "Log Debug Messages", false },
|
||||||
|
{ "FrontPanelTheme", "Front Panel Theme", themes.FP_THEME.SANDSTONE },
|
||||||
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
||||||
}
|
}
|
||||||
|
|
||||||
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
local side_options = { "Top", "Bottom", "Left", "Right", "Front", "Back" }
|
||||||
@@ -266,12 +272,13 @@ local function config_view(display)
|
|||||||
local spkr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local spkr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
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 clr_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 changelog = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local peri_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local peri_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local rs_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local rs_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
|
||||||
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,spkr_cfg,net_cfg,log_cfg,summary,changelog,peri_cfg,rs_cfg}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,spkr_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog,peri_cfg,rs_cfg}}
|
||||||
|
|
||||||
--#region Main Page
|
--#region Main Page
|
||||||
|
|
||||||
@@ -290,7 +297,7 @@ local function config_view(display)
|
|||||||
tool_ctl.gen_summary(settings_cfg)
|
tool_ctl.gen_summary(settings_cfg)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
tool_ctl.settings_confirm.hide(true)
|
tool_ctl.settings_confirm.hide(true)
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists("/rtu/config.lua") then
|
if fs.exists("/rtu/config.lua") then
|
||||||
@@ -300,12 +307,12 @@ local function config_view(display)
|
|||||||
|
|
||||||
local function show_peri_conns()
|
local function show_peri_conns()
|
||||||
tool_ctl.gen_peri_summary(ini_cfg)
|
tool_ctl.gen_peri_summary(ini_cfg)
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function show_rs_conns()
|
local function show_rs_conns()
|
||||||
tool_ctl.gen_rs_summary(ini_cfg)
|
tool_ctl.gen_rs_summary(ini_cfg)
|
||||||
main_pane.set_value(8)
|
main_pane.set_value(9)
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=main_page,x=2,y=y_start,min_width=19,text="Configure Gateway",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=y_start,min_width=19,text="Configure Gateway",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
@@ -313,15 +320,24 @@ local function config_view(display)
|
|||||||
tool_ctl.dev_cfg = PushButton{parent=main_page,x=2,y=y_start+4,min_width=24,text="Peripheral Connections",callback=show_peri_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.dev_cfg = PushButton{parent=main_page,x=2,y=y_start+4,min_width=24,text="Peripheral Connections",callback=show_peri_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
tool_ctl.rs_cfg = PushButton{parent=main_page,x=2,y=y_start+6,min_width=22,text="Redstone Connections",callback=show_rs_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.rs_cfg = PushButton{parent=main_page,x=2,y=y_start+6,min_width=22,text="Redstone Connections",callback=show_rs_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
|
||||||
|
local function jump_color()
|
||||||
|
tool_ctl.jumped_to_color = true
|
||||||
|
tool_ctl.color_next.hide(true)
|
||||||
|
tool_ctl.color_apply.show()
|
||||||
|
main_pane.set_value(5)
|
||||||
|
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}
|
||||||
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
if not tool_ctl.has_config then
|
if not tool_ctl.has_config then
|
||||||
tool_ctl.view_gw_cfg.disable()
|
tool_ctl.view_gw_cfg.disable()
|
||||||
tool_ctl.dev_cfg.disable()
|
tool_ctl.dev_cfg.disable()
|
||||||
tool_ctl.rs_cfg.disable()
|
tool_ctl.rs_cfg.disable()
|
||||||
|
tool_ctl.color_cfg.disable()
|
||||||
end
|
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}
|
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Speakers
|
--#region Speakers
|
||||||
@@ -432,7 +448,7 @@ local function config_view(display)
|
|||||||
TextBox{parent=net_c_3,x=1,y=11,height=1,text="Facility Auth Key"}
|
TextBox{parent=net_c_3,x=1,y=11,height=1,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=bw_fg_bg}
|
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(tri(enable, "*", nil)) end
|
||||||
|
|
||||||
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
local hide_key = CheckBox{parent=net_c_3,x=34,y=12,label="Hide",box_fg_bg=cpair(colors.lightBlue,colors.black),callback=censor_key}
|
||||||
|
|
||||||
@@ -480,11 +496,8 @@ local function config_view(display)
|
|||||||
tmp_cfg.LogMode = mode.get_value() - 1
|
tmp_cfg.LogMode = mode.get_value() - 1
|
||||||
tmp_cfg.LogPath = path.get_value()
|
tmp_cfg.LogPath = path.get_value()
|
||||||
tmp_cfg.LogDebug = en_dbg.get_value()
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.color_apply.hide(true)
|
||||||
tool_ctl.viewing_config = false
|
tool_ctl.color_next.show()
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
tool_ctl.settings_confirm.hide(true)
|
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(5)
|
||||||
else path_err.show() end
|
else path_err.show() end
|
||||||
end
|
end
|
||||||
@@ -494,6 +507,112 @@ local function config_view(display)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Color Options
|
||||||
|
|
||||||
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
||||||
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
||||||
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
||||||
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
||||||
|
|
||||||
|
local function recolor(value)
|
||||||
|
local c = themes.smooth_stone.color_modes[value]
|
||||||
|
|
||||||
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
b_off.hide()
|
||||||
|
g_off.show()
|
||||||
|
else
|
||||||
|
g_off.hide()
|
||||||
|
b_off.show()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #c == 0 then
|
||||||
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
||||||
|
else
|
||||||
|
term.setPaletteColor(colors.green, c[1].hex)
|
||||||
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
||||||
|
term.setPaletteColor(colors.red, c[3].hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
||||||
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function back_from_colors()
|
||||||
|
main_pane.set_value(tri(tool_ctl.jumped_to_color, 1, 4))
|
||||||
|
tool_ctl.jumped_to_color = false
|
||||||
|
recolor(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_access()
|
||||||
|
clr_pane.set_value(2)
|
||||||
|
recolor(c_mode.get_value())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_colors()
|
||||||
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
||||||
|
tmp_cfg.ColorMode = c_mode.get_value()
|
||||||
|
|
||||||
|
if tool_ctl.jumped_to_color then
|
||||||
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
||||||
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
||||||
|
|
||||||
|
if settings.save("/rtu.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
clr_pane.set_value(3)
|
||||||
|
else
|
||||||
|
clr_pane.set_value(4)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
tool_ctl.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
tool_ctl.settings_confirm.hide(true)
|
||||||
|
main_pane.set_value(6)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
||||||
|
PushButton{parent=clr_c_3,x=1,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=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=clr_c_4,x=1,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=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Summary and Saving
|
--#region Summary and Saving
|
||||||
|
|
||||||
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
@@ -520,7 +639,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.viewing_config = false
|
tool_ctl.viewing_config = false
|
||||||
else main_pane.set_value(4) end
|
else main_pane.set_value(5) end
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param element graphics_element
|
---@param element graphics_element
|
||||||
@@ -552,6 +671,8 @@ local function config_view(display)
|
|||||||
try_set(mode, ini_cfg.LogMode)
|
try_set(mode, ini_cfg.LogMode)
|
||||||
try_set(path, ini_cfg.LogPath)
|
try_set(path, ini_cfg.LogPath)
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
||||||
|
try_set(c_mode, ini_cfg.ColorMode)
|
||||||
|
|
||||||
if not exclude_conns then
|
if not exclude_conns then
|
||||||
tmp_cfg.Peripherals = deep_copy_peri(ini_cfg.Peripherals)
|
tmp_cfg.Peripherals = deep_copy_peri(ini_cfg.Peripherals)
|
||||||
@@ -589,7 +710,20 @@ local function config_view(display)
|
|||||||
PushButton{parent=sum_c_3,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_3,x=1,y=14,text="\x1b Back",callback=function()sum_pane.set_value(2)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=sum_c_3,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=sum_c_3,x=43,y=14,min_width=7,text="Apply",callback=save_and_continue,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function jump_peri_conns()
|
||||||
|
tool_ctl.go_home()
|
||||||
|
show_peri_conns()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function jump_rs_conns()
|
||||||
|
tool_ctl.go_home()
|
||||||
|
show_rs_conns()
|
||||||
|
end
|
||||||
|
|
||||||
TextBox{parent=sum_c_4,x=1,y=1,height=1,text="Settings saved!"}
|
TextBox{parent=sum_c_4,x=1,y=1,height=1,text="Settings saved!"}
|
||||||
|
TextBox{parent=sum_c_4,x=1,y=3,height=4,text="Remember to configure any peripherals or redstone that you have connected to this RTU gateway if you have not already done so, or if you have added, removed, or modified any of them."}
|
||||||
|
PushButton{parent=sum_c_4,x=1,y=8,min_width=24,text="Peripheral Connections",callback=jump_peri_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=sum_c_4,x=1,y=10,min_width=22,text="Redstone Connections",callback=jump_rs_conns,fg_bg=cpair(colors.black,colors.yellow),active_fg_bg=btn_act_fg_bg}
|
||||||
PushButton{parent=sum_c_4,x=1,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=1,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="Home",callback=function()tool_ctl.go_home()end,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="Home",callback=function()tool_ctl.go_home()end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
@@ -769,7 +903,7 @@ local function config_view(display)
|
|||||||
tool_ctl.p_desc.reposition(1, 8)
|
tool_ctl.p_desc.reposition(1, 8)
|
||||||
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
tool_ctl.p_desc.set_value("You can connect more than one environment detector for a particular unit or the facility. In that case, the maximum radiation reading from those assigned to that particular unit or the facility will be used for alarms and display.")
|
||||||
elseif type == "inductionPort" or type == "spsPort" then
|
elseif type == "inductionPort" or type == "spsPort" then
|
||||||
local dev = util.trinary(type == "inductionPort", "induction matrix", "SPS")
|
local dev = tri(type == "inductionPort", "induction matrix", "SPS")
|
||||||
tool_ctl.p_idx.hide(true)
|
tool_ctl.p_idx.hide(true)
|
||||||
tool_ctl.p_unit.hide(true)
|
tool_ctl.p_unit.hide(true)
|
||||||
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
tool_ctl.p_prompt.set_value("This is the " .. dev .. " for the facility.")
|
||||||
@@ -795,7 +929,7 @@ local function config_view(display)
|
|||||||
tool_ctl.ppm_devs.remove_all()
|
tool_ctl.ppm_devs.remove_all()
|
||||||
for name, entry in pairs(mounts) do
|
for name, entry in pairs(mounts) do
|
||||||
if util.table_contains(RTU_DEV_TYPES, entry.type) then
|
if util.table_contains(RTU_DEV_TYPES, entry.type) then
|
||||||
local bkg = util.trinary(alternate, colors.white, colors.lightGray)
|
local bkg = tri(alternate, colors.white, colors.lightGray)
|
||||||
|
|
||||||
---@cast entry ppm_entry
|
---@cast entry ppm_entry
|
||||||
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
local line = Div{parent=tool_ctl.ppm_devs,height=2,fg_bg=cpair(colors.black,bkg)}
|
||||||
@@ -957,8 +1091,9 @@ local function config_view(display)
|
|||||||
local rs_c_4 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_4 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
local rs_c_5 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_5 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
local rs_c_6 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
local rs_c_6 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
|
local rs_c_7 = Div{parent=rs_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6}}
|
local rs_pane = MultiPane{parent=rs_cfg,x=1,y=4,panes={rs_c_1,rs_c_2,rs_c_3,rs_c_4,rs_c_5,rs_c_6,rs_c_7}}
|
||||||
|
|
||||||
TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
TextBox{parent=rs_cfg,x=1,y=2,height=1,text=" Redstone Connections",fg_bg=cpair(colors.black,colors.red)}
|
||||||
|
|
||||||
@@ -1015,9 +1150,23 @@ local function config_view(display)
|
|||||||
text = "You selected the ALL_WASTE shortcut."
|
text = "You selected the ALL_WASTE shortcut."
|
||||||
else
|
else
|
||||||
tool_ctl.rs_cfg_shortcut.hide(true)
|
tool_ctl.rs_cfg_shortcut.hide(true)
|
||||||
tool_ctl.rs_cfg_side_l.set_value(util.trinary(rsio.get_io_dir(port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
tool_ctl.rs_cfg_side_l.set_value(tri(rsio.get_io_dir(port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
||||||
tool_ctl.rs_cfg_color.show()
|
tool_ctl.rs_cfg_color.show()
|
||||||
text = "You selected " .. rsio.to_string(port) .. " (for "
|
|
||||||
|
local io_type = "analog input "
|
||||||
|
local io_mode = rsio.get_io_mode(port)
|
||||||
|
local inv = tri(rsio.digital_is_active(port, IO_LVL.LOW) == true, "inverted ", "")
|
||||||
|
|
||||||
|
if io_mode == IO_MODE.DIGITAL_IN then
|
||||||
|
io_type = inv .. "digital input "
|
||||||
|
elseif io_mode == IO_MODE.DIGITAL_OUT then
|
||||||
|
io_type = inv .. "digital output "
|
||||||
|
elseif io_mode == IO_MODE.ANALOG_OUT then
|
||||||
|
io_type = "analog output "
|
||||||
|
end
|
||||||
|
|
||||||
|
text = "You selected the " .. io_type .. rsio.to_string(port) .. " (for "
|
||||||
|
|
||||||
if PORT_DSGN[port] == 1 then
|
if PORT_DSGN[port] == 1 then
|
||||||
text = text .. "a unit)."
|
text = text .. "a unit)."
|
||||||
tool_ctl.rs_cfg_unit_l.show()
|
tool_ctl.rs_cfg_unit_l.show()
|
||||||
@@ -1039,25 +1188,35 @@ local function config_view(display)
|
|||||||
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=all_w_macro,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">ALL_WASTE",callback=function()new_rs(-1)end,fg_bg=cpair(colors.black,colors.green),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=all_w_macro,x=16,y=1,width=5,height=1,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=all_w_macro,x=16,y=1,width=5,height=1,text="[n/a]",fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=all_w_macro,x=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=all_w_macro,x=22,y=1,height=1,text="Create all 4 waste entries",fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
|
||||||
for i = 1, rsio.NUM_PORTS do
|
for i = 1, rsio.NUM_PORTS do
|
||||||
local name = rsio.to_string(i)
|
local p = PORT_DESC_MAP[i][1]
|
||||||
local io_dir = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, "[in]", "[out]")
|
local name = rsio.to_string(p)
|
||||||
local btn_color = util.trinary(rsio.get_io_dir(i) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
local io_dir = tri(rsio.get_io_dir(p) == rsio.IO_DIR.IN, "[in]", "[out]")
|
||||||
|
local btn_color = tri(rsio.get_io_dir(p) == rsio.IO_DIR.IN, colors.yellow, colors.lightBlue)
|
||||||
|
|
||||||
local entry = Div{parent=rs_ports,height=1}
|
local entry = Div{parent=rs_ports,height=1}
|
||||||
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(i)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
PushButton{parent=entry,x=1,y=1,min_width=14,alignment=LEFT,height=1,text=">"..name,callback=function()new_rs(p)end,fg_bg=cpair(colors.black,btn_color),active_fg_bg=cpair(colors.white,colors.black)}
|
||||||
TextBox{parent=entry,x=16,y=1,width=5,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
TextBox{parent=entry,x=16,y=1,width=5,height=1,text=io_dir,fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
TextBox{parent=entry,x=22,y=1,height=1,text=PORT_DESC[i],fg_bg=cpair(colors.gray,colors.white)}
|
TextBox{parent=entry,x=22,y=1,height=1,text=PORT_DESC_MAP[i][2],fg_bg=cpair(colors.gray,colors.white)}
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=rs_c_2,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_selection = TextBox{parent=rs_c_3,x=1,y=1,height=1,text=""}
|
tool_ctl.rs_cfg_selection = TextBox{parent=rs_c_3,x=1,y=1,height=2,text=""}
|
||||||
|
|
||||||
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=27,y=3,width=7,height=1,text="Unit ID"}
|
PushButton{parent=rs_c_3,x=36,y=3,text="What's that?",min_width=14,callback=function()rs_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_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"}
|
TextBox{parent=rs_c_7,x=1,y=1,height=4,text="(Normal) Digital Input: On if there is a redstone signal, off otherwise\nInverted Digital Input: On without a redstone signal, off otherwise"}
|
||||||
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}
|
TextBox{parent=rs_c_7,x=1,y=6,height=4,text="(Normal) Digital Output: Redstone signal to 'turn it on', none to 'turn it off'\nInverted Digital Output: No redstone signal to 'turn it on', redstone signal to 'turn it off'"}
|
||||||
|
TextBox{parent=rs_c_7,x=1,y=11,height=2,text="Analog Input: 0-15 redstone power level input\nAnalog Output: 0-15 scaled redstone power level output"}
|
||||||
|
PushButton{parent=rs_c_7,x=1,y=14,text="\x1b Back",callback=function()rs_pane.set_value(3)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.rs_cfg_side_l = TextBox{parent=rs_c_3,x=1,y=4,width=11,height=1,text="Output Side"}
|
||||||
|
local side = Radio2D{parent=rs_c_3,x=1,y=5,rows=1,columns=6,default=1,options=side_options,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.red}
|
||||||
|
|
||||||
|
tool_ctl.rs_cfg_unit_l = TextBox{parent=rs_c_3,x=25,y=7,width=7,height=1,text="Unit ID"}
|
||||||
|
tool_ctl.rs_cfg_unit = NumberField{parent=rs_c_3,x=33,y=7,width=10,max_chars=2,min=1,max=4,fg_bg=bw_fg_bg}
|
||||||
|
|
||||||
local function set_bundled(bundled)
|
local function set_bundled(bundled)
|
||||||
if bundled then tool_ctl.rs_cfg_color.enable() else tool_ctl.rs_cfg_color.disable() end
|
if bundled then tool_ctl.rs_cfg_color.enable() else tool_ctl.rs_cfg_color.disable() end
|
||||||
@@ -1088,10 +1247,10 @@ local function config_view(display)
|
|||||||
if port >= 0 then
|
if port >= 0 then
|
||||||
---@type rtu_rs_definition
|
---@type rtu_rs_definition
|
||||||
local def = {
|
local def = {
|
||||||
unit = util.trinary(PORT_DSGN[port] == 1, u, nil),
|
unit = tri(PORT_DSGN[port] == 1, u, nil),
|
||||||
port = port,
|
port = port,
|
||||||
side = side_options_map[side.get_value()],
|
side = side_options_map[side.get_value()],
|
||||||
color = util.trinary(bundled.get_value(), color_options_map[tool_ctl.rs_cfg_color.get_value()], nil)
|
color = tri(bundled.get_value(), color_options_map[tool_ctl.rs_cfg_color.get_value()], nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tool_ctl.rs_cfg_editing == false then
|
if tool_ctl.rs_cfg_editing == false then
|
||||||
@@ -1105,10 +1264,10 @@ local function config_view(display)
|
|||||||
local default_colors = { colors.red, colors.orange, colors.yellow, colors.lime }
|
local default_colors = { colors.red, colors.orange, colors.yellow, colors.lime }
|
||||||
for i = 0, 3 do
|
for i = 0, 3 do
|
||||||
table.insert(tmp_cfg.Redstone, {
|
table.insert(tmp_cfg.Redstone, {
|
||||||
unit = util.trinary(PORT_DSGN[IO.WASTE_PU + i] == 1, u, nil),
|
unit = tri(PORT_DSGN[IO.WASTE_PU + i] == 1, u, nil),
|
||||||
port = IO.WASTE_PU + i,
|
port = IO.WASTE_PU + i,
|
||||||
side = util.trinary(bundled.get_value(), side_options_map[side.get_value()], default_sides[i + 1]),
|
side = tri(bundled.get_value(), side_options_map[side.get_value()], default_sides[i + 1]),
|
||||||
color = util.trinary(bundled.get_value(), default_colors[i + 1], nil)
|
color = tri(bundled.get_value(), default_colors[i + 1], nil)
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1161,7 +1320,7 @@ local function config_view(display)
|
|||||||
peri_import_list.remove_all()
|
peri_import_list.remove_all()
|
||||||
for _, entry in ipairs(config.RTU_DEVICES) do
|
for _, entry in ipairs(config.RTU_DEVICES) do
|
||||||
local for_facility = entry.for_reactor == 0
|
local for_facility = entry.for_reactor == 0
|
||||||
local ini_unit = util.trinary(for_facility, nil, entry.for_reactor)
|
local ini_unit = tri(for_facility, nil, entry.for_reactor)
|
||||||
|
|
||||||
local def = { name = entry.name, unit = ini_unit, index = entry.index }
|
local def = { name = entry.name, unit = ini_unit, index = entry.index }
|
||||||
local mount = mounts[def.name] ---@type ppm_entry|nil
|
local mount = mounts[def.name] ---@type ppm_entry|nil
|
||||||
@@ -1240,7 +1399,7 @@ local function config_view(display)
|
|||||||
table.insert(tmp_cfg.Redstone, def)
|
table.insert(tmp_cfg.Redstone, def)
|
||||||
|
|
||||||
local name = rsio.to_string(def.port)
|
local name = rsio.to_string(def.port)
|
||||||
local io_dir = util.trinary(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
local io_dir = tri(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
||||||
local conn = def.side
|
local conn = def.side
|
||||||
local unit = "facility"
|
local unit = "facility"
|
||||||
|
|
||||||
@@ -1257,7 +1416,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
if tool_ctl.importing_any_dc then sum_pane.set_value(7) else sum_pane.set_value(1) end
|
if tool_ctl.importing_any_dc then sum_pane.set_value(7) else sum_pane.set_value(1) end
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
tool_ctl.settings_confirm.show()
|
tool_ctl.settings_confirm.show()
|
||||||
tool_ctl.importing_legacy = true
|
tool_ctl.importing_legacy = true
|
||||||
@@ -1271,6 +1430,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
net_pane.set_value(1)
|
net_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
peri_pane.set_value(1)
|
peri_pane.set_value(1)
|
||||||
rs_pane.set_value(1)
|
rs_pane.set_value(1)
|
||||||
@@ -1301,11 +1461,17 @@ local function config_view(display)
|
|||||||
local raw = cfg[f[1]]
|
local raw = cfg[f[1]]
|
||||||
local val = util.strval(raw)
|
local val = util.strval(raw)
|
||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val)) end
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
||||||
if f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace") end
|
elseif f[1] == "LogMode" then val = tri(raw == log.MODE.APPEND, "append", "replace")
|
||||||
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
|
elseif f[1] == "ColorMode" then
|
||||||
|
val = util.strval(themes.color_mode_name(raw))
|
||||||
|
end
|
||||||
|
|
||||||
if val == "nil" then val = "<not set>" end
|
if val == "nil" then val = "<not set>" end
|
||||||
|
|
||||||
local c = util.trinary(alternate, g_lg_fg_bg, cpair(colors.gray,colors.white))
|
local c = tri(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
|
||||||
@@ -1419,7 +1585,7 @@ local function config_view(display)
|
|||||||
end
|
end
|
||||||
|
|
||||||
tool_ctl.rs_cfg_selection.set_value(text)
|
tool_ctl.rs_cfg_selection.set_value(text)
|
||||||
tool_ctl.rs_cfg_side_l.set_value(util.trinary(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
tool_ctl.rs_cfg_side_l.set_value(tri(rsio.get_io_dir(def.port) == rsio.IO_DIR.IN, "Input Side", "Output Side"))
|
||||||
side.set_value(side_to_idx(def.side))
|
side.set_value(side_to_idx(def.side))
|
||||||
bundled.set_value(def.color ~= nil)
|
bundled.set_value(def.color ~= nil)
|
||||||
tool_ctl.rs_cfg_color.set_value(value)
|
tool_ctl.rs_cfg_color.set_value(value)
|
||||||
@@ -1440,7 +1606,7 @@ local function config_view(display)
|
|||||||
local def = cfg.Redstone[i] ---@type rtu_rs_definition
|
local def = cfg.Redstone[i] ---@type rtu_rs_definition
|
||||||
|
|
||||||
local name = rsio.to_string(def.port)
|
local name = rsio.to_string(def.port)
|
||||||
local io_dir = util.trinary(rsio.get_io_mode(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
local io_dir = tri(rsio.get_io_mode(def.port) == rsio.IO_DIR.IN, "\x1a", "\x1b")
|
||||||
local conn = def.side
|
local conn = def.side
|
||||||
local unit = util.strval(def.unit or "F")
|
local unit = util.strval(def.unit or "F")
|
||||||
|
|
||||||
@@ -1503,9 +1669,11 @@ function configurator.configure(ask_config)
|
|||||||
elseif event == "paste" then
|
elseif event == "paste" then
|
||||||
display.handle_paste(param1)
|
display.handle_paste(param1)
|
||||||
elseif event == "peripheral_detach" then
|
elseif event == "peripheral_detach" then
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
ppm.handle_unmount(param1)
|
ppm.handle_unmount(param1)
|
||||||
tool_ctl.update_peri_list()
|
tool_ctl.update_peri_list()
|
||||||
elseif event == "peripheral" then
|
elseif event == "peripheral" then
|
||||||
|
---@diagnostic disable-next-line: discard-returns
|
||||||
ppm.mount(param1)
|
ppm.mount(param1)
|
||||||
tool_ctl.update_peri_list()
|
tool_ctl.update_peri_list()
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,60 +7,51 @@ local boilerv_rtu = {}
|
|||||||
---@param boiler table
|
---@param boiler table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function boilerv_rtu.new(boiler)
|
function boilerv_rtu.new(boiler)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(boiler)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
|
||||||
boiler.__p_clear_fault()
|
|
||||||
boiler.__p_disable_afc()
|
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
unit.connect_di(boiler.isFormed)
|
unit.connect_di("isFormed")
|
||||||
|
|
||||||
-- coils --
|
-- coils --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- input registers --
|
-- input registers --
|
||||||
-- multiblock properties
|
-- multiblock properties
|
||||||
unit.connect_input_reg(boiler.getLength)
|
unit.connect_input_reg("getLength")
|
||||||
unit.connect_input_reg(boiler.getWidth)
|
unit.connect_input_reg("getWidth")
|
||||||
unit.connect_input_reg(boiler.getHeight)
|
unit.connect_input_reg("getHeight")
|
||||||
unit.connect_input_reg(boiler.getMinPos)
|
unit.connect_input_reg("getMinPos")
|
||||||
unit.connect_input_reg(boiler.getMaxPos)
|
unit.connect_input_reg("getMaxPos")
|
||||||
-- build properties
|
-- build properties
|
||||||
unit.connect_input_reg(boiler.getBoilCapacity)
|
unit.connect_input_reg("getBoilCapacity")
|
||||||
unit.connect_input_reg(boiler.getSteamCapacity)
|
unit.connect_input_reg("getSteamCapacity")
|
||||||
unit.connect_input_reg(boiler.getWaterCapacity)
|
unit.connect_input_reg("getWaterCapacity")
|
||||||
unit.connect_input_reg(boiler.getHeatedCoolantCapacity)
|
unit.connect_input_reg("getHeatedCoolantCapacity")
|
||||||
unit.connect_input_reg(boiler.getCooledCoolantCapacity)
|
unit.connect_input_reg("getCooledCoolantCapacity")
|
||||||
unit.connect_input_reg(boiler.getSuperheaters)
|
unit.connect_input_reg("getSuperheaters")
|
||||||
unit.connect_input_reg(boiler.getMaxBoilRate)
|
unit.connect_input_reg("getMaxBoilRate")
|
||||||
-- current state
|
-- current state
|
||||||
unit.connect_input_reg(boiler.getTemperature)
|
unit.connect_input_reg("getTemperature")
|
||||||
unit.connect_input_reg(boiler.getBoilRate)
|
unit.connect_input_reg("getBoilRate")
|
||||||
unit.connect_input_reg(boiler.getEnvironmentalLoss)
|
unit.connect_input_reg("getEnvironmentalLoss")
|
||||||
-- tanks
|
-- tanks
|
||||||
unit.connect_input_reg(boiler.getSteam)
|
unit.connect_input_reg("getSteam")
|
||||||
unit.connect_input_reg(boiler.getSteamNeeded)
|
unit.connect_input_reg("getSteamNeeded")
|
||||||
unit.connect_input_reg(boiler.getSteamFilledPercentage)
|
unit.connect_input_reg("getSteamFilledPercentage")
|
||||||
unit.connect_input_reg(boiler.getWater)
|
unit.connect_input_reg("getWater")
|
||||||
unit.connect_input_reg(boiler.getWaterNeeded)
|
unit.connect_input_reg("getWaterNeeded")
|
||||||
unit.connect_input_reg(boiler.getWaterFilledPercentage)
|
unit.connect_input_reg("getWaterFilledPercentage")
|
||||||
unit.connect_input_reg(boiler.getHeatedCoolant)
|
unit.connect_input_reg("getHeatedCoolant")
|
||||||
unit.connect_input_reg(boiler.getHeatedCoolantNeeded)
|
unit.connect_input_reg("getHeatedCoolantNeeded")
|
||||||
unit.connect_input_reg(boiler.getHeatedCoolantFilledPercentage)
|
unit.connect_input_reg("getHeatedCoolantFilledPercentage")
|
||||||
unit.connect_input_reg(boiler.getCooledCoolant)
|
unit.connect_input_reg("getCooledCoolant")
|
||||||
unit.connect_input_reg(boiler.getCooledCoolantNeeded)
|
unit.connect_input_reg("getCooledCoolantNeeded")
|
||||||
unit.connect_input_reg(boiler.getCooledCoolantFilledPercentage)
|
unit.connect_input_reg("getCooledCoolantFilledPercentage")
|
||||||
|
|
||||||
-- holding registers --
|
-- holding registers --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- check if any calls faulted
|
return unit.interface(), false
|
||||||
local faulted = boiler.__p_is_faulted()
|
|
||||||
boiler.__p_clear_fault()
|
|
||||||
boiler.__p_enable_afc()
|
|
||||||
|
|
||||||
return unit.interface(), faulted
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return boilerv_rtu
|
return boilerv_rtu
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ local dynamicv_rtu = {}
|
|||||||
---@param dynamic_tank table
|
---@param dynamic_tank table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function dynamicv_rtu.new(dynamic_tank)
|
function dynamicv_rtu.new(dynamic_tank)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(dynamic_tank)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
|
||||||
dynamic_tank.__p_clear_fault()
|
|
||||||
dynamic_tank.__p_disable_afc()
|
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
unit.connect_di(dynamic_tank.isFormed)
|
unit.connect_di("isFormed")
|
||||||
|
|
||||||
-- coils --
|
-- coils --
|
||||||
unit.connect_coil(function () dynamic_tank.incrementContainerEditMode() end, function () end)
|
unit.connect_coil(function () dynamic_tank.incrementContainerEditMode() end, function () end)
|
||||||
@@ -22,27 +18,22 @@ function dynamicv_rtu.new(dynamic_tank)
|
|||||||
|
|
||||||
-- input registers --
|
-- input registers --
|
||||||
-- multiblock properties
|
-- multiblock properties
|
||||||
unit.connect_input_reg(dynamic_tank.getLength)
|
unit.connect_input_reg("getLength")
|
||||||
unit.connect_input_reg(dynamic_tank.getWidth)
|
unit.connect_input_reg("getWidth")
|
||||||
unit.connect_input_reg(dynamic_tank.getHeight)
|
unit.connect_input_reg("getHeight")
|
||||||
unit.connect_input_reg(dynamic_tank.getMinPos)
|
unit.connect_input_reg("getMinPos")
|
||||||
unit.connect_input_reg(dynamic_tank.getMaxPos)
|
unit.connect_input_reg("getMaxPos")
|
||||||
-- build properties
|
-- build properties
|
||||||
unit.connect_input_reg(dynamic_tank.getTankCapacity)
|
unit.connect_input_reg("getTankCapacity")
|
||||||
unit.connect_input_reg(dynamic_tank.getChemicalTankCapacity)
|
unit.connect_input_reg("getChemicalTankCapacity")
|
||||||
-- tanks/containers
|
-- tanks/containers
|
||||||
unit.connect_input_reg(dynamic_tank.getStored)
|
unit.connect_input_reg("getStored")
|
||||||
unit.connect_input_reg(dynamic_tank.getFilledPercentage)
|
unit.connect_input_reg("getFilledPercentage")
|
||||||
|
|
||||||
-- holding registers --
|
-- holding registers --
|
||||||
unit.connect_holding_reg(dynamic_tank.getContainerEditMode, dynamic_tank.setContainerEditMode)
|
unit.connect_holding_reg("getContainerEditMode", "setContainerEditMode")
|
||||||
|
|
||||||
-- check if any calls faulted
|
return unit.interface(), false
|
||||||
local faulted = dynamic_tank.__p_is_faulted()
|
|
||||||
dynamic_tank.__p_clear_fault()
|
|
||||||
dynamic_tank.__p_enable_afc()
|
|
||||||
|
|
||||||
return unit.interface(), faulted
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return dynamicv_rtu
|
return dynamicv_rtu
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ local envd_rtu = {}
|
|||||||
---@param envd table
|
---@param envd table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function envd_rtu.new(envd)
|
function envd_rtu.new(envd)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(envd)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
envd.__p_clear_fault()
|
envd.__p_clear_fault()
|
||||||
|
|||||||
@@ -7,47 +7,38 @@ local imatrix_rtu = {}
|
|||||||
---@param imatrix table
|
---@param imatrix table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function imatrix_rtu.new(imatrix)
|
function imatrix_rtu.new(imatrix)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(imatrix)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
|
||||||
imatrix.__p_clear_fault()
|
|
||||||
imatrix.__p_disable_afc()
|
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
unit.connect_di(imatrix.isFormed)
|
unit.connect_di("isFormed")
|
||||||
|
|
||||||
-- coils --
|
-- coils --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- input registers --
|
-- input registers --
|
||||||
-- multiblock properties
|
-- multiblock properties
|
||||||
unit.connect_input_reg(imatrix.getLength)
|
unit.connect_input_reg("getLength")
|
||||||
unit.connect_input_reg(imatrix.getWidth)
|
unit.connect_input_reg("getWidth")
|
||||||
unit.connect_input_reg(imatrix.getHeight)
|
unit.connect_input_reg("getHeight")
|
||||||
unit.connect_input_reg(imatrix.getMinPos)
|
unit.connect_input_reg("getMinPos")
|
||||||
unit.connect_input_reg(imatrix.getMaxPos)
|
unit.connect_input_reg("getMaxPos")
|
||||||
-- build properties
|
-- build properties
|
||||||
unit.connect_input_reg(imatrix.getMaxEnergy)
|
unit.connect_input_reg("getMaxEnergy")
|
||||||
unit.connect_input_reg(imatrix.getTransferCap)
|
unit.connect_input_reg("getTransferCap")
|
||||||
unit.connect_input_reg(imatrix.getInstalledCells)
|
unit.connect_input_reg("getInstalledCells")
|
||||||
unit.connect_input_reg(imatrix.getInstalledProviders)
|
unit.connect_input_reg("getInstalledProviders")
|
||||||
-- I/O rates
|
-- I/O rates
|
||||||
unit.connect_input_reg(imatrix.getLastInput)
|
unit.connect_input_reg("getLastInput")
|
||||||
unit.connect_input_reg(imatrix.getLastOutput)
|
unit.connect_input_reg("getLastOutput")
|
||||||
-- tanks
|
-- tanks
|
||||||
unit.connect_input_reg(imatrix.getEnergy)
|
unit.connect_input_reg("getEnergy")
|
||||||
unit.connect_input_reg(imatrix.getEnergyNeeded)
|
unit.connect_input_reg("getEnergyNeeded")
|
||||||
unit.connect_input_reg(imatrix.getEnergyFilledPercentage)
|
unit.connect_input_reg("getEnergyFilledPercentage")
|
||||||
|
|
||||||
-- holding registers --
|
-- holding registers --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- check if any calls faulted
|
return unit.interface(), false
|
||||||
local faulted = imatrix.__p_is_faulted()
|
|
||||||
imatrix.__p_clear_fault()
|
|
||||||
imatrix.__p_enable_afc()
|
|
||||||
|
|
||||||
return unit.interface(), faulted
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return imatrix_rtu
|
return imatrix_rtu
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ local sna_rtu = {}
|
|||||||
---@param sna table
|
---@param sna table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function sna_rtu.new(sna)
|
function sna_rtu.new(sna)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(sna)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
-- disable auto fault clearing
|
||||||
sna.__p_clear_fault()
|
sna.__p_clear_fault()
|
||||||
|
|||||||
@@ -7,52 +7,43 @@ local sps_rtu = {}
|
|||||||
---@param sps table
|
---@param sps table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function sps_rtu.new(sps)
|
function sps_rtu.new(sps)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(sps)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
|
||||||
sps.__p_clear_fault()
|
|
||||||
sps.__p_disable_afc()
|
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
unit.connect_di(sps.isFormed)
|
unit.connect_di("isFormed")
|
||||||
|
|
||||||
-- coils --
|
-- coils --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- input registers --
|
-- input registers --
|
||||||
-- multiblock properties
|
-- multiblock properties
|
||||||
unit.connect_input_reg(sps.getLength)
|
unit.connect_input_reg("getLength")
|
||||||
unit.connect_input_reg(sps.getWidth)
|
unit.connect_input_reg("getWidth")
|
||||||
unit.connect_input_reg(sps.getHeight)
|
unit.connect_input_reg("getHeight")
|
||||||
unit.connect_input_reg(sps.getMinPos)
|
unit.connect_input_reg("getMinPos")
|
||||||
unit.connect_input_reg(sps.getMaxPos)
|
unit.connect_input_reg("getMaxPos")
|
||||||
-- build properties
|
-- build properties
|
||||||
unit.connect_input_reg(sps.getCoils)
|
unit.connect_input_reg("getCoils")
|
||||||
unit.connect_input_reg(sps.getInputCapacity)
|
unit.connect_input_reg("getInputCapacity")
|
||||||
unit.connect_input_reg(sps.getOutputCapacity)
|
unit.connect_input_reg("getOutputCapacity")
|
||||||
unit.connect_input_reg(sps.getMaxEnergy)
|
unit.connect_input_reg("getMaxEnergy")
|
||||||
-- current state
|
-- current state
|
||||||
unit.connect_input_reg(sps.getProcessRate)
|
unit.connect_input_reg("getProcessRate")
|
||||||
-- tanks
|
-- tanks
|
||||||
unit.connect_input_reg(sps.getInput)
|
unit.connect_input_reg("getInput")
|
||||||
unit.connect_input_reg(sps.getInputNeeded)
|
unit.connect_input_reg("getInputNeeded")
|
||||||
unit.connect_input_reg(sps.getInputFilledPercentage)
|
unit.connect_input_reg("getInputFilledPercentage")
|
||||||
unit.connect_input_reg(sps.getOutput)
|
unit.connect_input_reg("getOutput")
|
||||||
unit.connect_input_reg(sps.getOutputNeeded)
|
unit.connect_input_reg("getOutputNeeded")
|
||||||
unit.connect_input_reg(sps.getOutputFilledPercentage)
|
unit.connect_input_reg("getOutputFilledPercentage")
|
||||||
unit.connect_input_reg(sps.getEnergy)
|
unit.connect_input_reg("getEnergy")
|
||||||
unit.connect_input_reg(sps.getEnergyNeeded)
|
unit.connect_input_reg("getEnergyNeeded")
|
||||||
unit.connect_input_reg(sps.getEnergyFilledPercentage)
|
unit.connect_input_reg("getEnergyFilledPercentage")
|
||||||
|
|
||||||
-- holding registers --
|
-- holding registers --
|
||||||
-- none
|
-- none
|
||||||
|
|
||||||
-- check if any calls faulted
|
return unit.interface(), false
|
||||||
local faulted = sps.__p_is_faulted()
|
|
||||||
sps.__p_clear_fault()
|
|
||||||
sps.__p_enable_afc()
|
|
||||||
|
|
||||||
return unit.interface(), faulted
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return sps_rtu
|
return sps_rtu
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ local turbinev_rtu = {}
|
|||||||
---@param turbine table
|
---@param turbine table
|
||||||
---@return rtu_device interface, boolean faulted
|
---@return rtu_device interface, boolean faulted
|
||||||
function turbinev_rtu.new(turbine)
|
function turbinev_rtu.new(turbine)
|
||||||
local unit = rtu.init_unit()
|
local unit = rtu.init_unit(turbine)
|
||||||
|
|
||||||
-- disable auto fault clearing
|
|
||||||
turbine.__p_clear_fault()
|
|
||||||
turbine.__p_disable_afc()
|
|
||||||
|
|
||||||
-- discrete inputs --
|
-- discrete inputs --
|
||||||
unit.connect_di(turbine.isFormed)
|
unit.connect_di("isFormed")
|
||||||
|
|
||||||
-- coils --
|
-- coils --
|
||||||
unit.connect_coil(function () turbine.incrementDumpingMode() end, function () end)
|
unit.connect_coil(function () turbine.incrementDumpingMode() end, function () end)
|
||||||
@@ -22,44 +18,39 @@ function turbinev_rtu.new(turbine)
|
|||||||
|
|
||||||
-- input registers --
|
-- input registers --
|
||||||
-- multiblock properties
|
-- multiblock properties
|
||||||
unit.connect_input_reg(turbine.getLength)
|
unit.connect_input_reg("getLength")
|
||||||
unit.connect_input_reg(turbine.getWidth)
|
unit.connect_input_reg("getWidth")
|
||||||
unit.connect_input_reg(turbine.getHeight)
|
unit.connect_input_reg("getHeight")
|
||||||
unit.connect_input_reg(turbine.getMinPos)
|
unit.connect_input_reg("getMinPos")
|
||||||
unit.connect_input_reg(turbine.getMaxPos)
|
unit.connect_input_reg("getMaxPos")
|
||||||
-- build properties
|
-- build properties
|
||||||
unit.connect_input_reg(turbine.getBlades)
|
unit.connect_input_reg("getBlades")
|
||||||
unit.connect_input_reg(turbine.getCoils)
|
unit.connect_input_reg("getCoils")
|
||||||
unit.connect_input_reg(turbine.getVents)
|
unit.connect_input_reg("getVents")
|
||||||
unit.connect_input_reg(turbine.getDispersers)
|
unit.connect_input_reg("getDispersers")
|
||||||
unit.connect_input_reg(turbine.getCondensers)
|
unit.connect_input_reg("getCondensers")
|
||||||
unit.connect_input_reg(turbine.getSteamCapacity)
|
unit.connect_input_reg("getSteamCapacity")
|
||||||
unit.connect_input_reg(turbine.getMaxEnergy)
|
unit.connect_input_reg("getMaxEnergy")
|
||||||
unit.connect_input_reg(turbine.getMaxFlowRate)
|
unit.connect_input_reg("getMaxFlowRate")
|
||||||
unit.connect_input_reg(turbine.getMaxProduction)
|
unit.connect_input_reg("getMaxProduction")
|
||||||
unit.connect_input_reg(turbine.getMaxWaterOutput)
|
unit.connect_input_reg("getMaxWaterOutput")
|
||||||
-- current state
|
-- current state
|
||||||
unit.connect_input_reg(turbine.getFlowRate)
|
unit.connect_input_reg("getFlowRate")
|
||||||
unit.connect_input_reg(turbine.getProductionRate)
|
unit.connect_input_reg("getProductionRate")
|
||||||
unit.connect_input_reg(turbine.getLastSteamInputRate)
|
unit.connect_input_reg("getLastSteamInputRate")
|
||||||
unit.connect_input_reg(turbine.getDumpingMode)
|
unit.connect_input_reg("getDumpingMode")
|
||||||
-- tanks/containers
|
-- tanks/containers
|
||||||
unit.connect_input_reg(turbine.getSteam)
|
unit.connect_input_reg("getSteam")
|
||||||
unit.connect_input_reg(turbine.getSteamNeeded)
|
unit.connect_input_reg("getSteamNeeded")
|
||||||
unit.connect_input_reg(turbine.getSteamFilledPercentage)
|
unit.connect_input_reg("getSteamFilledPercentage")
|
||||||
unit.connect_input_reg(turbine.getEnergy)
|
unit.connect_input_reg("getEnergy")
|
||||||
unit.connect_input_reg(turbine.getEnergyNeeded)
|
unit.connect_input_reg("getEnergyNeeded")
|
||||||
unit.connect_input_reg(turbine.getEnergyFilledPercentage)
|
unit.connect_input_reg("getEnergyFilledPercentage")
|
||||||
|
|
||||||
-- holding registers --
|
-- holding registers --
|
||||||
unit.connect_holding_reg(turbine.getDumpingMode, turbine.setDumpingMode)
|
unit.connect_holding_reg("getDumpingMode", "setDumpingMode")
|
||||||
|
|
||||||
-- check if any calls faulted
|
return unit.interface(), false
|
||||||
local faulted = turbine.__p_is_faulted()
|
|
||||||
turbine.__p_clear_fault()
|
|
||||||
turbine.__p_enable_afc()
|
|
||||||
|
|
||||||
return unit.interface(), faulted
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return turbinev_rtu
|
return turbinev_rtu
|
||||||
|
|||||||
@@ -37,12 +37,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
|||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
readings[i], access_fault = rtu_dev.read_coil(addr)
|
readings[i], access_fault = rtu_dev.read_coil(addr)
|
||||||
|
if access_fault then break end
|
||||||
if access_fault then
|
|
||||||
return_ok = false
|
|
||||||
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -86,12 +81,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
|||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
readings[i], access_fault = rtu_dev.read_di(addr)
|
readings[i], access_fault = rtu_dev.read_di(addr)
|
||||||
|
if access_fault then break end
|
||||||
if access_fault then
|
|
||||||
return_ok = false
|
|
||||||
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -135,12 +125,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
|||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
readings[i], access_fault = rtu_dev.read_holding_reg(addr)
|
readings[i], access_fault = rtu_dev.read_holding_reg(addr)
|
||||||
|
if access_fault then break end
|
||||||
if access_fault then
|
|
||||||
return_ok = false
|
|
||||||
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -184,12 +169,7 @@ function modbus.new(rtu_dev, use_parallel_read)
|
|||||||
end)
|
end)
|
||||||
else
|
else
|
||||||
readings[i], access_fault = rtu_dev.read_input_reg(addr)
|
readings[i], access_fault = rtu_dev.read_input_reg(addr)
|
||||||
|
if access_fault then break end
|
||||||
if access_fault then
|
|
||||||
return_ok = false
|
|
||||||
readings = MODBUS_EXCODE.SERVER_DEVICE_FAIL
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -16,14 +16,15 @@ local TextBox = require("graphics.elements.textbox")
|
|||||||
|
|
||||||
local DataIndicator = require("graphics.elements.indicators.data")
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
local LED = require("graphics.elements.indicators.led")
|
local LED = require("graphics.elements.indicators.led")
|
||||||
|
local LEDPair = require("graphics.elements.indicators.ledpair")
|
||||||
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||||
|
|
||||||
|
local LINK_STATE = types.PANEL_LINK_STATE
|
||||||
|
|
||||||
local ALIGN = core.ALIGN
|
local ALIGN = core.ALIGN
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local fp_label = style.fp_label
|
|
||||||
|
|
||||||
local ind_grn = style.ind_grn
|
local ind_grn = style.ind_grn
|
||||||
|
|
||||||
local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC TANK", "IND MATRIX", "SPS", "SNA", "ENV DETECTOR" }
|
local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC TANK", "IND MATRIX", "SPS", "SNA", "ENV DETECTOR" }
|
||||||
@@ -32,7 +33,9 @@ local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC
|
|||||||
---@param panel graphics_element main displaybox
|
---@param panel graphics_element main displaybox
|
||||||
---@param units table unit list
|
---@param units table unit list
|
||||||
local function init(panel, units)
|
local function init(panel, units)
|
||||||
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
local disabled_fg = style.fp.disabled_fg
|
||||||
|
|
||||||
|
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- system indicators
|
-- system indicators
|
||||||
@@ -48,12 +51,43 @@ local function init(panel, units)
|
|||||||
heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
|
heartbeat.register(databus.ps, "heartbeat", heartbeat.update)
|
||||||
|
|
||||||
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
local modem = LED{parent=system,label="MODEM",colors=ind_grn}
|
||||||
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
|
|
||||||
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
if not style.colorblind then
|
||||||
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,style.ind_bkg}}
|
||||||
|
network.update(types.PANEL_LINK_STATE.DISCONNECTED)
|
||||||
|
network.register(databus.ps, "link_state", network.update)
|
||||||
|
else
|
||||||
|
local nt_lnk = LEDPair{parent=system,label="NT LINKED",off=style.ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
local nt_ver = LEDPair{parent=system,label="NT VERSION",off=style.ind_bkg,c1=colors.red,c2=colors.green}
|
||||||
|
|
||||||
|
nt_lnk.register(databus.ps, "link_state", function (state)
|
||||||
|
local value = 2
|
||||||
|
|
||||||
|
if state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
elseif state == LINK_STATE.LINKED then
|
||||||
|
value = 3
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_lnk.update(value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
nt_ver.register(databus.ps, "link_state", function (state)
|
||||||
|
local value = 3
|
||||||
|
|
||||||
|
if state == LINK_STATE.BAD_VERSION then
|
||||||
|
value = 2
|
||||||
|
elseif state == LINK_STATE.DISCONNECTED then
|
||||||
|
value = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
nt_ver.update(value)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
system.line_break()
|
system.line_break()
|
||||||
|
|
||||||
modem.register(databus.ps, "has_modem", modem.update)
|
modem.register(databus.ps, "has_modem", modem.update)
|
||||||
network.register(databus.ps, "link_state", network.update)
|
|
||||||
|
|
||||||
local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
|
local rt_main = LED{parent=system,label="RT MAIN",colors=ind_grn}
|
||||||
local rt_comm = LED{parent=system,label="RT COMMS",colors=ind_grn}
|
local rt_comm = LED{parent=system,label="RT COMMS",colors=ind_grn}
|
||||||
@@ -64,17 +98,17 @@ local function init(panel, units)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=fp_label}
|
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=disabled_fg}
|
||||||
|
|
||||||
TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.label}
|
TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.fp.text_fg}
|
||||||
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=cpair(colors.gray,colors.white)}
|
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=style.theme.field_box}
|
||||||
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about label
|
-- about label
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=fp_label}
|
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
|
|
||||||
@@ -116,7 +150,7 @@ local function init(panel, units)
|
|||||||
|
|
||||||
-- assignment (unit # or facility)
|
-- assignment (unit # or facility)
|
||||||
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
local for_unit = util.trinary(unit.reactor == 0, "\x1a FACIL ", "\x1a UNIT " .. unit.reactor)
|
||||||
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,height=1,fg_bg=fp_label}
|
TextBox{parent=unit_hw_statuses,y=i,x=19,text=for_unit,height=1,fg_bg=disabled_fg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,47 +2,39 @@
|
|||||||
-- Graphics Style Options
|
-- Graphics Style Options
|
||||||
--
|
--
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
|
---@class rtu_style
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
-- GLOBAL --
|
style.theme = themes.sandstone
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
-- remap global colors
|
style.colorblind = false
|
||||||
colors.ivory = colors.pink
|
|
||||||
colors.yellow_hc = colors.purple
|
|
||||||
colors.red_off = colors.brown
|
|
||||||
colors.yellow_off = colors.magenta
|
|
||||||
colors.green_off = colors.lime
|
|
||||||
|
|
||||||
style.root = cpair(colors.black, colors.ivory)
|
|
||||||
style.header = cpair(colors.black, colors.lightGray)
|
|
||||||
|
|
||||||
style.colors = {
|
|
||||||
{ c = colors.red, hex = 0xdf4949 }, -- RED ON
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xf9fb53 }, -- YELLOW ON
|
|
||||||
{ c = colors.lime, hex = 0x16665a }, -- GREEN OFF
|
|
||||||
{ c = colors.green, hex = 0x6be551 }, -- GREEN ON
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee }, -- YELLOW HIGH CONTRAST
|
|
||||||
{ c = colors.pink, hex = 0xdcd9ca }, -- IVORY
|
|
||||||
{ c = colors.magenta, hex = 0x85862c }, -- YELLOW OFF
|
|
||||||
-- { c = colors.white, hex = 0xdcd9ca },
|
|
||||||
{ c = colors.lightGray, hex = 0xb1b8b3 },
|
|
||||||
{ c = colors.gray, hex = 0x575757 },
|
|
||||||
-- { c = colors.black, hex = 0x191919 },
|
|
||||||
{ c = colors.brown, hex = 0x672223 } -- RED OFF
|
|
||||||
}
|
|
||||||
|
|
||||||
-- COMMON COLOR PAIRS --
|
|
||||||
|
|
||||||
style.fp_label = cpair(colors.lightGray, colors.ivory)
|
|
||||||
|
|
||||||
style.ind_grn = cpair(colors.green, colors.green_off)
|
style.ind_grn = cpair(colors.green, colors.green_off)
|
||||||
|
|
||||||
|
-- set theme per configuration
|
||||||
|
---@param fp FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE the color mode to use
|
||||||
|
function style.set_theme(fp, color_mode)
|
||||||
|
if fp == themes.FP_THEME.SANDSTONE then
|
||||||
|
style.theme = themes.sandstone
|
||||||
|
elseif fp == themes.FP_THEME.BASALT then
|
||||||
|
style.theme = themes.basalt
|
||||||
|
end
|
||||||
|
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
|
|
||||||
|
style.colorblind = color_mode ~= themes.COLOR_MODE.STANDARD and color_mode ~= themes.COLOR_MODE.STD_ON_BLACK
|
||||||
|
|
||||||
|
if color_mode == themes.COLOR_MODE.STANDARD or color_mode == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
style.ind_bkg = colors.gray
|
||||||
|
else
|
||||||
|
style.ind_bkg = colors.black
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return style
|
return style
|
||||||
|
|||||||
@@ -19,11 +19,16 @@ local ui = {
|
|||||||
|
|
||||||
-- try to start the UI
|
-- try to start the UI
|
||||||
---@param units table RTU units
|
---@param units table RTU units
|
||||||
|
---@param theme FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE color mode
|
||||||
---@return boolean success, any error_msg
|
---@return boolean success, any error_msg
|
||||||
function renderer.try_start_ui(units)
|
function renderer.try_start_ui(units, theme, color_mode)
|
||||||
local status, msg = true, nil
|
local status, msg = true, nil
|
||||||
|
|
||||||
if ui.display == nil then
|
if ui.display == nil then
|
||||||
|
-- set theme
|
||||||
|
style.set_theme(theme, color_mode)
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
term.setTextColor(colors.white)
|
term.setTextColor(colors.white)
|
||||||
term.setBackgroundColor(colors.black)
|
term.setBackgroundColor(colors.black)
|
||||||
@@ -31,13 +36,19 @@ function renderer.try_start_ui(units)
|
|||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
|
|
||||||
-- set overridden colors
|
-- set overridden colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
term.setPaletteColor(style.theme.colors[i].c, style.theme.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply color mode
|
||||||
|
local c_mode_overrides = style.theme.color_modes[color_mode]
|
||||||
|
for i = 1, #c_mode_overrides do
|
||||||
|
term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init front panel view
|
-- init front panel view
|
||||||
status, msg = pcall(function ()
|
status, msg = pcall(function ()
|
||||||
ui.display = DisplayBox{window=term.current(),fg_bg=style.root}
|
ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root}
|
||||||
panel_view(ui.display, units)
|
panel_view(ui.display, units)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -65,9 +76,9 @@ function renderer.close_ui()
|
|||||||
ui.display = nil
|
ui.display = nil
|
||||||
|
|
||||||
-- restore colors
|
-- restore colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
local r, g, b = term.nativePaletteColor(style.theme.colors[i].c)
|
||||||
term.setPaletteColor(style.colors[i].c, r, g, b)
|
term.setPaletteColor(style.theme.colors[i].c, r, g, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
|
|||||||
77
rtu/rtu.lua
77
rtu/rtu.lua
@@ -5,6 +5,8 @@ local log = require("scada-common.log")
|
|||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local databus = require("rtu.databus")
|
local databus = require("rtu.databus")
|
||||||
local modbus = require("rtu.modbus")
|
local modbus = require("rtu.modbus")
|
||||||
|
|
||||||
@@ -29,15 +31,20 @@ function rtu.load_config()
|
|||||||
config.Redstone = settings.get("Redstone")
|
config.Redstone = settings.get("Redstone")
|
||||||
|
|
||||||
config.SpeakerVolume = settings.get("SpeakerVolume")
|
config.SpeakerVolume = settings.get("SpeakerVolume")
|
||||||
|
|
||||||
config.SVR_Channel = settings.get("SVR_Channel")
|
config.SVR_Channel = settings.get("SVR_Channel")
|
||||||
config.RTU_Channel = settings.get("RTU_Channel")
|
config.RTU_Channel = settings.get("RTU_Channel")
|
||||||
config.ConnTimeout = settings.get("ConnTimeout")
|
config.ConnTimeout = settings.get("ConnTimeout")
|
||||||
config.TrustedRange = settings.get("TrustedRange")
|
config.TrustedRange = settings.get("TrustedRange")
|
||||||
config.AuthKey = settings.get("AuthKey")
|
config.AuthKey = settings.get("AuthKey")
|
||||||
|
|
||||||
config.LogMode = settings.get("LogMode")
|
config.LogMode = settings.get("LogMode")
|
||||||
config.LogPath = settings.get("LogPath")
|
config.LogPath = settings.get("LogPath")
|
||||||
config.LogDebug = settings.get("LogDebug")
|
config.LogDebug = settings.get("LogDebug")
|
||||||
|
|
||||||
|
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
||||||
|
config.ColorMode = settings.get("ColorMode")
|
||||||
|
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
cfv.assert_type_num(config.SpeakerVolume)
|
cfv.assert_type_num(config.SpeakerVolume)
|
||||||
@@ -53,7 +60,7 @@ function rtu.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
@@ -61,15 +68,22 @@ function rtu.load_config()
|
|||||||
cfv.assert_type_str(config.LogPath)
|
cfv.assert_type_str(config.LogPath)
|
||||||
cfv.assert_type_bool(config.LogDebug)
|
cfv.assert_type_bool(config.LogDebug)
|
||||||
|
|
||||||
|
cfv.assert_type_int(config.FrontPanelTheme)
|
||||||
|
cfv.assert_range(config.FrontPanelTheme, 1, 2)
|
||||||
|
cfv.assert_type_int(config.ColorMode)
|
||||||
|
cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
||||||
|
|
||||||
cfv.assert_type_table(config.Peripherals)
|
cfv.assert_type_table(config.Peripherals)
|
||||||
cfv.assert_type_table(config.Redstone)
|
cfv.assert_type_table(config.Redstone)
|
||||||
|
|
||||||
return cfv.valid()
|
return cfv.valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- create a new RTU unit
|
-- create a new RTU unit<br>
|
||||||
|
-- if this is for a PPM peripheral, auto fault clearing MUST stay enabled once access begins
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function rtu.init_unit()
|
---@param device table|nil peripheral device, if applicable
|
||||||
|
function rtu.init_unit(device)
|
||||||
local self = {
|
local self = {
|
||||||
discrete_inputs = {},
|
discrete_inputs = {},
|
||||||
coils = {},
|
coils = {},
|
||||||
@@ -80,12 +94,18 @@ function rtu.init_unit()
|
|||||||
|
|
||||||
local insert = table.insert
|
local insert = table.insert
|
||||||
|
|
||||||
|
local stub = function () log.warning("tried to call an RTU function stub") end
|
||||||
|
|
||||||
---@class rtu_device
|
---@class rtu_device
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
---@class rtu
|
---@class rtu
|
||||||
local protected = {}
|
local protected = {}
|
||||||
|
|
||||||
|
-- function to check if the peripheral (if exists) is faulted
|
||||||
|
local function _is_faulted() return false end
|
||||||
|
if device then _is_faulted = device.__p_is_faulted end
|
||||||
|
|
||||||
-- refresh IO count
|
-- refresh IO count
|
||||||
local function _count_io()
|
local function _count_io()
|
||||||
self.io_count_cache = { #self.discrete_inputs, #self.coils, #self.input_regs, #self.holding_regs }
|
self.io_count_cache = { #self.discrete_inputs, #self.coils, #self.input_regs, #self.holding_regs }
|
||||||
@@ -97,13 +117,26 @@ function rtu.init_unit()
|
|||||||
return self.io_count_cache[1], self.io_count_cache[2], self.io_count_cache[3], self.io_count_cache[4]
|
return self.io_count_cache[1], self.io_count_cache[2], self.io_count_cache[3], self.io_count_cache[4]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- pass a function through or generate one to call a function by name from the device
|
||||||
|
---@param f function|string function or device function name
|
||||||
|
local function _as_func(f)
|
||||||
|
if type(f) == "string" then
|
||||||
|
local name = f
|
||||||
|
if device then
|
||||||
|
f = function (...) return device[name](...) end
|
||||||
|
else f = stub end
|
||||||
|
end
|
||||||
|
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
-- discrete inputs: single bit read-only
|
-- discrete inputs: single bit read-only
|
||||||
|
|
||||||
-- connect discrete input
|
-- connect discrete input
|
||||||
---@param f function
|
---@param f function|string function or function name
|
||||||
---@return integer count count of discrete inputs
|
---@return integer count count of discrete inputs
|
||||||
function protected.connect_di(f)
|
function protected.connect_di(f)
|
||||||
insert(self.discrete_inputs, { read = f })
|
insert(self.discrete_inputs, { read = _as_func(f) })
|
||||||
_count_io()
|
_count_io()
|
||||||
return #self.discrete_inputs
|
return #self.discrete_inputs
|
||||||
end
|
end
|
||||||
@@ -112,19 +145,18 @@ function rtu.init_unit()
|
|||||||
---@param di_addr integer
|
---@param di_addr integer
|
||||||
---@return any value, boolean access_fault
|
---@return any value, boolean access_fault
|
||||||
function public.read_di(di_addr)
|
function public.read_di(di_addr)
|
||||||
ppm.clear_fault()
|
|
||||||
local value = self.discrete_inputs[di_addr].read()
|
local value = self.discrete_inputs[di_addr].read()
|
||||||
return value, ppm.is_faulted()
|
return value, _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- coils: single bit read-write
|
-- coils: single bit read-write
|
||||||
|
|
||||||
-- connect coil
|
-- connect coil
|
||||||
---@param f_read function
|
---@param f_read function|string function or function name
|
||||||
---@param f_write function
|
---@param f_write function|string function or function name
|
||||||
---@return integer count count of coils
|
---@return integer count count of coils
|
||||||
function protected.connect_coil(f_read, f_write)
|
function protected.connect_coil(f_read, f_write)
|
||||||
insert(self.coils, { read = f_read, write = f_write })
|
insert(self.coils, { read = _as_func(f_read), write = _as_func(f_write) })
|
||||||
_count_io()
|
_count_io()
|
||||||
return #self.coils
|
return #self.coils
|
||||||
end
|
end
|
||||||
@@ -133,9 +165,8 @@ function rtu.init_unit()
|
|||||||
---@param coil_addr integer
|
---@param coil_addr integer
|
||||||
---@return any value, boolean access_fault
|
---@return any value, boolean access_fault
|
||||||
function public.read_coil(coil_addr)
|
function public.read_coil(coil_addr)
|
||||||
ppm.clear_fault()
|
|
||||||
local value = self.coils[coil_addr].read()
|
local value = self.coils[coil_addr].read()
|
||||||
return value, ppm.is_faulted()
|
return value, _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write coil
|
-- write coil
|
||||||
@@ -143,18 +174,17 @@ function rtu.init_unit()
|
|||||||
---@param value any
|
---@param value any
|
||||||
---@return boolean access_fault
|
---@return boolean access_fault
|
||||||
function public.write_coil(coil_addr, value)
|
function public.write_coil(coil_addr, value)
|
||||||
ppm.clear_fault()
|
|
||||||
self.coils[coil_addr].write(value)
|
self.coils[coil_addr].write(value)
|
||||||
return ppm.is_faulted()
|
return _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- input registers: multi-bit read-only
|
-- input registers: multi-bit read-only
|
||||||
|
|
||||||
-- connect input register
|
-- connect input register
|
||||||
---@param f function
|
---@param f function|string function or function name
|
||||||
---@return integer count count of input registers
|
---@return integer count count of input registers
|
||||||
function protected.connect_input_reg(f)
|
function protected.connect_input_reg(f)
|
||||||
insert(self.input_regs, { read = f })
|
insert(self.input_regs, { read = _as_func(f) })
|
||||||
_count_io()
|
_count_io()
|
||||||
return #self.input_regs
|
return #self.input_regs
|
||||||
end
|
end
|
||||||
@@ -163,19 +193,18 @@ function rtu.init_unit()
|
|||||||
---@param reg_addr integer
|
---@param reg_addr integer
|
||||||
---@return any value, boolean access_fault
|
---@return any value, boolean access_fault
|
||||||
function public.read_input_reg(reg_addr)
|
function public.read_input_reg(reg_addr)
|
||||||
ppm.clear_fault()
|
|
||||||
local value = self.input_regs[reg_addr].read()
|
local value = self.input_regs[reg_addr].read()
|
||||||
return value, ppm.is_faulted()
|
return value, _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- holding registers: multi-bit read-write
|
-- holding registers: multi-bit read-write
|
||||||
|
|
||||||
-- connect holding register
|
-- connect holding register
|
||||||
---@param f_read function
|
---@param f_read function|string function or function name
|
||||||
---@param f_write function
|
---@param f_write function|string function or function name
|
||||||
---@return integer count count of holding registers
|
---@return integer count count of holding registers
|
||||||
function protected.connect_holding_reg(f_read, f_write)
|
function protected.connect_holding_reg(f_read, f_write)
|
||||||
insert(self.holding_regs, { read = f_read, write = f_write })
|
insert(self.holding_regs, { read = _as_func(f_read), write = _as_func(f_write) })
|
||||||
_count_io()
|
_count_io()
|
||||||
return #self.holding_regs
|
return #self.holding_regs
|
||||||
end
|
end
|
||||||
@@ -184,9 +213,8 @@ function rtu.init_unit()
|
|||||||
---@param reg_addr integer
|
---@param reg_addr integer
|
||||||
---@return any value, boolean access_fault
|
---@return any value, boolean access_fault
|
||||||
function public.read_holding_reg(reg_addr)
|
function public.read_holding_reg(reg_addr)
|
||||||
ppm.clear_fault()
|
|
||||||
local value = self.holding_regs[reg_addr].read()
|
local value = self.holding_regs[reg_addr].read()
|
||||||
return value, ppm.is_faulted()
|
return value, _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write holding register
|
-- write holding register
|
||||||
@@ -194,9 +222,8 @@ function rtu.init_unit()
|
|||||||
---@param value any
|
---@param value any
|
||||||
---@return boolean access_fault
|
---@return boolean access_fault
|
||||||
function public.write_holding_reg(reg_addr, value)
|
function public.write_holding_reg(reg_addr, value)
|
||||||
ppm.clear_fault()
|
|
||||||
self.holding_regs[reg_addr].write(value)
|
self.holding_regs[reg_addr].write(value)
|
||||||
return ppm.is_faulted()
|
return _is_faulted()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- public RTU device access
|
-- public RTU device access
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ local sna_rtu = require("rtu.dev.sna_rtu")
|
|||||||
local sps_rtu = require("rtu.dev.sps_rtu")
|
local sps_rtu = require("rtu.dev.sps_rtu")
|
||||||
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
local turbinev_rtu = require("rtu.dev.turbinev_rtu")
|
||||||
|
|
||||||
local RTU_VERSION = "v1.7.13"
|
local RTU_VERSION = "v1.9.6"
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
local RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
@@ -47,9 +47,13 @@ if not rtu.load_config() then
|
|||||||
-- try to reconfigure (user action)
|
-- try to reconfigure (user action)
|
||||||
local success, error = configure.configure(true)
|
local success, error = configure.configure(true)
|
||||||
if success then
|
if success then
|
||||||
assert(rtu.load_config(), "failed to load valid configuration")
|
if not rtu.load_config() then
|
||||||
|
println("failed to load a valid configuration, please reconfigure")
|
||||||
|
return
|
||||||
|
end
|
||||||
else
|
else
|
||||||
assert(success, "RTU configuration error: " .. error)
|
println("configuration error: " .. error)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -67,6 +71,7 @@ log.info("========================================")
|
|||||||
println(">> RTU GATEWAY " .. RTU_VERSION .. " <<")
|
println(">> RTU GATEWAY " .. RTU_VERSION .. " <<")
|
||||||
|
|
||||||
crash.set_env("rtu", RTU_VERSION)
|
crash.set_env("rtu", RTU_VERSION)
|
||||||
|
crash.dbg_log_env()
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main application
|
-- main application
|
||||||
@@ -338,7 +343,7 @@ local function main()
|
|||||||
is_multiblock = true
|
is_multiblock = true
|
||||||
formed = device.isFormed()
|
formed = device.isFormed()
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
if formed == ppm.ACCESS_FAULT then
|
||||||
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
||||||
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed boiler multiblock"))
|
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed boiler multiblock"))
|
||||||
return false
|
return false
|
||||||
@@ -353,7 +358,7 @@ local function main()
|
|||||||
is_multiblock = true
|
is_multiblock = true
|
||||||
formed = device.isFormed()
|
formed = device.isFormed()
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
if formed == ppm.ACCESS_FAULT then
|
||||||
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
||||||
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed turbine multiblock"))
|
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed turbine multiblock"))
|
||||||
return false
|
return false
|
||||||
@@ -373,7 +378,7 @@ local function main()
|
|||||||
is_multiblock = true
|
is_multiblock = true
|
||||||
formed = device.isFormed()
|
formed = device.isFormed()
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
if formed == ppm.ACCESS_FAULT then
|
||||||
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
||||||
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed dynamic tank multiblock"))
|
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed dynamic tank multiblock"))
|
||||||
return false
|
return false
|
||||||
@@ -387,7 +392,7 @@ local function main()
|
|||||||
is_multiblock = true
|
is_multiblock = true
|
||||||
formed = device.isFormed()
|
formed = device.isFormed()
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
if formed == ppm.ACCESS_FAULT then
|
||||||
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
||||||
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed induction matrix multiblock"))
|
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed induction matrix multiblock"))
|
||||||
return false
|
return false
|
||||||
@@ -401,7 +406,7 @@ local function main()
|
|||||||
is_multiblock = true
|
is_multiblock = true
|
||||||
formed = device.isFormed()
|
formed = device.isFormed()
|
||||||
|
|
||||||
if formed == ppm.UNDEFINED_FIELD or formed == ppm.ACCESS_FAULT then
|
if formed == ppm.ACCESS_FAULT then
|
||||||
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
println_ts(util.c("sys_config> failed to check if '", name, "' is formed"))
|
||||||
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed SPS multiblock"))
|
log.fatal(util.c("sys_config> failed to check if '", name, "' is a formed SPS multiblock"))
|
||||||
return false
|
return false
|
||||||
@@ -467,7 +472,8 @@ local function main()
|
|||||||
for_message = util.c("reactor ", for_reactor)
|
for_message = util.c("reactor ", for_reactor)
|
||||||
end
|
end
|
||||||
|
|
||||||
log.info(util.c("sys_config> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
|
local index_str = util.trinary(index ~= nil, util.c(" [", index, "]"), "")
|
||||||
|
log.info(util.c("sys_config> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ")", index_str, " for ", for_message))
|
||||||
|
|
||||||
rtu_unit.uid = #units
|
rtu_unit.uid = #units
|
||||||
|
|
||||||
@@ -502,7 +508,7 @@ local function main()
|
|||||||
if sys_config() then
|
if sys_config() then
|
||||||
-- start UI
|
-- start UI
|
||||||
local message
|
local message
|
||||||
rtu_state.fp_ok, message = renderer.try_start_ui(units)
|
rtu_state.fp_ok, message = renderer.try_start_ui(units, config.FrontPanelTheme, config.ColorMode)
|
||||||
|
|
||||||
if not rtu_state.fp_ok then
|
if not rtu_state.fp_ok then
|
||||||
println_ts(util.c("UI error: ", message))
|
println_ts(util.c("UI error: ", message))
|
||||||
|
|||||||
@@ -517,82 +517,23 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
|
|
||||||
-- check if multiblock is still formed if this is a multiblock
|
-- check if multiblock is still formed if this is a multiblock
|
||||||
if unit.is_multiblock and (util.time_ms() - last_f_check > 250) then
|
if unit.is_multiblock and (util.time_ms() - last_f_check > 250) then
|
||||||
local is_formed = unit.device.isFormed()
|
|
||||||
|
|
||||||
last_f_check = util.time_ms()
|
last_f_check = util.time_ms()
|
||||||
|
|
||||||
|
local is_formed = unit.device.isFormed()
|
||||||
|
|
||||||
if unit.formed == nil then
|
if unit.formed == nil then
|
||||||
unit.formed = is_formed
|
unit.formed = is_formed
|
||||||
if is_formed then unit.hw_state = UNIT_HW_STATE.OK end
|
if is_formed then unit.hw_state = UNIT_HW_STATE.OK end
|
||||||
|
elseif not unit.formed then
|
||||||
|
unit.hw_state = UNIT_HW_STATE.UNFORMED
|
||||||
end
|
end
|
||||||
|
|
||||||
if not unit.formed then unit.hw_state = UNIT_HW_STATE.UNFORMED end
|
if (is_formed == true) and not unit.formed then
|
||||||
|
unit.hw_state = UNIT_HW_STATE.OK
|
||||||
if (not unit.formed) and is_formed then
|
log.info(util.c(detail_name, " is now formed"))
|
||||||
-- newly re-formed
|
rtu_comms.send_remounted(unit.uid)
|
||||||
local iface = ppm.get_iface(unit.device)
|
elseif (is_formed == false) and unit.formed then
|
||||||
if iface then
|
log.warning(util.c(detail_name, " is no longer formed"))
|
||||||
log.info(util.c("unmounting and remounting reformed RTU unit ", detail_name))
|
|
||||||
|
|
||||||
ppm.unmount(unit.device)
|
|
||||||
|
|
||||||
local type, device = ppm.mount(iface)
|
|
||||||
local faulted = false
|
|
||||||
|
|
||||||
if device ~= nil then
|
|
||||||
if type == "boilerValve" and unit.type == RTU_UNIT_TYPE.BOILER_VALVE then
|
|
||||||
-- boiler multiblock
|
|
||||||
unit.device = device
|
|
||||||
unit.rtu, faulted = boilerv_rtu.new(device)
|
|
||||||
unit.formed = device.isFormed()
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
|
||||||
elseif type == "turbineValve" and unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then
|
|
||||||
-- turbine multiblock
|
|
||||||
unit.device = device
|
|
||||||
unit.rtu, faulted = turbinev_rtu.new(device)
|
|
||||||
unit.formed = device.isFormed()
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
|
||||||
elseif type == "dynamicValve" and unit.type == RTU_UNIT_TYPE.DYNAMIC_VALVE then
|
|
||||||
-- dynamic tank multiblock
|
|
||||||
unit.device = device
|
|
||||||
unit.rtu, faulted = dynamicv_rtu.new(device)
|
|
||||||
unit.formed = device.isFormed()
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
|
||||||
elseif type == "inductionPort" and unit.type == RTU_UNIT_TYPE.IMATRIX then
|
|
||||||
-- induction matrix multiblock
|
|
||||||
unit.device = device
|
|
||||||
unit.rtu, faulted = imatrix_rtu.new(device)
|
|
||||||
unit.formed = device.isFormed()
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
|
||||||
elseif type == "spsPort" and unit.type == RTU_UNIT_TYPE.SPS then
|
|
||||||
-- SPS multiblock
|
|
||||||
unit.device = device
|
|
||||||
unit.rtu, faulted = sps_rtu.new(device)
|
|
||||||
unit.formed = device.isFormed()
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
|
||||||
else
|
|
||||||
log.error("illegal remount of non-multiblock RTU or type change attempted for " .. short_name, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
if unit.formed and faulted then
|
|
||||||
-- something is still wrong = can't mark as formed yet
|
|
||||||
unit.formed = false
|
|
||||||
unit.hw_state = UNIT_HW_STATE.UNFORMED
|
|
||||||
log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing"))
|
|
||||||
else
|
|
||||||
unit.hw_state = UNIT_HW_STATE.OK
|
|
||||||
rtu_comms.send_remounted(unit.uid)
|
|
||||||
end
|
|
||||||
|
|
||||||
local type_name = types.rtu_type_to_string(unit.type)
|
|
||||||
log.info(util.c("reconnected the ", type_name, " on interface ", unit.name))
|
|
||||||
else
|
|
||||||
-- fully lost the peripheral now :(
|
|
||||||
log.error(util.c(unit.name, " lost (failed reconnect)"))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
log.error("failed to get interface of previously connected RTU unit " .. detail_name, true)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unit.formed = is_formed
|
unit.formed = is_formed
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ local max_distance = nil
|
|||||||
---@class comms
|
---@class comms
|
||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
-- protocol/data version (protocol/data independent changes tracked by util.lua version)
|
-- protocol/data versions (protocol/data independent changes tracked by util.lua version)
|
||||||
comms.version = "2.4.4"
|
comms.version = "2.5.1"
|
||||||
|
comms.api_version = "0.0.2"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
local PROTOCOL = {
|
local PROTOCOL = {
|
||||||
@@ -64,7 +65,9 @@ local CRDN_TYPE = {
|
|||||||
FAC_CMD = 3, -- faility command
|
FAC_CMD = 3, -- faility command
|
||||||
UNIT_BUILDS = 4, -- build of each reactor unit (reactor + RTUs)
|
UNIT_BUILDS = 4, -- build of each reactor unit (reactor + RTUs)
|
||||||
UNIT_STATUSES = 5, -- state of each of the reactor units
|
UNIT_STATUSES = 5, -- state of each of the reactor units
|
||||||
UNIT_CMD = 6 -- command a reactor unit
|
UNIT_CMD = 6, -- command a reactor unit
|
||||||
|
API_GET_FAC = 7, -- API: get all the facility data
|
||||||
|
API_GET_UNIT = 8 -- API: get reactor unit data
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum ESTABLISH_ACK
|
---@enum ESTABLISH_ACK
|
||||||
@@ -72,7 +75,8 @@ local ESTABLISH_ACK = {
|
|||||||
ALLOW = 0, -- link approved
|
ALLOW = 0, -- link approved
|
||||||
DENY = 1, -- link denied
|
DENY = 1, -- link denied
|
||||||
COLLISION = 2, -- link denied due to existing active link
|
COLLISION = 2, -- link denied due to existing active link
|
||||||
BAD_VERSION = 3 -- link denied due to comms version mismatch
|
BAD_VERSION = 3, -- link denied due to comms version mismatch
|
||||||
|
BAD_API_VERSION = 4 -- link denied due to api version mismatch
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum DEVICE_TYPE device types for establish messages
|
---@enum DEVICE_TYPE device types for establish messages
|
||||||
@@ -93,7 +97,8 @@ local FAC_COMMAND = {
|
|||||||
START = 2, -- start automatic process control
|
START = 2, -- start automatic process control
|
||||||
ACK_ALL_ALARMS = 3, -- acknowledge all alarms on all units
|
ACK_ALL_ALARMS = 3, -- acknowledge all alarms on all units
|
||||||
SET_WASTE_MODE = 4, -- set automatic waste processing mode
|
SET_WASTE_MODE = 4, -- set automatic waste processing mode
|
||||||
SET_PU_FB = 5 -- set plutonium fallback mode
|
SET_PU_FB = 5, -- set plutonium fallback mode
|
||||||
|
SET_SPS_LP = 6 -- set SPS at low power mode
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum UNIT_COMMAND
|
---@enum UNIT_COMMAND
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ local annunc = {}
|
|||||||
annunc.RCSFlowLow_H2O = -3.2 -- flow < -3.2 mB/s
|
annunc.RCSFlowLow_H2O = -3.2 -- flow < -3.2 mB/s
|
||||||
annunc.RCSFlowLow_NA = -2.0 -- flow < -2.0 mB/s
|
annunc.RCSFlowLow_NA = -2.0 -- flow < -2.0 mB/s
|
||||||
annunc.CoolantLevelLow = 0.4 -- fill < 40%
|
annunc.CoolantLevelLow = 0.4 -- fill < 40%
|
||||||
annunc.ReactorTempHigh = 1000 -- temp > 1000K
|
annunc.OpTempTolerance = 5 -- high temp if >= operational temp + X
|
||||||
annunc.ReactorHighDeltaT = 50 -- rate > 50K/s
|
annunc.ReactorHighDeltaT = 50 -- rate > 50K/s
|
||||||
annunc.FuelLevelLow = 0.05 -- fill <= 5%
|
annunc.FuelLevelLow = 0.05 -- fill <= 5%
|
||||||
annunc.WasteLevelHigh = 0.80 -- fill >= 80%
|
annunc.WasteLevelHigh = 0.80 -- fill >= 80%
|
||||||
@@ -66,21 +66,48 @@ constants.ALARM_LIMITS = alarms
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Supervisor Redstone Activation Thresholds
|
||||||
|
|
||||||
|
---@class _rs_threshold_constants
|
||||||
|
local rs = {}
|
||||||
|
|
||||||
|
rs.IMATRIX_CHARGE_LOW = 0.05 -- activation threshold (less than) for F_MATRIX_LOW
|
||||||
|
rs.IMATRIX_CHARGE_HIGH = 0.95 -- activation threshold (greater than) for F_MATRIX_HIGH
|
||||||
|
|
||||||
|
constants.RS_THRESHOLDS = rs
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Supervisor Constants
|
--#region Supervisor Constants
|
||||||
|
|
||||||
-- milliseconds until turbine flow is assumed to be stable enough to enable coolant checks
|
-- milliseconds until coolant flow is assumed to be stable enough to enable certain coolant checks
|
||||||
constants.FLOW_STABILITY_DELAY_MS = 15000
|
constants.FLOW_STABILITY_DELAY_MS = 10000
|
||||||
|
|
||||||
-- Notes on Radiation
|
-- Notes on Radiation
|
||||||
-- - background radiation 0.0000001 Sv/h (99.99 nSv/h)
|
-- - background radiation 0.0000001 Sv/h (99.99 nSv/h)
|
||||||
-- - "green tint" radiation 0.00001 Sv/h (10 uSv/h)
|
-- - "green tint" radiation 0.00001 Sv/h (10 uSv/h)
|
||||||
-- - damaging radiation 0.00006 Sv/h (60 uSv/h)
|
-- - damaging radiation 0.00006 Sv/h (60 uSv/h)
|
||||||
constants.LOW_RADIATION = 0.00001
|
constants.LOW_RADIATION = 0.00001
|
||||||
constants.HAZARD_RADIATION = 0.00006
|
constants.HAZARD_RADIATION = 0.00006
|
||||||
constants.HIGH_RADIATION = 0.001
|
constants.HIGH_RADIATION = 0.001
|
||||||
constants.VERY_HIGH_RADIATION = 0.1
|
constants.VERY_HIGH_RADIATION = 0.1
|
||||||
constants.SEVERE_RADIATION = 8.0
|
constants.SEVERE_RADIATION = 8.0
|
||||||
constants.EXTREME_RADIATION = 100.0
|
constants.EXTREME_RADIATION = 100.0
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Mekanism Configuration Constants
|
||||||
|
|
||||||
|
---@class _mek_constants
|
||||||
|
local mek = {}
|
||||||
|
|
||||||
|
mek.BASE_BOIL_TEMP = 373.15 -- mekanism: HeatUtils.BASE_BOIL_TEMP
|
||||||
|
mek.JOULES_PER_MB = 1000000 -- mekanism: energyPerFissionFuel
|
||||||
|
mek.TURBINE_GAS_PER_TANK = 64000 -- mekanism: turbineGasPerTank
|
||||||
|
mek.TURBINE_DISPERSER_FLOW = 1280 -- mekanism: turbineDisperserGasFlow
|
||||||
|
mek.TURBINE_VENT_FLOW = 32000 -- mekanism: turbineVentGasFlow
|
||||||
|
|
||||||
|
constants.mek = mek
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,21 @@ function crash.set_env(application, version)
|
|||||||
ver = version
|
ver = version
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- log environment versions
|
||||||
|
---@param log_msg function log function to use
|
||||||
|
local function log_versions(log_msg)
|
||||||
|
log_msg(util.c("RUNTIME: ", _HOST))
|
||||||
|
log_msg(util.c("LUA VERSION: ", _VERSION))
|
||||||
|
log_msg(util.c("APPLICATION: ", app))
|
||||||
|
log_msg(util.c("FIRMWARE VERSION: ", ver))
|
||||||
|
log_msg(util.c("COMMS VERSION: ", comms.version))
|
||||||
|
if has_graphics then log_msg(util.c("GRAPHICS VERSION: ", core.version)) end
|
||||||
|
if has_lockbox then log_msg(util.c("LOCKBOX VERSION: ", lockbox.version)) end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- when running with debug logs, log the useful information that the crash handler knows
|
||||||
|
function crash.dbg_log_env() log_versions(log.debug) end
|
||||||
|
|
||||||
-- handle a crash error
|
-- handle a crash error
|
||||||
---@param error string error message
|
---@param error string error message
|
||||||
function crash.handler(error)
|
function crash.handler(error)
|
||||||
@@ -31,13 +46,7 @@ function crash.handler(error)
|
|||||||
log.info("=====> FATAL SOFTWARE FAULT <=====")
|
log.info("=====> FATAL SOFTWARE FAULT <=====")
|
||||||
log.fatal(error)
|
log.fatal(error)
|
||||||
log.info("----------------------------------")
|
log.info("----------------------------------")
|
||||||
log.info(util.c("RUNTIME: ", _HOST))
|
log_versions(log.info)
|
||||||
log.info(util.c("LUA VERSION: ", _VERSION))
|
|
||||||
log.info(util.c("APPLICATION: ", app))
|
|
||||||
log.info(util.c("FIRMWARE VERSION: ", ver))
|
|
||||||
log.info(util.c("COMMS VERSION: ", comms.version))
|
|
||||||
if has_graphics then log.info(util.c("GRAPHICS VERSION: ", core.version)) end
|
|
||||||
if has_lockbox then log.info(util.c("LOCKBOX VERSION: ", lockbox.version)) end
|
|
||||||
log.info("----------------------------------")
|
log.info("----------------------------------")
|
||||||
log.info(debug.traceback("--- begin debug trace ---", 1))
|
log.info(debug.traceback("--- begin debug trace ---", 1))
|
||||||
log.info("--- end debug trace ---")
|
log.info("--- end debug trace ---")
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ function network.init_mac(passkey)
|
|||||||
return init_time
|
return init_time
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- de-initialize message authentication system
|
||||||
|
function network.deinit_mac()
|
||||||
|
c_eng.key, c_eng.hmac = nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
-- generate HMAC of message
|
-- generate HMAC of message
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param message string initial value concatenated with ciphertext
|
---@param message string initial value concatenated with ciphertext
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ local util = require("scada-common.util")
|
|||||||
local ppm = {}
|
local ppm = {}
|
||||||
|
|
||||||
local ACCESS_FAULT = nil ---@type nil
|
local ACCESS_FAULT = nil ---@type nil
|
||||||
local UNDEFINED_FIELD = "undefined field"
|
local UNDEFINED_FIELD = "__PPM_UNDEF_FIELD__"
|
||||||
local VIRTUAL_DEVICE_TYPE = "ppm_vdev"
|
local VIRTUAL_DEVICE_TYPE = "ppm_vdev"
|
||||||
|
|
||||||
ppm.ACCESS_FAULT = ACCESS_FAULT
|
ppm.ACCESS_FAULT = ACCESS_FAULT
|
||||||
@@ -51,11 +51,13 @@ local function peri_init(iface)
|
|||||||
self.device = peripheral.wrap(iface)
|
self.device = peripheral.wrap(iface)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- initialization process (re-map)
|
-- create a protected version of a peripheral function call
|
||||||
|
---@nodiscard
|
||||||
for key, func in pairs(self.device) do
|
---@param key string function name
|
||||||
self.fault_counts[key] = 0
|
---@param func function function
|
||||||
self.device[key] = function (...)
|
---@return function method protected version of the function
|
||||||
|
local function protect_peri_function(key, func)
|
||||||
|
return function (...)
|
||||||
local return_table = table.pack(pcall(func, ...))
|
local return_table = table.pack(pcall(func, ...))
|
||||||
|
|
||||||
local status = return_table[1]
|
local status = return_table[1]
|
||||||
@@ -85,20 +87,24 @@ local function peri_init(iface)
|
|||||||
count_str = " [" .. self.fault_counts[key] .. " total faults]"
|
count_str = " [" .. self.fault_counts[key] .. " total faults]"
|
||||||
end
|
end
|
||||||
|
|
||||||
log.error(util.c("PPM: protected ", key, "() -> ", result, count_str))
|
log.error(util.c("PPM: [@", iface, "] protected ", key, "() -> ", result, count_str))
|
||||||
end
|
end
|
||||||
|
|
||||||
self.fault_counts[key] = self.fault_counts[key] + 1
|
self.fault_counts[key] = self.fault_counts[key] + 1
|
||||||
|
|
||||||
if result == "Terminated" then
|
if result == "Terminated" then ppm_sys.terminate = true end
|
||||||
ppm_sys.terminate = true
|
|
||||||
end
|
|
||||||
|
|
||||||
return ACCESS_FAULT
|
return ACCESS_FAULT, result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- initialization process (re-map)
|
||||||
|
for key, func in pairs(self.device) do
|
||||||
|
self.fault_counts[key] = 0
|
||||||
|
self.device[key] = protect_peri_function(key, func)
|
||||||
|
end
|
||||||
|
|
||||||
-- fault management & monitoring functions
|
-- fault management & monitoring functions
|
||||||
|
|
||||||
local function clear_fault() self.faulted = false end
|
local function clear_fault() self.faulted = false end
|
||||||
@@ -131,31 +137,44 @@ local function peri_init(iface)
|
|||||||
|
|
||||||
local mt = {
|
local mt = {
|
||||||
__index = function (_, key)
|
__index = function (_, key)
|
||||||
-- this will continuously be counting calls here as faults
|
-- try to find the function in case it was added (multiblock formed)
|
||||||
-- unlike other functions, faults here can't be cleared as it is just not defined
|
local funcs = peripheral.wrap(iface)
|
||||||
if self.fault_counts[key] == nil then
|
if (type(funcs) == "table") and (type(funcs[key]) == "function") then
|
||||||
|
-- add this function then return it
|
||||||
self.fault_counts[key] = 0
|
self.fault_counts[key] = 0
|
||||||
|
self.device[key] = protect_peri_function(key, funcs[key])
|
||||||
|
|
||||||
|
log.info(util.c("PPM: [@", iface, "] initialized previously undefined field ", key, "()"))
|
||||||
|
|
||||||
|
return self.device[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- function failed
|
-- function still missing, return an undefined function handler
|
||||||
self.faulted = true
|
-- note: code should avoid storing functions for multiblocks and instead try to index them again
|
||||||
self.last_fault = UNDEFINED_FIELD
|
return (function ()
|
||||||
|
-- this will continuously be counting calls here as faults
|
||||||
|
if self.fault_counts[key] == nil then self.fault_counts[key] = 0 end
|
||||||
|
|
||||||
ppm_sys.faulted = true
|
-- function failed
|
||||||
ppm_sys.last_fault = UNDEFINED_FIELD
|
self.faulted = true
|
||||||
|
self.last_fault = UNDEFINED_FIELD
|
||||||
|
|
||||||
if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
ppm_sys.faulted = true
|
||||||
local count_str = ""
|
ppm_sys.last_fault = UNDEFINED_FIELD
|
||||||
if self.fault_counts[key] > 0 then
|
|
||||||
count_str = " [" .. self.fault_counts[key] .. " total calls]"
|
if not ppm_sys.mute and (self.fault_counts[key] % REPORT_FREQUENCY == 0) then
|
||||||
|
local count_str = ""
|
||||||
|
if self.fault_counts[key] > 0 then
|
||||||
|
count_str = " [" .. self.fault_counts[key] .. " total calls]"
|
||||||
|
end
|
||||||
|
|
||||||
|
log.error(util.c("PPM: [@", iface, "] caught undefined function ", key, "()", count_str))
|
||||||
end
|
end
|
||||||
|
|
||||||
log.error(util.c("PPM: caught undefined function ", key, "()", count_str))
|
self.fault_counts[key] = self.fault_counts[key] + 1
|
||||||
end
|
|
||||||
|
|
||||||
self.fault_counts[key] = self.fault_counts[key] + 1
|
return ACCESS_FAULT, UNDEFINED_FIELD
|
||||||
|
end)
|
||||||
return (function () return ACCESS_FAULT end)
|
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,6 +319,17 @@ function ppm.handle_unmount(iface)
|
|||||||
return pm_type, pm_dev
|
return pm_type, pm_dev
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- log all mounts, to be used if `ppm.mount_all` is called before logging is ready
|
||||||
|
function ppm.log_mounts()
|
||||||
|
for iface, mount in pairs(ppm_sys.mounts) do
|
||||||
|
log.info(util.c("PPM: had found a ", mount.type, " (", iface, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
if util.table_len(ppm_sys.mounts) == 0 then
|
||||||
|
log.warning("PPM: no devices had been found")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- GENERAL ACCESSORS --
|
-- GENERAL ACCESSORS --
|
||||||
|
|
||||||
-- list all available peripherals
|
-- list all available peripherals
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ 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
|
F_ALARM_ANY = 8, -- active high, any alarm regardless of priority
|
||||||
|
F_MATRIX_LOW = 27, -- active high, induction matrix charge low
|
||||||
|
F_MATRIX_HIGH = 28, -- active high, induction matrix charge high
|
||||||
|
|
||||||
-- waste
|
-- waste
|
||||||
WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
|
WASTE_PU = 9, -- active low, waste -> plutonium -> pellets route
|
||||||
@@ -75,17 +77,27 @@ local IO_PORT = {
|
|||||||
|
|
||||||
-- unit outputs
|
-- unit outputs
|
||||||
U_ALARM = 25, -- active high, unit alarm
|
U_ALARM = 25, -- active high, unit alarm
|
||||||
U_EMER_COOL = 26 -- active low, emergency coolant control
|
U_EMER_COOL = 26, -- active low, emergency coolant control
|
||||||
|
|
||||||
|
-- analog outputs --
|
||||||
|
|
||||||
|
-- facility
|
||||||
|
F_MATRIX_CHG = 29 -- analog charge level of the induction matrix
|
||||||
}
|
}
|
||||||
|
|
||||||
rsio.IO_LVL = IO_LVL
|
rsio.IO_LVL = IO_LVL
|
||||||
rsio.IO_DIR = IO_DIR
|
rsio.IO_DIR = IO_DIR
|
||||||
rsio.IO_MODE = IO_MODE
|
rsio.IO_MODE = IO_MODE
|
||||||
rsio.IO = IO_PORT
|
rsio.IO = IO_PORT
|
||||||
rsio.NUM_PORTS = IO_PORT.U_EMER_COOL
|
|
||||||
|
rsio.NUM_PORTS = 29
|
||||||
|
rsio.NUM_DIG_PORTS = 28
|
||||||
|
rsio.NUM_ANA_PORTS = 1
|
||||||
|
|
||||||
-- self checks
|
-- self checks
|
||||||
|
|
||||||
|
assert(rsio.NUM_PORTS == (rsio.NUM_DIG_PORTS + rsio.NUM_ANA_PORTS), "port counts inconsistent")
|
||||||
|
|
||||||
local dup_chk = {}
|
local dup_chk = {}
|
||||||
for _, v in pairs(IO_PORT) do
|
for _, v in pairs(IO_PORT) do
|
||||||
assert(dup_chk[v] ~= true, "duplicate in port list")
|
assert(dup_chk[v] ~= true, "duplicate in port list")
|
||||||
@@ -96,64 +108,45 @@ assert(#dup_chk == rsio.NUM_PORTS, "port list malformed")
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Utility Functions
|
--#region Utility Functions and Attribute Tables
|
||||||
|
|
||||||
local PORT_NAMES = {
|
local IO = IO_PORT
|
||||||
"F_SCRAM",
|
|
||||||
"F_ACK",
|
|
||||||
"R_SCRAM",
|
|
||||||
"R_RESET",
|
|
||||||
"R_ENABLE",
|
|
||||||
"U_ACK",
|
|
||||||
"F_ALARM",
|
|
||||||
"F_ALARM_ANY",
|
|
||||||
"WASTE_PU",
|
|
||||||
"WASTE_PO",
|
|
||||||
"WASTE_POPL",
|
|
||||||
"WASTE_AM",
|
|
||||||
"R_ACTIVE",
|
|
||||||
"R_AUTO_CTRL",
|
|
||||||
"R_SCRAMMED",
|
|
||||||
"R_AUTO_SCRAM",
|
|
||||||
"R_HIGH_DMG",
|
|
||||||
"R_HIGH_TEMP",
|
|
||||||
"R_LOW_COOLANT",
|
|
||||||
"R_EXCESS_HC",
|
|
||||||
"R_EXCESS_WS",
|
|
||||||
"R_INSUFF_FUEL",
|
|
||||||
"R_PLC_FAULT",
|
|
||||||
"R_PLC_TIMEOUT",
|
|
||||||
"U_ALARM",
|
|
||||||
"U_EMER_COOL"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
-- list of all port names
|
||||||
|
local PORT_NAMES = {}
|
||||||
|
for k, v in pairs(IO) do PORT_NAMES[v] = k end
|
||||||
|
|
||||||
|
-- list of all port I/O modes
|
||||||
local MODES = {
|
local MODES = {
|
||||||
IO_MODE.DIGITAL_IN, -- F_SCRAM
|
[IO.F_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- F_ACK
|
[IO.F_ACK] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_SCRAM
|
[IO.R_SCRAM] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_RESET
|
[IO.R_RESET] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- R_ENABLE
|
[IO.R_ENABLE] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_IN, -- U_ACK
|
[IO.U_ACK] = IO_MODE.DIGITAL_IN,
|
||||||
IO_MODE.DIGITAL_OUT, -- F_ALARM
|
[IO.F_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- F_ALARM_ANY
|
[IO.F_ALARM_ANY] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_PU
|
[IO.F_MATRIX_LOW] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_PO
|
[IO.F_MATRIX_HIGH] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_POPL
|
[IO.WASTE_PU] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- WASTE_AM
|
[IO.WASTE_PO] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_ACTIVE
|
[IO.WASTE_POPL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_CTRL
|
[IO.WASTE_AM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_SCRAMMED
|
[IO.R_ACTIVE] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_AUTO_SCRAM
|
[IO.R_AUTO_CTRL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_DMG
|
[IO.R_SCRAMMED] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_HIGH_TEMP
|
[IO.R_AUTO_SCRAM] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_LOW_COOLANT
|
[IO.R_HIGH_DMG] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_HC
|
[IO.R_HIGH_TEMP] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_EXCESS_WS
|
[IO.R_LOW_COOLANT] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_INSUFF_FUEL
|
[IO.R_EXCESS_HC] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_PLC_FAULT
|
[IO.R_EXCESS_WS] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- R_PLC_TIMEOUT
|
[IO.R_INSUFF_FUEL] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT, -- U_ALARM
|
[IO.R_PLC_FAULT] = IO_MODE.DIGITAL_OUT,
|
||||||
IO_MODE.DIGITAL_OUT -- U_EMER_COOL
|
[IO.R_PLC_TIMEOUT] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.U_ALARM] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.U_EMER_COOL] = IO_MODE.DIGITAL_OUT,
|
||||||
|
[IO.F_MATRIX_CHG] = IO_MODE.ANALOG_OUT
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rsio.NUM_PORTS == #PORT_NAMES, "port names length incorrect")
|
assert(rsio.NUM_PORTS == #PORT_NAMES, "port names length incorrect")
|
||||||
@@ -179,74 +172,51 @@ local function _O_ACTIVE_LOW(active) if active then return IO_LVL.LOW else retur
|
|||||||
|
|
||||||
-- I/O mappings to I/O function and I/O mode
|
-- I/O mappings to I/O function and I/O mode
|
||||||
local RS_DIO_MAP = {
|
local RS_DIO_MAP = {
|
||||||
-- F_SCRAM
|
[IO.F_SCRAM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
[IO.F_ACK] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
-- F_ACK
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- R_SCRAM
|
[IO.R_SCRAM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.IN },
|
[IO.R_RESET] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
-- R_RESET
|
[IO.R_ENABLE] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
-- R_ENABLE
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- U_ACK
|
[IO.U_ACK] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.IN },
|
|
||||||
|
|
||||||
-- F_ALARM
|
[IO.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 },
|
[IO.F_ALARM_ANY] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- F_ALARM_ANY
|
[IO.F_MATRIX_LOW] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.F_MATRIX_HIGH] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
|
|
||||||
-- WASTE_PU
|
[IO.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 },
|
[IO.WASTE_PO] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
-- WASTE_PO
|
[IO.WASTE_POPL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
[IO.WASTE_AM] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
||||||
-- WASTE_POPL
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
|
||||||
-- WASTE_AM
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT },
|
|
||||||
|
|
||||||
-- R_ACTIVE
|
[IO.R_ACTIVE] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_AUTO_CTRL] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_AUTO_CTRL
|
[IO.R_SCRAMMED] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_AUTO_SCRAM] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_SCRAMMED
|
[IO.R_HIGH_DMG] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_HIGH_TEMP] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_AUTO_SCRAM
|
[IO.R_LOW_COOLANT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_EXCESS_HC] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_HIGH_DMG
|
[IO.R_EXCESS_WS] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_INSUFF_FUEL] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_HIGH_TEMP
|
[IO.R_PLC_FAULT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
[IO.R_PLC_TIMEOUT] = { _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
||||||
-- R_LOW_COOLANT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_EXCESS_HC
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_EXCESS_WS
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_INSUFF_FUEL
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_PLC_FAULT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
-- R_PLC_TIMEOUT
|
|
||||||
{ _in = _I_ACTIVE_HIGH, _out = _O_ACTIVE_HIGH, mode = IO_DIR.OUT },
|
|
||||||
|
|
||||||
-- U_ALARM
|
[IO.U_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 },
|
[IO.U_EMER_COOL] = { _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
||||||
-- U_EMER_COOL
|
|
||||||
{ _in = _I_ACTIVE_LOW, _out = _O_ACTIVE_LOW, mode = IO_DIR.OUT }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rsio.NUM_PORTS == #RS_DIO_MAP, "RS_DIO_MAP length incorrect")
|
assert(rsio.NUM_DIG_PORTS == #RS_DIO_MAP, "RS_DIO_MAP length incorrect")
|
||||||
|
|
||||||
-- get the I/O direction of a port
|
-- get the I/O direction of a port
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param port IO_PORT
|
---@param port IO_PORT
|
||||||
---@return IO_DIR
|
---@return IO_DIR
|
||||||
function rsio.get_io_dir(port)
|
function rsio.get_io_dir(port)
|
||||||
if rsio.is_valid_port(port) then return RS_DIO_MAP[port].mode
|
if rsio.is_valid_port(port) then
|
||||||
|
return util.trinary(MODES[port] == IO_MODE.DIGITAL_OUT or MODES[port] == IO_MODE.ANALOG_OUT, IO_DIR.OUT, IO_DIR.IN)
|
||||||
else return IO_DIR.IN end
|
else return IO_DIR.IN end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -310,6 +280,13 @@ end
|
|||||||
|
|
||||||
--#region Digital I/O
|
--#region Digital I/O
|
||||||
|
|
||||||
|
-- check if a port is digital
|
||||||
|
---@nodiscard
|
||||||
|
---@param port IO_PORT
|
||||||
|
function rsio.is_digital(port)
|
||||||
|
return rsio.is_valid_port(port) and (MODES[port] == IO_MODE.DIGITAL_IN or MODES[port] == IO_MODE.DIGITAL_OUT)
|
||||||
|
end
|
||||||
|
|
||||||
-- get digital I/O level reading from a redstone boolean input value
|
-- get digital I/O level reading from a redstone boolean input value
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param rs_value boolean raw value from redstone
|
---@param rs_value boolean raw value from redstone
|
||||||
@@ -330,7 +307,7 @@ function rsio.digital_write(level) return level == IO_LVL.HIGH end
|
|||||||
---@param active boolean state to convert to logic level
|
---@param active boolean state to convert to logic level
|
||||||
---@return IO_LVL|false
|
---@return IO_LVL|false
|
||||||
function rsio.digital_write_active(port, active)
|
function rsio.digital_write_active(port, active)
|
||||||
if (not util.is_int(port)) or (port < IO_PORT.F_ALARM) or (port > IO_PORT.U_EMER_COOL) then
|
if not rsio.is_digital(port) then
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
return RS_DIO_MAP[port]._out(active)
|
return RS_DIO_MAP[port]._out(active)
|
||||||
@@ -343,9 +320,7 @@ end
|
|||||||
---@param level IO_LVL logic level
|
---@param level IO_LVL logic level
|
||||||
---@return boolean|nil state true for active, false for inactive, or nil if invalid port or level provided
|
---@return boolean|nil state true for active, false for inactive, or nil if invalid port or level provided
|
||||||
function rsio.digital_is_active(port, level)
|
function rsio.digital_is_active(port, level)
|
||||||
if not util.is_int(port) then
|
if (not rsio.is_digital(port)) or level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
||||||
return nil
|
|
||||||
elseif level == IO_LVL.FLOATING or level == IO_LVL.DISCONNECT then
|
|
||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return RS_DIO_MAP[port]._in(level)
|
return RS_DIO_MAP[port]._in(level)
|
||||||
@@ -356,6 +331,13 @@ end
|
|||||||
|
|
||||||
--#region Analog I/O
|
--#region Analog I/O
|
||||||
|
|
||||||
|
-- check if a port is analog
|
||||||
|
---@nodiscard
|
||||||
|
---@param port IO_PORT
|
||||||
|
function rsio.is_analog(port)
|
||||||
|
return rsio.is_valid_port(port) and (MODES[port] == IO_MODE.ANALOG_IN or MODES[port] == IO_MODE.ANALOG_OUT)
|
||||||
|
end
|
||||||
|
|
||||||
-- read an analog value scaled from min to max
|
-- read an analog value scaled from min to max
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param rs_value number redstone reading (0 to 15)
|
---@param rs_value number redstone reading (0 to 15)
|
||||||
@@ -372,7 +354,7 @@ end
|
|||||||
---@param value number value to write (from min to max range)
|
---@param value number value to write (from min to max range)
|
||||||
---@param min number minimum of range
|
---@param min number minimum of range
|
||||||
---@param max number maximum of range
|
---@param max number maximum of range
|
||||||
---@return number rs_value scaled redstone reading (0 to 15)
|
---@return integer rs_value scaled redstone reading (0 to 15)
|
||||||
function rsio.analog_write(value, min, max)
|
function rsio.analog_write(value, min, max)
|
||||||
local scaled_value = (value - min) / (max - min)
|
local scaled_value = (value - min) / (max - min)
|
||||||
return math.floor(scaled_value * 15)
|
return math.floor(scaled_value * 15)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ local t_pack = table.pack
|
|||||||
local util = {}
|
local util = {}
|
||||||
|
|
||||||
-- scada-common version
|
-- scada-common version
|
||||||
util.version = "1.1.14"
|
util.version = "1.3.0"
|
||||||
|
|
||||||
util.TICK_TIME_S = 0.05
|
util.TICK_TIME_S = 0.05
|
||||||
util.TICK_TIME_MS = 50
|
util.TICK_TIME_MS = 50
|
||||||
@@ -181,8 +181,7 @@ function util.round(x) return math.floor(x + 0.5) end
|
|||||||
-- get a new moving average object
|
-- get a new moving average object
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param length integer history length
|
---@param length integer history length
|
||||||
---@param default number value to fill history with for first call to compute()
|
function util.mov_avg(length)
|
||||||
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
|
||||||
@@ -190,11 +189,15 @@ function util.mov_avg(length, default)
|
|||||||
---@class moving_average
|
---@class moving_average
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- reset all to a given value
|
-- reset all to a given value, or clear all data if no value is given
|
||||||
---@param x number value
|
---@param x number? value
|
||||||
function public.reset(x)
|
function public.reset(x)
|
||||||
|
index = 1
|
||||||
data = {}
|
data = {}
|
||||||
for _ = 1, length do t_insert(data, x) end
|
|
||||||
|
if x then
|
||||||
|
for _ = 1, length do t_insert(data, x) end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- record a new value
|
-- record a new value
|
||||||
@@ -214,12 +217,15 @@ function util.mov_avg(length, default)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@return number average
|
---@return number average
|
||||||
function public.compute()
|
function public.compute()
|
||||||
local sum = 0
|
if #data == 0 then return 0 end
|
||||||
for i = 1, length do sum = sum + data[i] end
|
|
||||||
return sum / length
|
|
||||||
end
|
|
||||||
|
|
||||||
public.reset(default)
|
local sum = 0
|
||||||
|
for i = 1, #data do
|
||||||
|
sum = sum + data[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
return sum / #data
|
||||||
|
end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
@@ -284,11 +290,13 @@ function util.cancel_timer(timer) os.cancelTimer(timer) end
|
|||||||
|
|
||||||
--#region PARALLELIZATION
|
--#region PARALLELIZATION
|
||||||
|
|
||||||
-- protected sleep call so we still are in charge of catching termination
|
-- protected sleep call so we still are in charge of catching termination<br>
|
||||||
---@param t integer seconds
|
-- returns the result of pcall
|
||||||
|
---@param t number seconds
|
||||||
|
---@return boolean success, any result, any ...
|
||||||
--- EVENT_CONSUMER: this function consumes events
|
--- EVENT_CONSUMER: this function consumes events
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
function util.psleep(t) pcall(os.sleep, t) end
|
function util.psleep(t) return pcall(os.sleep, t) 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
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ local tcd = require("scada-common.tcd")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local DisplayBox = require("graphics.elements.displaybox")
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
@@ -22,6 +23,8 @@ local RadioButton = require("graphics.elements.controls.radio_button")
|
|||||||
local NumberField = require("graphics.elements.form.number_field")
|
local NumberField = require("graphics.elements.form.number_field")
|
||||||
local TextField = require("graphics.elements.form.text_field")
|
local TextField = require("graphics.elements.form.text_field")
|
||||||
|
|
||||||
|
local IndLight = require("graphics.elements.indicators.light")
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local tri = util.trinary
|
local tri = util.trinary
|
||||||
|
|
||||||
@@ -32,7 +35,10 @@ 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
|
-- changes to the config data/format to let the user know
|
||||||
local changes = {}
|
local changes = {
|
||||||
|
{ "v1.2.12", { "Added front panel UI theme", "Added color accessibility modes" } },
|
||||||
|
{ "v1.3.2", { "Added standard with black off state color mode", "Added blue indicator color modes" } }
|
||||||
|
}
|
||||||
|
|
||||||
---@class svr_configurator
|
---@class svr_configurator
|
||||||
local configurator = {}
|
local configurator = {}
|
||||||
@@ -42,34 +48,25 @@ 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.colors = {
|
style.colors = themes.smooth_stone.colors
|
||||||
{ c = colors.red, hex = 0xdf4949 },
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xfffc79 },
|
|
||||||
{ c = colors.lime, hex = 0x80ff80 },
|
|
||||||
{ c = colors.green, hex = 0x4aee8a },
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0096ff },
|
|
||||||
{ c = colors.purple, hex = 0xb156ee },
|
|
||||||
{ c = colors.pink, hex = 0xf26ba2 },
|
|
||||||
{ c = colors.magenta, hex = 0xf9488a },
|
|
||||||
{ c = colors.lightGray, hex = 0xcacaca },
|
|
||||||
{ c = colors.gray, hex = 0x575757 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local bw_fg_bg = cpair(colors.black, colors.white)
|
local bw_fg_bg = cpair(colors.black, colors.white)
|
||||||
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
local g_lg_fg_bg = cpair(colors.gray, colors.lightGray)
|
||||||
local nav_fg_bg = bw_fg_bg
|
local nav_fg_bg = bw_fg_bg
|
||||||
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
local btn_act_fg_bg = cpair(colors.white, colors.gray)
|
||||||
|
|
||||||
|
---@class _svr_cfg_tool_ctl
|
||||||
local tool_ctl = {
|
local tool_ctl = {
|
||||||
ask_config = false,
|
ask_config = false,
|
||||||
has_config = false,
|
has_config = false,
|
||||||
viewing_config = false,
|
viewing_config = false,
|
||||||
importing_legacy = false,
|
importing_legacy = false,
|
||||||
|
jumped_to_color = false,
|
||||||
|
|
||||||
view_cfg = nil, ---@type graphics_element
|
view_cfg = nil, ---@type graphics_element
|
||||||
|
color_cfg = nil, ---@type graphics_element
|
||||||
|
color_next = nil, ---@type graphics_element
|
||||||
|
color_apply = nil, ---@type graphics_element
|
||||||
settings_apply = nil, ---@type graphics_element
|
settings_apply = nil, ---@type graphics_element
|
||||||
|
|
||||||
gen_summary = nil, ---@type function
|
gen_summary = nil, ---@type function
|
||||||
@@ -94,6 +91,7 @@ local tmp_cfg = {
|
|||||||
CoolingConfig = {},
|
CoolingConfig = {},
|
||||||
FacilityTankMode = 0,
|
FacilityTankMode = 0,
|
||||||
FacilityTankDefs = {},
|
FacilityTankDefs = {},
|
||||||
|
ExtChargeIdling = false,
|
||||||
SVR_Channel = nil, ---@type integer
|
SVR_Channel = nil, ---@type integer
|
||||||
PLC_Channel = nil, ---@type integer
|
PLC_Channel = nil, ---@type integer
|
||||||
RTU_Channel = nil, ---@type integer
|
RTU_Channel = nil, ---@type integer
|
||||||
@@ -108,6 +106,8 @@ local tmp_cfg = {
|
|||||||
LogMode = 0,
|
LogMode = 0,
|
||||||
LogPath = "",
|
LogPath = "",
|
||||||
LogDebug = false,
|
LogDebug = false,
|
||||||
|
FrontPanelTheme = 1,
|
||||||
|
ColorMode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class svr_config
|
---@class svr_config
|
||||||
@@ -121,6 +121,7 @@ local fields = {
|
|||||||
{ "CoolingConfig", "Cooling Configuration", {} },
|
{ "CoolingConfig", "Cooling Configuration", {} },
|
||||||
{ "FacilityTankMode", "Facility Tank Mode", 0 },
|
{ "FacilityTankMode", "Facility Tank Mode", 0 },
|
||||||
{ "FacilityTankDefs", "Facility Tank Definitions", {} },
|
{ "FacilityTankDefs", "Facility Tank Definitions", {} },
|
||||||
|
{ "ExtChargeIdling", "Extended Charge Idling", false },
|
||||||
{ "SVR_Channel", "SVR Channel", 16240 },
|
{ "SVR_Channel", "SVR Channel", 16240 },
|
||||||
{ "PLC_Channel", "PLC Channel", 16241 },
|
{ "PLC_Channel", "PLC Channel", 16241 },
|
||||||
{ "RTU_Channel", "RTU Channel", 16242 },
|
{ "RTU_Channel", "RTU Channel", 16242 },
|
||||||
@@ -134,7 +135,9 @@ local fields = {
|
|||||||
{ "AuthKey", "Facility Auth Key" , ""},
|
{ "AuthKey", "Facility Auth Key" , ""},
|
||||||
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
{ "LogMode", "Log Mode", log.MODE.APPEND },
|
||||||
{ "LogPath", "Log Path", "/log.txt" },
|
{ "LogPath", "Log Path", "/log.txt" },
|
||||||
{ "LogDebug","Log Debug Messages", false }
|
{ "LogDebug", "Log Debug Messages", false },
|
||||||
|
{ "FrontPanelTheme", "Front Panel Theme", themes.FP_THEME.SANDSTONE },
|
||||||
|
{ "ColorMode", "Color Mode", themes.COLOR_MODE.STANDARD }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- load data from the settings file
|
-- load data from the settings file
|
||||||
@@ -164,11 +167,12 @@ local function config_view(display)
|
|||||||
local svr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
local svr_cfg = Div{parent=root_pane_div,x=1,y=1}
|
||||||
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 clr_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 changelog = Div{parent=root_pane_div,x=1,y=1}
|
||||||
local import_err = Div{parent=root_pane_div,x=1,y=1}
|
local import_err = Div{parent=root_pane_div,x=1,y=1}
|
||||||
|
|
||||||
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,svr_cfg,net_cfg,log_cfg,summary,changelog,import_err}}
|
local main_pane = MultiPane{parent=root_pane_div,x=1,y=1,panes={main_page,svr_cfg,net_cfg,log_cfg,clr_cfg,summary,changelog,import_err}}
|
||||||
|
|
||||||
-- Main Page
|
-- Main Page
|
||||||
|
|
||||||
@@ -185,7 +189,7 @@ local function config_view(display)
|
|||||||
tool_ctl.viewing_config = true
|
tool_ctl.viewing_config = true
|
||||||
tool_ctl.gen_summary(settings_cfg)
|
tool_ctl.gen_summary(settings_cfg)
|
||||||
tool_ctl.settings_apply.hide(true)
|
tool_ctl.settings_apply.hide(true)
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
end
|
end
|
||||||
|
|
||||||
if fs.exists("/supervisor/config.lua") then
|
if fs.exists("/supervisor/config.lua") then
|
||||||
@@ -196,10 +200,21 @@ local function config_view(display)
|
|||||||
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=main_page,x=2,y=y_start,min_width=18,text="Configure System",callback=function()main_pane.set_value(2)end,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg}
|
||||||
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
tool_ctl.view_cfg = PushButton{parent=main_page,x=2,y=y_start+2,min_width=20,text="View Configuration",callback=view_config,fg_bg=cpair(colors.black,colors.blue),active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
|
||||||
if not tool_ctl.has_config then tool_ctl.view_cfg.disable() end
|
local function jump_color()
|
||||||
|
tool_ctl.jumped_to_color = true
|
||||||
|
tool_ctl.color_next.hide(true)
|
||||||
|
tool_ctl.color_apply.show()
|
||||||
|
main_pane.set_value(5)
|
||||||
|
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}
|
tool_ctl.color_cfg = PushButton{parent=main_page,x=23,y=17,min_width=15,text="Color Options",callback=jump_color,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg,dis_fg_bg=cpair(colors.lightGray,colors.white)}
|
||||||
|
PushButton{parent=main_page,x=39,y=17,min_width=12,text="Change Log",callback=function()main_pane.set_value(7)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
if not tool_ctl.has_config then
|
||||||
|
tool_ctl.view_cfg.disable()
|
||||||
|
tool_ctl.color_cfg.disable()
|
||||||
|
end
|
||||||
|
|
||||||
--#region Facility
|
--#region Facility
|
||||||
|
|
||||||
@@ -209,8 +224,9 @@ local function config_view(display)
|
|||||||
local svr_c_4 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_4 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
local svr_c_5 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_5 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
local svr_c_6 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
local svr_c_6 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
|
local svr_c_7 = Div{parent=svr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6}}
|
local svr_pane = MultiPane{parent=svr_cfg,x=1,y=4,panes={svr_c_1,svr_c_2,svr_c_3,svr_c_4,svr_c_5,svr_c_6,svr_c_7}}
|
||||||
|
|
||||||
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
TextBox{parent=svr_cfg,x=1,y=2,height=1,text=" Facility Configuration",fg_bg=cpair(colors.black,colors.yellow)}
|
||||||
|
|
||||||
@@ -316,7 +332,7 @@ local function config_view(display)
|
|||||||
else
|
else
|
||||||
tmp_cfg.FacilityTankMode = 0
|
tmp_cfg.FacilityTankMode = 0
|
||||||
tmp_cfg.FacilityTankDefs = {}
|
tmp_cfg.FacilityTankDefs = {}
|
||||||
main_pane.set_value(3)
|
svr_pane.set_value(7)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -550,7 +566,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
local function submit_mode()
|
local function submit_mode()
|
||||||
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
tmp_cfg.FacilityTankMode = tank_mode.get_value()
|
||||||
main_pane.set_value(3)
|
svr_pane.set_value(7)
|
||||||
end
|
end
|
||||||
|
|
||||||
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_5,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(4)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
@@ -564,6 +580,23 @@ local function config_view(display)
|
|||||||
|
|
||||||
PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
PushButton{parent=svr_c_6,x=1,y=14,text="\x1b Back",callback=function()svr_pane.set_value(5)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=svr_c_7,height=6,text="Charge control provides automatic control to maintain an induction matrix charge level. In order to have smoother control, reactors that were activated will be held on at 0.01 mB/t for a short period before allowing them to turn off. This minimizes overshooting the charge target."}
|
||||||
|
TextBox{parent=svr_c_7,y=8,height=3,text="You can extend this to a full minute to minimize reactors flickering on/off, but there may be more overshoot of the target."}
|
||||||
|
|
||||||
|
local ext_idling = CheckBox{parent=svr_c_7,x=1,y=12,label="Enable Extended Idling",default=ini_cfg.ExtChargeIdling,box_fg_bg=cpair(colors.yellow,colors.black)}
|
||||||
|
|
||||||
|
local function back_from_idling()
|
||||||
|
svr_pane.set_value(util.trinary(tmp_cfg.FacilityTankMode == 0, 3, 5))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_idling()
|
||||||
|
tmp_cfg.ExtChargeIdling = ext_idling.get_value()
|
||||||
|
main_pane.set_value(3)
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=svr_c_7,x=1,y=14,text="\x1b Back",callback=back_from_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=svr_c_7,x=44,y=14,text="Next \x1a",callback=submit_idling,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region Network
|
--#region Network
|
||||||
@@ -721,10 +754,8 @@ local function config_view(display)
|
|||||||
tmp_cfg.LogMode = mode.get_value() - 1
|
tmp_cfg.LogMode = mode.get_value() - 1
|
||||||
tmp_cfg.LogPath = path.get_value()
|
tmp_cfg.LogPath = path.get_value()
|
||||||
tmp_cfg.LogDebug = en_dbg.get_value()
|
tmp_cfg.LogDebug = en_dbg.get_value()
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.color_apply.hide(true)
|
||||||
tool_ctl.viewing_config = false
|
tool_ctl.color_next.show()
|
||||||
tool_ctl.importing_legacy = false
|
|
||||||
tool_ctl.settings_apply.show()
|
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(5)
|
||||||
else path_err.show() end
|
else path_err.show() end
|
||||||
end
|
end
|
||||||
@@ -734,6 +765,116 @@ local function config_view(display)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
|
--#region Color Options
|
||||||
|
|
||||||
|
local clr_c_1 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_2 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_3 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
local clr_c_4 = Div{parent=clr_cfg,x=2,y=4,width=49}
|
||||||
|
|
||||||
|
local clr_pane = MultiPane{parent=clr_cfg,x=1,y=4,panes={clr_c_1,clr_c_2,clr_c_3,clr_c_4}}
|
||||||
|
|
||||||
|
TextBox{parent=clr_cfg,x=1,y=2,height=1,text=" Color Configuration",fg_bg=cpair(colors.black,colors.magenta)}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=1,height=2,text="Here you can select the color theme for the front panel."}
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=4,height=2,text="Click 'Accessibility' below to access colorblind assistive options.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_1,x=1,y=7,height=1,text="Front Panel Theme"}
|
||||||
|
local fp_theme = RadioButton{parent=clr_c_1,x=1,y=8,default=ini_cfg.FrontPanelTheme,options=themes.FP_THEME_NAMES,callback=function()end,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=1,height=6,text="This system uses color heavily to distinguish ok and not, with some indicators using many colors. By selecting a mode below, indicators will change as shown. For non-standard modes, indicators with more than two colors will be split up."}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=7,height=1,text="Preview"}
|
||||||
|
local _ = IndLight{parent=clr_c_2,x=21,y=8,label="Good",colors=cpair(colors.black,colors.green)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=9,label="Warning",colors=cpair(colors.black,colors.yellow)}
|
||||||
|
_ = IndLight{parent=clr_c_2,x=21,y=10,label="Bad",colors=cpair(colors.black,colors.red)}
|
||||||
|
local b_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.black,colors.black),hidden=true}
|
||||||
|
local g_off = IndLight{parent=clr_c_2,x=21,y=11,label="Off",colors=cpair(colors.gray,colors.gray),hidden=true}
|
||||||
|
|
||||||
|
local function recolor(value)
|
||||||
|
local c = themes.smooth_stone.color_modes[value]
|
||||||
|
|
||||||
|
if value == themes.COLOR_MODE.STANDARD or value == themes.COLOR_MODE.BLUE_IND then
|
||||||
|
b_off.hide()
|
||||||
|
g_off.show()
|
||||||
|
else
|
||||||
|
g_off.hide()
|
||||||
|
b_off.show()
|
||||||
|
end
|
||||||
|
|
||||||
|
if #c == 0 then
|
||||||
|
for i = 1, #style.colors do term.setPaletteColor(style.colors[i].c, style.colors[i].hex) end
|
||||||
|
else
|
||||||
|
term.setPaletteColor(colors.green, c[1].hex)
|
||||||
|
term.setPaletteColor(colors.yellow, c[2].hex)
|
||||||
|
term.setPaletteColor(colors.red, c[3].hex)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=1,y=7,height=1,width=10,text="Color Mode"}
|
||||||
|
local c_mode = RadioButton{parent=clr_c_2,x=1,y=8,default=ini_cfg.ColorMode,options=themes.COLOR_MODE_NAMES,callback=recolor,radio_colors=cpair(colors.lightGray,colors.black),select_color=colors.magenta}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_2,x=21,y=13,height=2,width=18,text="Note: exact color varies by theme.",fg_bg=g_lg_fg_bg}
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_2,x=44,y=14,min_width=6,text="Done",callback=function()clr_pane.set_value(1)end,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
local function back_from_colors()
|
||||||
|
main_pane.set_value(util.trinary(tool_ctl.jumped_to_color, 1, 4))
|
||||||
|
tool_ctl.jumped_to_color = false
|
||||||
|
recolor(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function show_access()
|
||||||
|
clr_pane.set_value(2)
|
||||||
|
recolor(c_mode.get_value())
|
||||||
|
end
|
||||||
|
|
||||||
|
local function submit_colors()
|
||||||
|
tmp_cfg.FrontPanelTheme = fp_theme.get_value()
|
||||||
|
tmp_cfg.ColorMode = c_mode.get_value()
|
||||||
|
|
||||||
|
if tool_ctl.jumped_to_color then
|
||||||
|
settings.set("FrontPanelTheme", tmp_cfg.FrontPanelTheme)
|
||||||
|
settings.set("ColorMode", tmp_cfg.ColorMode)
|
||||||
|
|
||||||
|
if settings.save("/supervisor.settings") then
|
||||||
|
load_settings(settings_cfg, true)
|
||||||
|
load_settings(ini_cfg)
|
||||||
|
clr_pane.set_value(3)
|
||||||
|
else
|
||||||
|
clr_pane.set_value(4)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
|
tool_ctl.viewing_config = false
|
||||||
|
tool_ctl.importing_legacy = false
|
||||||
|
tool_ctl.settings_apply.show()
|
||||||
|
main_pane.set_value(6)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
PushButton{parent=clr_c_1,x=1,y=14,text="\x1b Back",callback=back_from_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
PushButton{parent=clr_c_1,x=8,y=14,min_width=15,text="Accessibility",callback=show_access,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_next = PushButton{parent=clr_c_1,x=44,y=14,text="Next \x1a",callback=submit_colors,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
tool_ctl.color_apply = PushButton{parent=clr_c_1,x=43,y=14,min_width=7,text="Apply",callback=submit_colors,fg_bg=cpair(colors.black,colors.green),active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
tool_ctl.color_apply.hide(true)
|
||||||
|
|
||||||
|
local function c_go_home()
|
||||||
|
main_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_3,x=1,y=1,height=1,text="Settings saved!"}
|
||||||
|
PushButton{parent=clr_c_3,x=1,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=clr_c_3,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
TextBox{parent=clr_c_4,x=1,y=1,height=5,text="Failed to save the settings file.\n\nThere may not be enough space for the modification or server file permissions may be denying writes."}
|
||||||
|
PushButton{parent=clr_c_4,x=1,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=clr_c_4,x=44,y=14,min_width=6,text="Home",callback=c_go_home,fg_bg=nav_fg_bg,active_fg_bg=btn_act_fg_bg}
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
--#region Summary and Saving
|
--#region Summary and Saving
|
||||||
|
|
||||||
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
local sum_c_1 = Div{parent=summary,x=2,y=4,width=49}
|
||||||
@@ -754,7 +895,7 @@ local function config_view(display)
|
|||||||
tool_ctl.importing_legacy = false
|
tool_ctl.importing_legacy = false
|
||||||
tool_ctl.settings_apply.show()
|
tool_ctl.settings_apply.show()
|
||||||
else
|
else
|
||||||
main_pane.set_value(4)
|
main_pane.set_value(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -787,6 +928,8 @@ local function config_view(display)
|
|||||||
try_set(mode, ini_cfg.LogMode)
|
try_set(mode, ini_cfg.LogMode)
|
||||||
try_set(path, ini_cfg.LogPath)
|
try_set(path, ini_cfg.LogPath)
|
||||||
try_set(en_dbg, ini_cfg.LogDebug)
|
try_set(en_dbg, ini_cfg.LogDebug)
|
||||||
|
try_set(fp_theme, ini_cfg.FrontPanelTheme)
|
||||||
|
try_set(c_mode, ini_cfg.ColorMode)
|
||||||
|
|
||||||
for i = 1, #ini_cfg.CoolingConfig do
|
for i = 1, #ini_cfg.CoolingConfig do
|
||||||
local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i]
|
local cfg, elems = ini_cfg.CoolingConfig[i], tool_ctl.cooling_elems[i]
|
||||||
@@ -824,6 +967,7 @@ local function config_view(display)
|
|||||||
main_pane.set_value(1)
|
main_pane.set_value(1)
|
||||||
svr_pane.set_value(1)
|
svr_pane.set_value(1)
|
||||||
net_pane.set_value(1)
|
net_pane.set_value(1)
|
||||||
|
clr_pane.set_value(1)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -887,7 +1031,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then
|
if config.REACTOR_COOLING == nil or tmp_cfg.UnitCount ~= #config.REACTOR_COOLING then
|
||||||
import_err_msg.set_value("Cooling configuration table length must match the number of units.")
|
import_err_msg.set_value("Cooling configuration table length must match the number of units.")
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -896,7 +1040,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
if type(cfg) ~= "table" then
|
if type(cfg) ~= "table" then
|
||||||
import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.")
|
import_err_msg.set_value("Cooling configuration for unit " .. i .. " must be a table.")
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -907,14 +1051,14 @@ local function config_view(display)
|
|||||||
|
|
||||||
if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then
|
if not (util.is_int(tmp_cfg.FacilityTankMode) and tmp_cfg.FacilityTankMode >= 0 and tmp_cfg.FacilityTankMode <= 8) then
|
||||||
import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.")
|
import_err_msg.set_value("Invalid tank mode present in config. FAC_TANK_MODE must be a number 0 through 8.")
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if config.FAC_TANK_MODE > 0 then
|
if config.FAC_TANK_MODE > 0 then
|
||||||
if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then
|
if config.FAC_TANK_DEFS == nil or tmp_cfg.UnitCount ~= #config.FAC_TANK_DEFS then
|
||||||
import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.")
|
import_err_msg.set_value("Facility tank definitions table length must match the number of units when using facility tanks.")
|
||||||
main_pane.set_value(7)
|
main_pane.set_value(8)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -945,7 +1089,7 @@ local function config_view(display)
|
|||||||
|
|
||||||
tool_ctl.gen_summary(tmp_cfg)
|
tool_ctl.gen_summary(tmp_cfg)
|
||||||
sum_pane.set_value(1)
|
sum_pane.set_value(1)
|
||||||
main_pane.set_value(5)
|
main_pane.set_value(6)
|
||||||
tool_ctl.importing_legacy = true
|
tool_ctl.importing_legacy = true
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -976,6 +1120,10 @@ local function config_view(display)
|
|||||||
|
|
||||||
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
if f[1] == "AuthKey" then val = string.rep("*", string.len(val))
|
||||||
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
elseif f[1] == "LogMode" then val = util.trinary(raw == log.MODE.APPEND, "append", "replace")
|
||||||
|
elseif f[1] == "FrontPanelTheme" then
|
||||||
|
val = util.strval(themes.fp_theme_name(raw))
|
||||||
|
elseif f[1] == "ColorMode" then
|
||||||
|
val = util.strval(themes.color_mode_name(raw))
|
||||||
elseif f[1] == "CoolingConfig" and type(cfg.CoolingConfig) == "table" then
|
elseif f[1] == "CoolingConfig" and type(cfg.CoolingConfig) == "table" then
|
||||||
val = ""
|
val = ""
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ function databus.tx_plc_rtt(reactor_id, rtt)
|
|||||||
elseif rtt > WARN_RTT then
|
elseif rtt > WARN_RTT then
|
||||||
databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.yellow_hc)
|
databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.yellow_hc)
|
||||||
else
|
else
|
||||||
databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.green)
|
databus.ps.publish("plc_" .. reactor_id .. "_rtt_color", colors.green_hc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -95,7 +95,7 @@ function databus.tx_rtu_rtt(session_id, rtt)
|
|||||||
elseif rtt > WARN_RTT then
|
elseif rtt > WARN_RTT then
|
||||||
databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
||||||
else
|
else
|
||||||
databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.green)
|
databus.ps.publish("rtu_" .. session_id .. "_rtt_color", colors.green_hc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ function databus.tx_crd_rtt(rtt)
|
|||||||
elseif rtt > WARN_RTT then
|
elseif rtt > WARN_RTT then
|
||||||
databus.ps.publish("crd_rtt_color", colors.yellow_hc)
|
databus.ps.publish("crd_rtt_color", colors.yellow_hc)
|
||||||
else
|
else
|
||||||
databus.ps.publish("crd_rtt_color", colors.green)
|
databus.ps.publish("crd_rtt_color", colors.green_hc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ function databus.tx_pdg_rtt(session_id, rtt)
|
|||||||
elseif rtt > WARN_RTT then
|
elseif rtt > WARN_RTT then
|
||||||
databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.yellow_hc)
|
||||||
else
|
else
|
||||||
databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.green)
|
databus.ps.publish("pdg_" .. session_id .. "_rtt_color", colors.green_hc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
|||||||
local WASTE_MODE = types.WASTE_MODE
|
local WASTE_MODE = types.WASTE_MODE
|
||||||
local WASTE = types.WASTE_PRODUCT
|
local WASTE = types.WASTE_PRODUCT
|
||||||
|
|
||||||
local IO = rsio.IO
|
local IO = rsio.IO
|
||||||
|
|
||||||
local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA
|
local DTV_RTU_S_DATA = qtypes.DTV_RTU_S_DATA
|
||||||
|
|
||||||
@@ -50,9 +50,9 @@ local START_STATUS = {
|
|||||||
BLADE_MISMATCH = 2
|
BLADE_MISMATCH = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
local charge_Kp = 0.275
|
local charge_Kp = 0.15
|
||||||
local charge_Ki = 0.0
|
local charge_Ki = 0.0
|
||||||
local charge_Kd = 4.5
|
local charge_Kd = 0.6
|
||||||
|
|
||||||
local rate_Kp = 2.45
|
local rate_Kp = 2.45
|
||||||
local rate_Ki = 0.4825
|
local rate_Ki = 0.4825
|
||||||
@@ -63,9 +63,9 @@ local facility = {}
|
|||||||
|
|
||||||
-- create a new facility management object
|
-- create a new facility management object
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param num_reactors integer number of reactor units
|
---@param config svr_config supervisor configuration
|
||||||
---@param cooling_conf sv_cooling_conf cooling configurations of reactor units
|
---@param cooling_conf sv_cooling_conf cooling configurations of reactor units
|
||||||
function facility.new(num_reactors, cooling_conf)
|
function facility.new(config, cooling_conf)
|
||||||
local self = {
|
local self = {
|
||||||
units = {},
|
units = {},
|
||||||
status_text = { "START UP", "initializing..." },
|
status_text = { "START UP", "initializing..." },
|
||||||
@@ -120,6 +120,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
waste_product = WASTE.PLUTONIUM,
|
waste_product = WASTE.PLUTONIUM,
|
||||||
current_waste_product = WASTE.PLUTONIUM,
|
current_waste_product = WASTE.PLUTONIUM,
|
||||||
pu_fallback = false,
|
pu_fallback = false,
|
||||||
|
sps_low_power = false,
|
||||||
|
disabled_sps = false,
|
||||||
-- alarm tones
|
-- alarm tones
|
||||||
tone_states = {},
|
tone_states = {},
|
||||||
test_tone_set = false,
|
test_tone_set = false,
|
||||||
@@ -128,14 +130,21 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
test_alarm_states = {},
|
test_alarm_states = {},
|
||||||
-- statistics
|
-- statistics
|
||||||
im_stat_init = false,
|
im_stat_init = false,
|
||||||
avg_charge = util.mov_avg(3, 0.0),
|
avg_charge = util.mov_avg(3), -- 3 seconds
|
||||||
avg_inflow = util.mov_avg(6, 0.0),
|
avg_inflow = util.mov_avg(6), -- 3 seconds
|
||||||
avg_outflow = util.mov_avg(6, 0.0)
|
avg_outflow = util.mov_avg(6), -- 3 seconds
|
||||||
|
-- induction matrix charge delta stats
|
||||||
|
avg_net = util.mov_avg(60), -- 60 seconds
|
||||||
|
imtx_last_capacity = 0,
|
||||||
|
imtx_last_charge = 0,
|
||||||
|
imtx_last_charge_t = 0,
|
||||||
|
-- track faulted induction matrix update times to reject
|
||||||
|
imtx_faulted_times = { 0, 0, 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
-- create units
|
-- create units
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BoilerCount, cooling_conf.r_cool[i].TurbineCount))
|
table.insert(self.units, unit.new(i, cooling_conf.r_cool[i].BoilerCount, cooling_conf.r_cool[i].TurbineCount, config.ExtChargeIdling))
|
||||||
table.insert(self.group_map, 0)
|
table.insert(self.group_map, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -152,6 +161,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
table.insert(self.test_tone_states, false)
|
table.insert(self.test_tone_states, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
-- check if all auto-controlled units completed ramping
|
-- check if all auto-controlled units completed ramping
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
local function _all_units_ramped()
|
local function _all_units_ramped()
|
||||||
@@ -223,12 +234,20 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
return unallocated, false
|
return unallocated, false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set idle state of all assigned reactors
|
||||||
|
---@param idle boolean idle state
|
||||||
|
local function _set_idling(idle)
|
||||||
|
for i = 1, #self.prio_defs do
|
||||||
|
for _, u in pairs(self.prio_defs[i]) do u.auto_set_idle(idle) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- PUBLIC FUNCTIONS --
|
-- PUBLIC FUNCTIONS --
|
||||||
|
|
||||||
---@class facility
|
---@class facility
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- ADD/LINK DEVICES --
|
--#region Add/Link Devices
|
||||||
|
|
||||||
-- link a redstone RTU session
|
-- link a redstone RTU session
|
||||||
---@param rs_unit unit_session
|
---@param rs_unit unit_session
|
||||||
@@ -268,11 +287,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
for _, v in pairs(self.rtu_list) do util.filter_table(v, function (s) return s.get_session_id() ~= session end) end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- UPDATE --
|
--#endregion
|
||||||
|
|
||||||
-- supervisor sessions reporting the list of active RTU sessions
|
--#region Update
|
||||||
---@param rtu_sessions table session list of all connected RTUs
|
|
||||||
function public.report_rtus(rtu_sessions) self.rtu_conn_count = #rtu_sessions end
|
|
||||||
|
|
||||||
-- update (iterate) the facility management
|
-- update (iterate) the facility management
|
||||||
function public.update()
|
function public.update()
|
||||||
@@ -292,23 +309,68 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
|
|
||||||
-- calculate moving averages for induction matrix
|
-- calculate moving averages for induction matrix
|
||||||
if self.induction[1] ~= nil then
|
if self.induction[1] ~= nil then
|
||||||
local matrix = self.induction[1] ---@type unit_session
|
local matrix = self.induction[1] ---@type unit_session
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
local db = matrix.get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
charge_update = db.tanks.last_update
|
local build_update = db.build.last_update
|
||||||
rate_update = db.state.last_update
|
rate_update = db.state.last_update
|
||||||
|
charge_update = db.tanks.last_update
|
||||||
|
|
||||||
|
local has_data = build_update > 0 and rate_update > 0 and charge_update > 0
|
||||||
|
|
||||||
|
if matrix.is_faulted() then
|
||||||
|
-- a fault occured, cannot reliably update stats
|
||||||
|
has_data = false
|
||||||
|
self.im_stat_init = false
|
||||||
|
self.imtx_faulted_times = { build_update, rate_update, charge_update }
|
||||||
|
elseif not self.im_stat_init then
|
||||||
|
-- prevent operation with partially invalid data
|
||||||
|
-- all fields must have updated since the last fault
|
||||||
|
has_data = self.imtx_faulted_times[1] < build_update and
|
||||||
|
self.imtx_faulted_times[2] < rate_update and
|
||||||
|
self.imtx_faulted_times[3] < charge_update
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_data then
|
||||||
|
local energy = util.joules_to_fe(db.tanks.energy)
|
||||||
|
local input = util.joules_to_fe(db.state.last_input)
|
||||||
|
local output = util.joules_to_fe(db.state.last_output)
|
||||||
|
|
||||||
if (charge_update > 0) and (rate_update > 0) then
|
|
||||||
if self.im_stat_init then
|
if self.im_stat_init then
|
||||||
self.avg_charge.record(util.joules_to_fe(db.tanks.energy), charge_update)
|
self.avg_charge.record(energy, charge_update)
|
||||||
self.avg_inflow.record(util.joules_to_fe(db.state.last_input), rate_update)
|
self.avg_inflow.record(input, rate_update)
|
||||||
self.avg_outflow.record(util.joules_to_fe(db.state.last_output), rate_update)
|
self.avg_outflow.record(output, rate_update)
|
||||||
|
|
||||||
|
if charge_update ~= self.imtx_last_charge_t then
|
||||||
|
local delta = (energy - self.imtx_last_charge) / (charge_update - self.imtx_last_charge_t)
|
||||||
|
|
||||||
|
self.imtx_last_charge = energy
|
||||||
|
self.imtx_last_charge_t = charge_update
|
||||||
|
|
||||||
|
-- if the capacity changed, toss out existing data
|
||||||
|
if db.build.max_energy ~= self.imtx_last_capacity then
|
||||||
|
self.imtx_last_capacity = db.build.max_energy
|
||||||
|
self.avg_net.reset()
|
||||||
|
else
|
||||||
|
self.avg_net.record(delta, charge_update)
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self.im_stat_init = true
|
self.im_stat_init = true
|
||||||
self.avg_charge.reset(util.joules_to_fe(db.tanks.energy))
|
|
||||||
self.avg_inflow.reset(util.joules_to_fe(db.state.last_input))
|
self.avg_charge.reset(energy)
|
||||||
self.avg_outflow.reset(util.joules_to_fe(db.state.last_output))
|
self.avg_inflow.reset(input)
|
||||||
|
self.avg_outflow.reset(output)
|
||||||
|
self.avg_net.reset()
|
||||||
|
|
||||||
|
self.imtx_last_capacity = db.build.max_energy
|
||||||
|
self.imtx_last_charge = energy
|
||||||
|
self.imtx_last_charge_t = charge_update
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- prevent use by control systems
|
||||||
|
rate_update = 0
|
||||||
|
charge_update = 0
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self.im_stat_init = false
|
self.im_stat_init = false
|
||||||
@@ -323,12 +385,13 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- Run Process Control --
|
-- Run Process Control --
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
--#region Process Control
|
--#region
|
||||||
|
|
||||||
local avg_charge = self.avg_charge.compute()
|
local avg_charge = self.avg_charge.compute()
|
||||||
local avg_inflow = self.avg_inflow.compute()
|
local avg_inflow = self.avg_inflow.compute()
|
||||||
|
local avg_outflow = self.avg_outflow.compute()
|
||||||
|
|
||||||
local now = util.time_s()
|
local now = os.clock()
|
||||||
|
|
||||||
local state_changed = self.mode ~= self.last_mode
|
local state_changed = self.mode ~= self.last_mode
|
||||||
local next_mode = self.mode
|
local next_mode = self.mode
|
||||||
@@ -390,6 +453,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- disable reactors and disengage auto control
|
-- disable reactors and disengage auto control
|
||||||
for _, u in pairs(self.prio_defs[i]) do
|
for _, u in pairs(self.prio_defs[i]) do
|
||||||
u.disable()
|
u.disable()
|
||||||
|
u.auto_set_idle(false)
|
||||||
u.auto_disengage()
|
u.auto_disengage()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -460,9 +524,12 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
self.last_error = 0
|
self.last_error = 0
|
||||||
self.accumulator = 0
|
self.accumulator = 0
|
||||||
|
|
||||||
|
-- enabling idling on all assigned units
|
||||||
|
_set_idling(true)
|
||||||
|
|
||||||
self.status_text = { "CHARGE MODE", "running control loop" }
|
self.status_text = { "CHARGE MODE", "running control loop" }
|
||||||
log.info("FAC: CHARGE mode starting PID control")
|
log.info("FAC: CHARGE mode starting PID control")
|
||||||
elseif self.last_update ~= charge_update then
|
elseif self.last_update < charge_update then
|
||||||
-- convert to kFE to make constants not microscopic
|
-- convert to kFE to make constants not microscopic
|
||||||
local error = util.round((self.charge_setpoint - avg_charge) / 1000) / 1000
|
local error = util.round((self.charge_setpoint - avg_charge) / 1000) / 1000
|
||||||
|
|
||||||
@@ -475,9 +542,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
local integral = self.accumulator
|
local integral = self.accumulator
|
||||||
local derivative = (error - self.last_error) / (now - self.last_time)
|
local derivative = (error - self.last_error) / (now - self.last_time)
|
||||||
|
|
||||||
local P = (charge_Kp * error)
|
local P = charge_Kp * error
|
||||||
local I = (charge_Ki * integral)
|
local I = charge_Ki * integral
|
||||||
local D = (charge_Kd * derivative)
|
local D = charge_Kd * derivative
|
||||||
|
|
||||||
local output = P + I + D
|
local output = P + I + D
|
||||||
|
|
||||||
@@ -486,7 +553,12 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
|
|
||||||
self.saturated = output ~= out_c
|
self.saturated = output ~= out_c
|
||||||
|
|
||||||
-- log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%d] }",
|
if not config.ExtChargeIdling then
|
||||||
|
-- stop idling early if the output is zero, we are at or above the setpoint, and are not losing charge
|
||||||
|
_set_idling(not ((out_c == 0) and (error <= 0) and (avg_outflow <= 0)))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- log.debug(util.sprintf("CHARGE[%f] { CHRG[%f] ERR[%f] INT[%f] => OUT[%f] OUT_C[%f] <= P[%f] I[%f] D[%f] }",
|
||||||
-- runtime, avg_charge, error, integral, output, out_c, P, I, D))
|
-- runtime, avg_charge, error, integral, output, out_c, P, I, D))
|
||||||
|
|
||||||
_allocate_burn_rate(out_c, true)
|
_allocate_burn_rate(out_c, true)
|
||||||
@@ -531,7 +603,7 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
self.status_text = { "GENERATION MODE", "running control loop" }
|
self.status_text = { "GENERATION MODE", "running control loop" }
|
||||||
log.info("FAC: GEN_RATE process mode initial hold completed, starting PID control")
|
log.info("FAC: GEN_RATE process mode initial hold completed, starting PID control")
|
||||||
end
|
end
|
||||||
elseif self.last_update ~= rate_update then
|
elseif self.last_update < rate_update then
|
||||||
-- convert to MFE (in rounded kFE) to make constants not microscopic
|
-- convert to MFE (in rounded kFE) to make constants not microscopic
|
||||||
local error = util.round((self.gen_rate_setpoint - avg_inflow) / 1000) / 1000
|
local error = util.round((self.gen_rate_setpoint - avg_inflow) / 1000) / 1000
|
||||||
|
|
||||||
@@ -544,9 +616,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
local integral = self.accumulator
|
local integral = self.accumulator
|
||||||
local derivative = (error - self.last_error) / (now - self.last_time)
|
local derivative = (error - self.last_error) / (now - self.last_time)
|
||||||
|
|
||||||
local P = (rate_Kp * error)
|
local P = rate_Kp * error
|
||||||
local I = (rate_Ki * integral)
|
local I = rate_Ki * integral
|
||||||
local D = (rate_Kd * derivative)
|
local D = rate_Kd * derivative
|
||||||
|
|
||||||
-- velocity (rate) (derivative of charge level => rate) feed forward
|
-- velocity (rate) (derivative of charge level => rate) feed forward
|
||||||
local FF = self.gen_rate_setpoint / self.charge_conversion
|
local FF = self.gen_rate_setpoint / self.charge_conversion
|
||||||
@@ -597,13 +669,12 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- Evaluate Automatic SCRAM --
|
-- Evaluate Automatic SCRAM --
|
||||||
------------------------------
|
------------------------------
|
||||||
|
|
||||||
--#region Automatic SCRAM
|
--#region
|
||||||
|
|
||||||
local astatus = self.ascram_status
|
local astatus = self.ascram_status
|
||||||
|
|
||||||
if self.induction[1] ~= nil then
|
if self.induction[1] ~= nil then
|
||||||
local matrix = self.induction[1] ---@type unit_session
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
|
||||||
|
|
||||||
-- clear matrix disconnected
|
-- clear matrix disconnected
|
||||||
if astatus.matrix_dc then
|
if astatus.matrix_dc then
|
||||||
@@ -727,6 +798,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
-- Handle Redstone I/O --
|
-- Handle Redstone I/O --
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
--#region
|
||||||
|
|
||||||
if #self.redstone > 0 then
|
if #self.redstone > 0 then
|
||||||
-- handle facility SCRAM
|
-- handle facility SCRAM
|
||||||
if self.io_ctl.digital_read(IO.F_SCRAM) then
|
if self.io_ctl.digital_read(IO.F_SCRAM) then
|
||||||
@@ -754,12 +827,25 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
|
|
||||||
self.io_ctl.digital_write(IO.F_ALARM, has_prio_alarm)
|
self.io_ctl.digital_write(IO.F_ALARM, has_prio_alarm)
|
||||||
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
|
self.io_ctl.digital_write(IO.F_ALARM_ANY, has_any_alarm)
|
||||||
|
|
||||||
|
-- update induction matrix related outputs
|
||||||
|
if self.induction[1] ~= nil then
|
||||||
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
|
self.io_ctl.digital_write(IO.F_MATRIX_LOW, db.tanks.energy_fill < const.RS_THRESHOLDS.IMATRIX_CHARGE_LOW)
|
||||||
|
self.io_ctl.digital_write(IO.F_MATRIX_HIGH, db.tanks.energy_fill > const.RS_THRESHOLDS.IMATRIX_CHARGE_HIGH)
|
||||||
|
self.io_ctl.analog_write(IO.F_MATRIX_CHG, db.tanks.energy_fill, 0, 1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
----------------
|
----------------
|
||||||
-- Unit Tasks --
|
-- Unit Tasks --
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
|
--#region
|
||||||
|
|
||||||
local insufficent_po_rate = false
|
local insufficent_po_rate = false
|
||||||
local need_emcool = false
|
local need_emcool = false
|
||||||
|
|
||||||
@@ -780,9 +866,25 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- update waste product
|
-- update waste product
|
||||||
if self.waste_product == WASTE.PLUTONIUM or (self.pu_fallback and insufficent_po_rate) then
|
|
||||||
|
self.current_waste_product = self.waste_product
|
||||||
|
|
||||||
|
if (not self.sps_low_power) and (self.waste_product == WASTE.ANTI_MATTER) and (self.induction[1] ~= nil) then
|
||||||
|
local db = self.induction[1].get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
|
if db.tanks.energy_fill >= 0.15 then
|
||||||
|
self.disabled_sps = false
|
||||||
|
elseif self.disabled_sps or ((db.tanks.last_update > 0) and (db.tanks.energy_fill < 0.1)) then
|
||||||
|
self.disabled_sps = true
|
||||||
|
self.current_waste_product = WASTE.POLONIUM
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.disabled_sps = false
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.pu_fallback and insufficent_po_rate then
|
||||||
self.current_waste_product = WASTE.PLUTONIUM
|
self.current_waste_product = WASTE.PLUTONIUM
|
||||||
else self.current_waste_product = self.waste_product end
|
end
|
||||||
|
|
||||||
-- make sure dynamic tanks are allowing outflow if required
|
-- make sure dynamic tanks are allowing outflow if required
|
||||||
-- set all, rather than trying to determine which is for which (simpler & safer)
|
-- set all, rather than trying to determine which is for which (simpler & safer)
|
||||||
@@ -798,10 +900,14 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
------------------------
|
------------------------
|
||||||
-- Update Alarm Tones --
|
-- Update Alarm Tones --
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
--#region
|
||||||
|
|
||||||
local allow_test = self.allow_testing and self.test_tone_set
|
local allow_test = self.allow_testing and self.test_tone_set
|
||||||
|
|
||||||
local alarms = { false, false, false, false, false, false, false, false, false, false, false, false }
|
local alarms = { false, false, false, false, false, false, false, false, false, false, false, false }
|
||||||
@@ -888,6 +994,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
self.test_tone_set = false
|
self.test_tone_set = false
|
||||||
self.test_tone_reset = true
|
self.test_tone_reset = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
end
|
end
|
||||||
|
|
||||||
-- call the update function of all units in the facility<br>
|
-- call the update function of all units in the facility<br>
|
||||||
@@ -900,7 +1008,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- COMMANDS --
|
--#endregion
|
||||||
|
|
||||||
|
--#region Commands
|
||||||
|
|
||||||
-- SCRAM all reactor units
|
-- SCRAM all reactor units
|
||||||
function public.scram_all()
|
function public.scram_all()
|
||||||
@@ -922,41 +1032,41 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
function public.auto_stop() self.mode = PROCESS.INACTIVE end
|
||||||
|
|
||||||
-- set automatic control configuration and start the process
|
-- set automatic control configuration and start the process
|
||||||
---@param config coord_auto_config configuration
|
---@param auto_cfg coord_auto_config configuration
|
||||||
---@return table response ready state (successfully started) and current configuration (after updating)
|
---@return table response ready state (successfully started) and current configuration (after updating)
|
||||||
function public.auto_start(config)
|
function public.auto_start(auto_cfg)
|
||||||
local charge_scaler = 1000000 -- convert MFE to FE
|
local charge_scaler = 1000000 -- convert MFE to FE
|
||||||
local gen_scaler = 1000 -- convert kFE to FE
|
local gen_scaler = 1000 -- convert kFE to FE
|
||||||
local ready = false
|
local ready = false
|
||||||
|
|
||||||
-- load up current limits
|
-- load up current limits
|
||||||
local limits = {}
|
local limits = {}
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
local u = self.units[i] ---@type reactor_unit
|
local u = self.units[i] ---@type reactor_unit
|
||||||
limits[i] = u.get_control_inf().lim_br100 * 100
|
limits[i] = u.get_control_inf().lim_br100 * 100
|
||||||
end
|
end
|
||||||
|
|
||||||
-- only allow changes if not running
|
-- only allow changes if not running
|
||||||
if self.mode == PROCESS.INACTIVE then
|
if self.mode == PROCESS.INACTIVE then
|
||||||
if (type(config.mode) == "number") and (config.mode > PROCESS.INACTIVE) and (config.mode <= PROCESS.GEN_RATE) then
|
if (type(auto_cfg.mode) == "number") and (auto_cfg.mode > PROCESS.INACTIVE) and (auto_cfg.mode <= PROCESS.GEN_RATE) then
|
||||||
self.mode_set = config.mode
|
self.mode_set = auto_cfg.mode
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.burn_target) == "number") and config.burn_target >= 0.1 then
|
if (type(auto_cfg.burn_target) == "number") and auto_cfg.burn_target >= 0.1 then
|
||||||
self.burn_target = config.burn_target
|
self.burn_target = auto_cfg.burn_target
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.charge_target) == "number") and config.charge_target >= 0 then
|
if (type(auto_cfg.charge_target) == "number") and auto_cfg.charge_target >= 0 then
|
||||||
self.charge_setpoint = config.charge_target * charge_scaler
|
self.charge_setpoint = auto_cfg.charge_target * charge_scaler
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.gen_target) == "number") and config.gen_target >= 0 then
|
if (type(auto_cfg.gen_target) == "number") and auto_cfg.gen_target >= 0 then
|
||||||
self.gen_rate_setpoint = config.gen_target * gen_scaler
|
self.gen_rate_setpoint = auto_cfg.gen_target * gen_scaler
|
||||||
end
|
end
|
||||||
|
|
||||||
if (type(config.limits) == "table") and (#config.limits == num_reactors) then
|
if (type(auto_cfg.limits) == "table") and (#auto_cfg.limits == config.UnitCount) then
|
||||||
for i = 1, num_reactors do
|
for i = 1, config.UnitCount do
|
||||||
local limit = config.limits[i]
|
local limit = auto_cfg.limits[i]
|
||||||
|
|
||||||
if (type(limit) == "number") and (limit >= 0.1) then
|
if (type(limit) == "number") and (limit >= 0.1) then
|
||||||
limits[i] = limit
|
limits[i] = limit
|
||||||
@@ -988,13 +1098,15 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- SETTINGS --
|
--#endregion
|
||||||
|
|
||||||
|
--#region Settings
|
||||||
|
|
||||||
-- set the automatic control group of a unit
|
-- set the automatic control group of a unit
|
||||||
---@param unit_id integer unit ID
|
---@param unit_id integer unit ID
|
||||||
---@param group integer group ID or 0 for independent
|
---@param group integer group ID or 0 for independent
|
||||||
function public.set_group(unit_id, group)
|
function public.set_group(unit_id, group)
|
||||||
if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= num_reactors) and self.mode == PROCESS.INACTIVE then
|
if (group >= 0 and group <= 4) and (unit_id > 0 and unit_id <= config.UnitCount) and self.mode == PROCESS.INACTIVE then
|
||||||
-- remove from old group if previously assigned
|
-- remove from old group if previously assigned
|
||||||
local old_group = self.group_map[unit_id]
|
local old_group = self.group_map[unit_id]
|
||||||
if old_group ~= 0 then
|
if old_group ~= 0 then
|
||||||
@@ -1029,7 +1141,17 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
return self.pu_fallback
|
return self.pu_fallback
|
||||||
end
|
end
|
||||||
|
|
||||||
-- DIAGNOSTIC TESTING --
|
-- enable/disable SPS at low power
|
||||||
|
---@param enabled boolean requested state
|
||||||
|
---@return boolean enabled newly set value
|
||||||
|
function public.set_sps_low_power(enabled)
|
||||||
|
self.sps_low_power = enabled == true
|
||||||
|
return self.sps_low_power
|
||||||
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
--#region Diagnostic Testing
|
||||||
|
|
||||||
-- attempt to set a test tone state
|
-- attempt to set a test tone state
|
||||||
---@param id TONE|0 tone ID or 0 to disable all
|
---@param id TONE|0 tone ID or 0 to disable all
|
||||||
@@ -1069,7 +1191,9 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
return self.allow_testing, self.test_alarm_states
|
return self.allow_testing, self.test_alarm_states
|
||||||
end
|
end
|
||||||
|
|
||||||
-- READ STATES/PROPERTIES --
|
--#endregion
|
||||||
|
|
||||||
|
--#region Read States/Properties
|
||||||
|
|
||||||
-- get current alarm tone on/off states
|
-- get current alarm tone on/off states
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -1129,7 +1253,8 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
self.status_text[2],
|
self.status_text[2],
|
||||||
self.group_map,
|
self.group_map,
|
||||||
self.current_waste_product,
|
self.current_waste_product,
|
||||||
(self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM)
|
self.pu_fallback and (self.current_waste_product == WASTE.PLUTONIUM) and (self.waste_product ~= WASTE.PLUTONIUM),
|
||||||
|
self.disabled_sps
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1145,15 +1270,21 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
status.power = {
|
status.power = {
|
||||||
self.avg_charge.compute(),
|
self.avg_charge.compute(),
|
||||||
self.avg_inflow.compute(),
|
self.avg_inflow.compute(),
|
||||||
self.avg_outflow.compute()
|
self.avg_outflow.compute(),
|
||||||
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
-- status of induction matricies (including tanks)
|
-- status of induction matricies (including tanks)
|
||||||
status.induction = {}
|
status.induction = {}
|
||||||
for i = 1, #self.induction do
|
for i = 1, #self.induction do
|
||||||
local matrix = self.induction[i] ---@type unit_session
|
local matrix = self.induction[i] ---@type unit_session
|
||||||
local db = matrix.get_db() ---@type imatrix_session_db
|
local db = matrix.get_db() ---@type imatrix_session_db
|
||||||
|
|
||||||
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
status.induction[i] = { matrix.is_faulted(), db.formed, db.state, db.tanks }
|
||||||
|
|
||||||
|
local fe_per_ms = self.avg_net.compute()
|
||||||
|
local remaining = util.joules_to_fe(util.trinary(fe_per_ms >= 0, db.tanks.energy_need, db.tanks.energy))
|
||||||
|
status.power[4] = remaining / fe_per_ms
|
||||||
end
|
end
|
||||||
|
|
||||||
-- status of sps
|
-- status of sps
|
||||||
@@ -1183,6 +1314,12 @@ function facility.new(num_reactors, cooling_conf)
|
|||||||
return status
|
return status
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
|
-- supervisor sessions reporting the list of active RTU sessions
|
||||||
|
---@param rtu_sessions table session list of all connected RTUs
|
||||||
|
function public.report_rtus(rtu_sessions) self.rtu_conn_count = #rtu_sessions end
|
||||||
|
|
||||||
-- get the units in this facility
|
-- get the units in this facility
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.get_units() return self.units end
|
function public.get_units() return self.units end
|
||||||
|
|||||||
@@ -17,31 +17,32 @@ local ALIGN = core.ALIGN
|
|||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local black_lg = style.black_lg
|
|
||||||
local lg_white = style.lg_white
|
|
||||||
|
|
||||||
-- create a pocket diagnostics list entry
|
-- create a pocket diagnostics list entry
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param id integer PDG session ID
|
---@param id integer PDG session ID
|
||||||
local function init(parent, id)
|
local function init(parent, id)
|
||||||
|
local s_hi_box = style.theme.highlight_box
|
||||||
|
|
||||||
|
local label_fg = style.fp.label_fg
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
||||||
|
|
||||||
local ps_prefix = "pdg_" .. id .. "_"
|
local ps_prefix = "pdg_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=black_lg,nav_active=cpair(colors.gray,colors.black)}
|
local pdg_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value)
|
pdg_addr.register(databus.ps, ps_prefix .. "addr", pdg_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=10,y=2,text="FW:",width=3,height=1}
|
||||||
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=lg_white}
|
local pdg_fw_v = TextBox{parent=entry,x=14,y=2,text=" ------- ",width=20,height=1,fg_bg=label_fg}
|
||||||
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
pdg_fw_v.register(databus.ps, ps_prefix .. "fw", pdg_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=35,y=2,text="RTT:",width=4,height=1}
|
||||||
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
|
local pdg_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt", pdg_rtt.update)
|
||||||
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
pdg_rtt.register(databus.ps, ps_prefix .. "rtt_color", pdg_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -17,35 +17,36 @@ local ALIGN = core.ALIGN
|
|||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local black_lg = style.black_lg
|
|
||||||
local lg_white = style.lg_white
|
|
||||||
|
|
||||||
-- create an RTU list entry
|
-- create an RTU list entry
|
||||||
---@param parent graphics_element parent
|
---@param parent graphics_element parent
|
||||||
---@param id integer RTU session ID
|
---@param id integer RTU session ID
|
||||||
local function init(parent, id)
|
local function init(parent, id)
|
||||||
|
local s_hi_box = style.theme.highlight_box
|
||||||
|
|
||||||
|
local label_fg = style.fp.label_fg
|
||||||
|
|
||||||
-- root div
|
-- root div
|
||||||
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
local root = Div{parent=parent,x=2,y=2,height=4,width=parent.get_width()-2,hidden=true}
|
||||||
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.bw_fg_bg}
|
local entry = Div{parent=root,x=2,y=1,height=3,fg_bg=style.theme.highlight_box_bright}
|
||||||
|
|
||||||
local ps_prefix = "rtu_" .. id .. "_"
|
local ps_prefix = "rtu_" .. id .. "_"
|
||||||
|
|
||||||
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=black_lg,nav_active=cpair(colors.gray,colors.black)}
|
local rtu_addr = TextBox{parent=entry,x=1,y=2,text="@ C ??",alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box,nav_active=cpair(colors.gray,colors.black)}
|
||||||
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value)
|
rtu_addr.register(databus.ps, ps_prefix .. "addr", rtu_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1}
|
TextBox{parent=entry,x=10,y=2,text="UNITS:",width=7,height=1}
|
||||||
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.gray_white}
|
local unit_count = DataIndicator{parent=entry,x=17,y=2,label="",unit="",format="%2d",value=0,width=2,fg_bg=style.fp.label_d_fg}
|
||||||
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
unit_count.register(databus.ps, ps_prefix .. "units", unit_count.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=21,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=entry,x=21,y=2,text="FW:",width=3,height=1}
|
||||||
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
|
local rtu_fw_v = TextBox{parent=entry,x=25,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||||
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
rtu_fw_v.register(databus.ps, ps_prefix .. "fw", rtu_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=entry,x=36,y=2,text="RTT:",width=4,height=1}
|
||||||
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
|
local rtu_rtt = DataIndicator{parent=entry,x=40,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
|
TextBox{parent=entry,x=46,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt", rtu_rtt.update)
|
||||||
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
rtu_rtt.register(databus.ps, ps_prefix .. "rtt_color", rtu_rtt.recolor)
|
||||||
|
|
||||||
|
|||||||
@@ -29,18 +29,18 @@ local ALIGN = core.ALIGN
|
|||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local bw_fg_bg = style.bw_fg_bg
|
|
||||||
|
|
||||||
local black_lg = style.black_lg
|
|
||||||
local lg_white = style.lg_white
|
|
||||||
local gry_wht = style.gray_white
|
|
||||||
|
|
||||||
local ind_grn = style.ind_grn
|
local ind_grn = style.ind_grn
|
||||||
|
|
||||||
-- create new front panel view
|
-- create new front panel view
|
||||||
---@param panel graphics_element main displaybox
|
---@param panel graphics_element main displaybox
|
||||||
local function init(panel)
|
local function init(panel)
|
||||||
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.header}
|
local s_hi_box = style.theme.highlight_box
|
||||||
|
local s_hi_bright = style.theme.highlight_box_bright
|
||||||
|
|
||||||
|
local label_fg = style.fp.label_fg
|
||||||
|
local label_d_fg = style.fp.label_d_fg
|
||||||
|
|
||||||
|
TextBox{parent=panel,y=1,text="SCADA SUPERVISOR",alignment=ALIGN.CENTER,height=1,fg_bg=style.theme.header}
|
||||||
|
|
||||||
local page_div = Div{parent=panel,x=1,y=3}
|
local page_div = Div{parent=panel,x=1,y=3}
|
||||||
|
|
||||||
@@ -66,13 +66,13 @@ local function init(panel)
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
local comp_id = util.sprintf("(%d)", os.getComputerID())
|
||||||
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp_label}
|
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=style.fp.disabled_fg}
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about footer
|
-- about footer
|
||||||
--
|
--
|
||||||
|
|
||||||
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp_label}
|
local about = Div{parent=main_page,width=15,height=3,x=1,y=16,fg_bg=style.fp.disabled_fg}
|
||||||
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=ALIGN.LEFT,height=1}
|
||||||
|
|
||||||
@@ -90,25 +90,25 @@ local function init(panel)
|
|||||||
|
|
||||||
for i = 1, supervisor.config.UnitCount do
|
for i = 1, supervisor.config.UnitCount do
|
||||||
local ps_prefix = "plc_" .. i .. "_"
|
local ps_prefix = "plc_" .. i .. "_"
|
||||||
local plc_entry = Div{parent=plc_list,height=3,fg_bg=bw_fg_bg}
|
local plc_entry = Div{parent=plc_list,height=3,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=1,y=1,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=plc_entry,x=1,y=1,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=ALIGN.CENTER,width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=plc_entry,x=1,y=2,text="UNIT "..i,alignment=ALIGN.CENTER,width=8,height=1,fg_bg=s_hi_box}
|
||||||
TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=black_lg}
|
TextBox{parent=plc_entry,x=1,y=3,text="",width=8,height=1,fg_bg=s_hi_box}
|
||||||
|
|
||||||
local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=ind_grn}
|
local conn = LED{parent=plc_entry,x=10,y=2,label="LINK",colors=cpair(colors.green_hc,colors.green_off)}
|
||||||
conn.register(databus.ps, ps_prefix .. "conn", conn.update)
|
conn.register(databus.ps, ps_prefix .. "conn", conn.update)
|
||||||
|
|
||||||
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=gry_wht}
|
local plc_addr = TextBox{parent=plc_entry,x=17,y=2,text=" --- ",width=5,height=1,fg_bg=label_d_fg}
|
||||||
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
plc_addr.register(databus.ps, ps_prefix .. "addr", plc_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=plc_entry,x=23,y=2,text="FW:",width=3,height=1}
|
||||||
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
|
local plc_fw_v = TextBox{parent=plc_entry,x=27,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||||
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
plc_fw_v.register(databus.ps, ps_prefix .. "fw", plc_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=plc_entry,x=37,y=2,text="RTT:",width=4,height=1}
|
||||||
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=lg_white}
|
local plc_rtt = DataIndicator{parent=plc_entry,x=42,y=2,label="",unit="",format="%4d",value=0,width=4,fg_bg=label_fg}
|
||||||
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
|
TextBox{parent=plc_entry,x=47,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt", plc_rtt.update)
|
||||||
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
plc_rtt.register(databus.ps, ps_prefix .. "rtt_color", plc_rtt.recolor)
|
||||||
|
|
||||||
@@ -124,29 +124,29 @@ local function init(panel)
|
|||||||
-- coordinator page
|
-- coordinator page
|
||||||
|
|
||||||
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local crd_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=bw_fg_bg}
|
local crd_box = Div{parent=crd_page,x=2,y=2,width=49,height=4,fg_bg=s_hi_bright}
|
||||||
|
|
||||||
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=ind_grn}
|
local crd_conn = LED{parent=crd_box,x=2,y=2,label="CONNECTION",colors=cpair(colors.green_hc,colors.green_off)}
|
||||||
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
crd_conn.register(databus.ps, "crd_conn", crd_conn.update)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=gry_wht}
|
TextBox{parent=crd_box,x=4,y=3,text="COMPUTER",width=8,height=1,fg_bg=label_d_fg}
|
||||||
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=gry_wht}
|
local crd_addr = TextBox{parent=crd_box,x=13,y=3,text="---",width=5,height=1,fg_bg=label_d_fg}
|
||||||
crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value)
|
crd_addr.register(databus.ps, "crd_addr", crd_addr.set_value)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1}
|
TextBox{parent=crd_box,x=22,y=2,text="FW:",width=3,height=1}
|
||||||
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=lg_white}
|
local crd_fw_v = TextBox{parent=crd_box,x=26,y=2,text=" ------- ",width=9,height=1,fg_bg=label_fg}
|
||||||
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
crd_fw_v.register(databus.ps, "crd_fw", crd_fw_v.set_value)
|
||||||
|
|
||||||
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4,height=1}
|
TextBox{parent=crd_box,x=36,y=2,text="RTT:",width=4,height=1}
|
||||||
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=lg_white}
|
local crd_rtt = DataIndicator{parent=crd_box,x=41,y=2,label="",unit="",format="%5d",value=0,width=5,fg_bg=label_fg}
|
||||||
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,height=1,fg_bg=lg_white}
|
TextBox{parent=crd_box,x=47,y=2,text="ms",width=4,height=1,fg_bg=label_fg}
|
||||||
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
crd_rtt.register(databus.ps, "crd_rtt", crd_rtt.update)
|
||||||
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
crd_rtt.register(databus.ps, "crd_rtt_color", crd_rtt.recolor)
|
||||||
|
|
||||||
-- pocket diagnostics page
|
-- pocket diagnostics page
|
||||||
|
|
||||||
local pkt_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
local pkt_page = Div{parent=page_div,x=1,y=1,hidden=true}
|
||||||
local pdg_list = ListBox{parent=pkt_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=cpair(colors.black,colors.ivory),nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
local pdg_list = ListBox{parent=pkt_page,x=1,y=1,height=17,width=51,scroll_height=1000,fg_bg=style.fp.text_fg,nav_fg_bg=cpair(colors.gray,colors.lightGray),nav_active=cpair(colors.black,colors.gray)}
|
||||||
local _ = Div{parent=pdg_list,height=1,hidden=true} -- padding
|
local _ = Div{parent=pdg_list,height=1,hidden=true} -- padding
|
||||||
|
|
||||||
-- assemble page panes
|
-- assemble page panes
|
||||||
@@ -156,14 +156,14 @@ local function init(panel)
|
|||||||
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
local page_pane = MultiPane{parent=page_div,x=1,y=1,panes=panes}
|
||||||
|
|
||||||
local tabs = {
|
local tabs = {
|
||||||
{ name = "SVR", color = cpair(colors.black, colors.ivory) },
|
{ name = "SVR", color = style.fp.text },
|
||||||
{ name = "PLC", color = cpair(colors.black, colors.ivory) },
|
{ name = "PLC", color = style.fp.text },
|
||||||
{ name = "RTU", color = cpair(colors.black, colors.ivory) },
|
{ name = "RTU", color = style.fp.text },
|
||||||
{ name = "CRD", color = cpair(colors.black, colors.ivory) },
|
{ name = "CRD", color = style.fp.text },
|
||||||
{ name = "PKT", color = cpair(colors.black, colors.ivory) },
|
{ name = "PKT", color = style.fp.text },
|
||||||
}
|
}
|
||||||
|
|
||||||
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=bw_fg_bg}
|
TabBar{parent=panel,y=2,tabs=tabs,min_width=9,callback=page_pane.set_value,fg_bg=style.theme.highlight_box_bright}
|
||||||
|
|
||||||
-- link RTU/PDG list management to PGI
|
-- link RTU/PDG list management to PGI
|
||||||
pgi.link_elements(rtu_list, rtu_entry, pdg_list, pdg_entry)
|
pgi.link_elements(rtu_list, rtu_entry, pdg_list, pdg_entry)
|
||||||
|
|||||||
@@ -2,53 +2,33 @@
|
|||||||
-- Graphics Style Options
|
-- Graphics Style Options
|
||||||
--
|
--
|
||||||
|
|
||||||
local core = require("graphics.core")
|
local core = require("graphics.core")
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
|
---@class svr_style
|
||||||
local style = {}
|
local style = {}
|
||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
-- GLOBAL --
|
style.theme = themes.sandstone
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
-- remap global colors
|
style.colorblind = false
|
||||||
colors.ivory = colors.pink
|
|
||||||
colors.yellow_hc = colors.purple
|
|
||||||
colors.red_off = colors.brown
|
|
||||||
colors.yellow_off = colors.magenta
|
|
||||||
colors.green_off = colors.lime
|
|
||||||
|
|
||||||
style.root = cpair(colors.black, colors.ivory)
|
|
||||||
style.header = cpair(colors.black, colors.lightGray)
|
|
||||||
|
|
||||||
style.colors = {
|
|
||||||
{ c = colors.red, hex = 0xdf4949 }, -- RED ON
|
|
||||||
{ c = colors.orange, hex = 0xffb659 },
|
|
||||||
{ c = colors.yellow, hex = 0xf9fb53 }, -- YELLOW ON
|
|
||||||
{ c = colors.lime, hex = 0x16665a }, -- GREEN OFF
|
|
||||||
{ c = colors.green, hex = 0x6be551 }, -- GREEN ON
|
|
||||||
{ c = colors.cyan, hex = 0x34bac8 },
|
|
||||||
{ c = colors.lightBlue, hex = 0x6cc0f2 },
|
|
||||||
{ c = colors.blue, hex = 0x0008fe }, -- LCD BLUE
|
|
||||||
{ c = colors.purple, hex = 0xe3bc2a }, -- YELLOW HIGH CONTRAST
|
|
||||||
{ c = colors.pink, hex = 0xdcd9ca }, -- IVORY
|
|
||||||
{ c = colors.magenta, hex = 0x85862c }, -- YELLOW OFF
|
|
||||||
-- { c = colors.white, hex = 0xdcd9ca },
|
|
||||||
{ c = colors.lightGray, hex = 0xb1b8b3 },
|
|
||||||
{ c = colors.gray, hex = 0x575757 },
|
|
||||||
-- { c = colors.black, hex = 0x191919 },
|
|
||||||
{ c = colors.brown, hex = 0x672223 } -- RED OFF
|
|
||||||
}
|
|
||||||
|
|
||||||
-- COMMON COLOR PAIRS --
|
|
||||||
|
|
||||||
style.text_fg_bg = cpair(colors.black, colors.ivory)
|
|
||||||
style.bw_fg_bg = cpair(colors.black, colors.white)
|
|
||||||
style.fp_label = cpair(colors.lightGray, colors.ivory)
|
|
||||||
|
|
||||||
style.black_lg = cpair(colors.black, colors.lightGray)
|
|
||||||
style.lg_white = cpair(colors.lightGray, colors.white)
|
|
||||||
style.gray_white = cpair(colors.gray, colors.white)
|
|
||||||
|
|
||||||
style.ind_grn = cpair(colors.green, colors.green_off)
|
style.ind_grn = cpair(colors.green, colors.green_off)
|
||||||
|
|
||||||
|
-- set theme per configuration
|
||||||
|
---@param fp FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE the color mode to use
|
||||||
|
function style.set_theme(fp, color_mode)
|
||||||
|
if fp == themes.FP_THEME.SANDSTONE then
|
||||||
|
style.theme = themes.sandstone
|
||||||
|
elseif fp == themes.FP_THEME.BASALT then
|
||||||
|
style.theme = themes.basalt
|
||||||
|
end
|
||||||
|
|
||||||
|
style.fp = themes.get_fp_style(style.theme)
|
||||||
|
|
||||||
|
style.colorblind = color_mode ~= themes.COLOR_MODE.STANDARD and color_mode ~= themes.COLOR_MODE.STD_ON_BLACK
|
||||||
|
end
|
||||||
|
|
||||||
return style
|
return style
|
||||||
|
|||||||
@@ -19,11 +19,16 @@ local ui = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
-- try to start the UI
|
-- try to start the UI
|
||||||
|
---@param theme FP_THEME front panel theme
|
||||||
|
---@param color_mode COLOR_MODE color mode
|
||||||
---@return boolean success, any error_msg
|
---@return boolean success, any error_msg
|
||||||
function renderer.try_start_ui()
|
function renderer.try_start_ui(theme, color_mode)
|
||||||
local status, msg = true, nil
|
local status, msg = true, nil
|
||||||
|
|
||||||
if ui.display == nil then
|
if ui.display == nil then
|
||||||
|
-- set theme
|
||||||
|
style.set_theme(theme, color_mode)
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
term.setTextColor(colors.white)
|
term.setTextColor(colors.white)
|
||||||
term.setBackgroundColor(colors.black)
|
term.setBackgroundColor(colors.black)
|
||||||
@@ -31,13 +36,19 @@ function renderer.try_start_ui()
|
|||||||
term.setCursorPos(1, 1)
|
term.setCursorPos(1, 1)
|
||||||
|
|
||||||
-- set overridden colors
|
-- set overridden colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
term.setPaletteColor(style.theme.colors[i].c, style.theme.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- apply color mode
|
||||||
|
local c_mode_overrides = style.theme.color_modes[color_mode]
|
||||||
|
for i = 1, #c_mode_overrides do
|
||||||
|
term.setPaletteColor(c_mode_overrides[i].c, c_mode_overrides[i].hex)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- init front panel view
|
-- init front panel view
|
||||||
status, msg = pcall(function ()
|
status, msg = pcall(function ()
|
||||||
ui.display = DisplayBox{window=term.current(),fg_bg=style.root}
|
ui.display = DisplayBox{window=term.current(),fg_bg=style.fp.root}
|
||||||
panel_view(ui.display)
|
panel_view(ui.display)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -70,9 +81,9 @@ function renderer.close_ui()
|
|||||||
ui.display = nil
|
ui.display = nil
|
||||||
|
|
||||||
-- restore colors
|
-- restore colors
|
||||||
for i = 1, #style.colors do
|
for i = 1, #style.theme.colors do
|
||||||
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
local r, g, b = term.nativePaletteColor(style.theme.colors[i].c)
|
||||||
term.setPaletteColor(style.colors[i].c, r, g, b)
|
term.setPaletteColor(style.theme.colors[i].c, r, g, b)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- reset terminal
|
-- reset terminal
|
||||||
|
|||||||
@@ -17,9 +17,6 @@ local FAC_COMMAND = comms.FAC_COMMAND
|
|||||||
|
|
||||||
local SV_Q_DATA = svqtypes.SV_Q_DATA
|
local SV_Q_DATA = svqtypes.SV_Q_DATA
|
||||||
|
|
||||||
-- grace period in seconds for coordinator to finish UI draw to prevent timeout
|
|
||||||
local WATCHDOG_GRACE = 20.0
|
|
||||||
|
|
||||||
-- retry time constants in ms
|
-- retry time constants in ms
|
||||||
-- local INITIAL_WAIT = 1500
|
-- local INITIAL_WAIT = 1500
|
||||||
local RETRY_PERIOD = 1000
|
local RETRY_PERIOD = 1000
|
||||||
@@ -273,6 +270,12 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
else
|
else
|
||||||
log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
|
log.debug(log_header .. "CRDN set pu fallback packet length mismatch")
|
||||||
end
|
end
|
||||||
|
elseif cmd == FAC_COMMAND.SET_SPS_LP then
|
||||||
|
if pkt.length == 2 then
|
||||||
|
_send(CRDN_TYPE.FAC_CMD, { cmd, facility.set_sps_low_power(pkt.data[2]) })
|
||||||
|
else
|
||||||
|
log.debug(log_header .. "CRDN set sps low power packet length mismatch")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
log.debug(log_header .. "CRDN facility command unknown")
|
log.debug(log_header .. "CRDN facility command unknown")
|
||||||
end
|
end
|
||||||
@@ -360,15 +363,7 @@ function coordinator.new_session(id, s_addr, in_queue, out_queue, timeout, facil
|
|||||||
-- check if a timer matches this session's watchdog
|
-- check if a timer matches this session's watchdog
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function public.check_wd(timer)
|
function public.check_wd(timer)
|
||||||
local is_wd = self.conn_watchdog.is_timer(timer) and self.connected
|
return self.conn_watchdog.is_timer(timer) and self.connected
|
||||||
|
|
||||||
-- if we are waiting for initial coordinator UI draw, don't close yet
|
|
||||||
if is_wd and (util.time_s() - self.establish_time) <= WATCHDOG_GRACE then
|
|
||||||
self.conn_watchdog.feed()
|
|
||||||
is_wd = false
|
|
||||||
end
|
|
||||||
|
|
||||||
return is_wd
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- close the connection
|
-- close the connection
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
local comms = require("scada-common.comms")
|
local comms = require("scada-common.comms")
|
||||||
|
local const = require("scada-common.constants")
|
||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local mqueue = require("scada-common.mqueue")
|
local mqueue = require("scada-common.mqueue")
|
||||||
local types = require("scada-common.types")
|
local types = require("scada-common.types")
|
||||||
@@ -105,6 +106,8 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
formed = false,
|
formed = false,
|
||||||
rps_tripped = false,
|
rps_tripped = false,
|
||||||
rps_trip_cause = "ok", ---@type rps_trip_cause
|
rps_trip_cause = "ok", ---@type rps_trip_cause
|
||||||
|
max_op_temp_H2O = 1200,
|
||||||
|
max_op_temp_Na = 1200,
|
||||||
---@class rps_status
|
---@class rps_status
|
||||||
rps_status = {
|
rps_status = {
|
||||||
high_dmg = false,
|
high_dmg = false,
|
||||||
@@ -138,11 +141,11 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
waste = 0,
|
waste = 0,
|
||||||
waste_need = 0,
|
waste_need = 0,
|
||||||
waste_fill = 0.0,
|
waste_fill = 0.0,
|
||||||
ccool_type = "?",
|
ccool_type = types.FLUID.EMPTY_GAS, ---@type fluid
|
||||||
ccool_amnt = 0,
|
ccool_amnt = 0,
|
||||||
ccool_need = 0,
|
ccool_need = 0,
|
||||||
ccool_fill = 0.0,
|
ccool_fill = 0.0,
|
||||||
hcool_type = "?",
|
hcool_type = types.FLUID.EMPTY_GAS, ---@type fluid
|
||||||
hcool_amnt = 0,
|
hcool_amnt = 0,
|
||||||
hcool_need = 0,
|
hcool_need = 0,
|
||||||
hcool_fill = 0.0
|
hcool_fill = 0.0
|
||||||
@@ -169,6 +172,21 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
---@class plc_session
|
---@class plc_session
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
|
-- compute maximum expected operational temperatures for high temp warnings
|
||||||
|
local function _compute_op_temps()
|
||||||
|
local JOULES_PER_MB = const.mek.JOULES_PER_MB
|
||||||
|
local BASE_BOIL_TEMP = const.mek.BASE_BOIL_TEMP
|
||||||
|
|
||||||
|
local heat_cap = self.sDB.mek_struct.heat_cap
|
||||||
|
local max_burn = self.sDB.mek_struct.max_burn
|
||||||
|
|
||||||
|
self.sDB.max_op_temp_H2O = max_burn * 2 * (JOULES_PER_MB * heat_cap ^ -1) + BASE_BOIL_TEMP
|
||||||
|
self.sDB.max_op_temp_Na = max_burn * (JOULES_PER_MB * heat_cap ^ -1) + BASE_BOIL_TEMP
|
||||||
|
|
||||||
|
log.info(util.sprintf(log_header .. "computed maximum operational temperatures %.3fK (H2O) and %.3fK (Na)",
|
||||||
|
self.sDB.max_op_temp_H2O, self.sDB.max_op_temp_Na))
|
||||||
|
end
|
||||||
|
|
||||||
-- copy in the RPS status
|
-- copy in the RPS status
|
||||||
---@param rps_status table
|
---@param rps_status table
|
||||||
local function _copy_rps_status(rps_status)
|
local function _copy_rps_status(rps_status)
|
||||||
@@ -351,6 +369,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
local status = pcall(_copy_struct, pkt.data)
|
local status = pcall(_copy_struct, pkt.data)
|
||||||
if status then
|
if status then
|
||||||
-- copied in structure data OK
|
-- copied in structure data OK
|
||||||
|
_compute_op_temps()
|
||||||
self.received_struct = true
|
self.received_struct = true
|
||||||
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
out_queue.push_data(svqtypes.SV_Q_DATA.PLC_BUILD_CHANGED, reactor_id)
|
||||||
else
|
else
|
||||||
@@ -639,6 +658,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
local cmd = message.message
|
local cmd = message.message
|
||||||
if cmd == PLC_S_CMDS.ENABLE then
|
if cmd == PLC_S_CMDS.ENABLE then
|
||||||
-- enable reactor
|
-- enable reactor
|
||||||
|
self.acks.disable = true
|
||||||
if not self.auto_lock then
|
if not self.auto_lock then
|
||||||
_send(RPLC_TYPE.RPS_ENABLE, {})
|
_send(RPLC_TYPE.RPS_ENABLE, {})
|
||||||
end
|
end
|
||||||
@@ -695,6 +715,7 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
self.auto_cmd_token = 0
|
self.auto_cmd_token = 0
|
||||||
self.ramping_rate = true
|
self.ramping_rate = true
|
||||||
self.acks.burn_rate = false
|
self.acks.burn_rate = false
|
||||||
|
self.acks.disable = true
|
||||||
self.retry_times.burn_rate_req = util.time() + INITIAL_WAIT
|
self.retry_times.burn_rate_req = util.time() + INITIAL_WAIT
|
||||||
_send(RPLC_TYPE.MEK_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate })
|
_send(RPLC_TYPE.MEK_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate })
|
||||||
end
|
end
|
||||||
@@ -702,13 +723,14 @@ function plc.new_session(id, s_addr, reactor_id, in_queue, out_queue, timeout, f
|
|||||||
elseif cmd.key == PLC_S_DATA.AUTO_BURN_RATE then
|
elseif cmd.key == PLC_S_DATA.AUTO_BURN_RATE then
|
||||||
-- set automatic burn rate
|
-- set automatic burn rate
|
||||||
if self.auto_lock then
|
if self.auto_lock then
|
||||||
cmd.val = math.floor(cmd.val * 100) / 100 -- round to 100ths place
|
cmd.val = math.floor(cmd.val * 100) / 100 -- round to 100ths place
|
||||||
if cmd.val >= 0 and cmd.val <= self.sDB.mek_struct.max_burn then
|
if cmd.val >= 0 and cmd.val <= self.sDB.mek_struct.max_burn then
|
||||||
self.auto_cmd_token = util.time_ms()
|
self.auto_cmd_token = util.time_ms()
|
||||||
self.commanded_burn_rate = cmd.val
|
self.commanded_burn_rate = cmd.val
|
||||||
|
|
||||||
-- this is only for manual control, only retry auto ramps
|
-- this is only for manual control, only retry auto ramps
|
||||||
self.acks.burn_rate = not self.ramping_rate
|
self.acks.burn_rate = not self.ramping_rate
|
||||||
|
self.acks.disable = true
|
||||||
self.retry_times.burn_rate_req = util.time() + INITIAL_AUTO_WAIT
|
self.retry_times.burn_rate_req = util.time() + INITIAL_AUTO_WAIT
|
||||||
|
|
||||||
_send(RPLC_TYPE.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate, self.auto_cmd_token })
|
_send(RPLC_TYPE.AUTO_BURN_RATE, { self.commanded_burn_rate, self.ramping_rate, self.auto_cmd_token })
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
-- Redstone RTU Session I/O Controller
|
-- Redstone RTU Session I/O Controller
|
||||||
--
|
--
|
||||||
|
|
||||||
|
local rsio = require("scada-common.rsio")
|
||||||
|
|
||||||
local rsctl = {}
|
local rsctl = {}
|
||||||
|
|
||||||
-- create a new redstone RTU I/O controller
|
-- create a new redstone RTU I/O controller
|
||||||
@@ -16,7 +18,7 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@return boolean
|
---@return boolean
|
||||||
function public.is_connected(port)
|
function public.is_connected(port)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
if db.io[port] ~= nil then return true end
|
if db.io[port] ~= nil then return true end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -28,8 +30,8 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@param value boolean
|
---@param value boolean
|
||||||
function public.digital_write(port, value)
|
function public.digital_write(port, value)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
local io = db.io[port] ---@type rs_db_dig_io|nil
|
local io = db.io[port] ---@type rs_db_dig_io|nil
|
||||||
if io ~= nil then io.write(value) end
|
if io ~= nil then io.write(value) end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -40,12 +42,25 @@ function rsctl.new(redstone_rtus)
|
|||||||
---@return boolean|nil
|
---@return boolean|nil
|
||||||
function public.digital_read(port)
|
function public.digital_read(port)
|
||||||
for i = 1, #redstone_rtus do
|
for i = 1, #redstone_rtus do
|
||||||
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
local io = db.io[port] ---@type rs_db_dig_io|nil
|
local io = db.io[port] ---@type rs_db_dig_io|nil
|
||||||
if io ~= nil then return io.read() end
|
if io ~= nil then return io.read() end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- write to an analog redstone port (applies to all RTUs)
|
||||||
|
---@param port IO_PORT
|
||||||
|
---@param value number value
|
||||||
|
---@param min number minimum value for scaling 0 to 15
|
||||||
|
---@param max number maximum value for scaling 0 to 15
|
||||||
|
function public.analog_write(port, value, min, max)
|
||||||
|
for i = 1, #redstone_rtus do
|
||||||
|
local db = redstone_rtus[i].get_db() ---@type redstone_session_db
|
||||||
|
local io = db.io[port] ---@type rs_db_ana_io|nil
|
||||||
|
if io ~= nil then io.write(rsio.analog_write(value, min, max)) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
return public
|
return public
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ function svsessions.init(nic, fp_ok, config, cooling_conf)
|
|||||||
self.nic = nic
|
self.nic = nic
|
||||||
self.fp_ok = fp_ok
|
self.fp_ok = fp_ok
|
||||||
self.config = config
|
self.config = config
|
||||||
self.facility = facility.new(config.UnitCount, cooling_conf)
|
self.facility = facility.new(config, cooling_conf)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- find an RTU session by the computer ID
|
-- find an RTU session by the computer ID
|
||||||
|
|||||||
@@ -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.2.8"
|
local SUPERVISOR_VERSION = "v1.3.11"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@@ -34,9 +34,13 @@ if not supervisor.load_config() then
|
|||||||
-- try to reconfigure (user action)
|
-- try to reconfigure (user action)
|
||||||
local success, error = configure.configure(true)
|
local success, error = configure.configure(true)
|
||||||
if success then
|
if success then
|
||||||
assert(supervisor.load_config(), "failed to load valid configuration")
|
if not supervisor.load_config() then
|
||||||
|
println("failed to load a valid configuration, please reconfigure")
|
||||||
|
return
|
||||||
|
end
|
||||||
else
|
else
|
||||||
assert(success, "supervisor configuration error: " .. error)
|
println("configuration error: " .. error)
|
||||||
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -82,6 +86,7 @@ log.info("========================================")
|
|||||||
println(">> SCADA Supervisor " .. SUPERVISOR_VERSION .. " <<")
|
println(">> SCADA Supervisor " .. SUPERVISOR_VERSION .. " <<")
|
||||||
|
|
||||||
crash.set_env("supervisor", SUPERVISOR_VERSION)
|
crash.set_env("supervisor", SUPERVISOR_VERSION)
|
||||||
|
crash.dbg_log_env()
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- main application
|
-- main application
|
||||||
@@ -114,7 +119,7 @@ local function main()
|
|||||||
databus.tx_hw_modem(true)
|
databus.tx_hw_modem(true)
|
||||||
|
|
||||||
-- start UI
|
-- start UI
|
||||||
local fp_ok, message = renderer.try_start_ui()
|
local fp_ok, message = renderer.try_start_ui(config.FrontPanelTheme, config.ColorMode)
|
||||||
|
|
||||||
if not fp_ok then
|
if not fp_ok then
|
||||||
println_ts(util.c("UI error: ", message))
|
println_ts(util.c("UI error: ", message))
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ local comms = require("scada-common.comms")
|
|||||||
local log = require("scada-common.log")
|
local log = require("scada-common.log")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local themes = require("graphics.themes")
|
||||||
|
|
||||||
local svsessions = require("supervisor.session.svsessions")
|
local svsessions = require("supervisor.session.svsessions")
|
||||||
|
|
||||||
local supervisor = {}
|
local supervisor = {}
|
||||||
@@ -24,6 +26,7 @@ function supervisor.load_config()
|
|||||||
config.CoolingConfig = settings.get("CoolingConfig")
|
config.CoolingConfig = settings.get("CoolingConfig")
|
||||||
config.FacilityTankMode = settings.get("FacilityTankMode")
|
config.FacilityTankMode = settings.get("FacilityTankMode")
|
||||||
config.FacilityTankDefs = settings.get("FacilityTankDefs")
|
config.FacilityTankDefs = settings.get("FacilityTankDefs")
|
||||||
|
config.ExtChargeIdling = settings.get("ExtChargeIdling")
|
||||||
|
|
||||||
config.SVR_Channel = settings.get("SVR_Channel")
|
config.SVR_Channel = settings.get("SVR_Channel")
|
||||||
config.PLC_Channel = settings.get("PLC_Channel")
|
config.PLC_Channel = settings.get("PLC_Channel")
|
||||||
@@ -43,6 +46,9 @@ function supervisor.load_config()
|
|||||||
config.LogPath = settings.get("LogPath")
|
config.LogPath = settings.get("LogPath")
|
||||||
config.LogDebug = settings.get("LogDebug")
|
config.LogDebug = settings.get("LogDebug")
|
||||||
|
|
||||||
|
config.FrontPanelTheme = settings.get("FrontPanelTheme")
|
||||||
|
config.ColorMode = settings.get("ColorMode")
|
||||||
|
|
||||||
local cfv = util.new_validator()
|
local cfv = util.new_validator()
|
||||||
|
|
||||||
cfv.assert_type_int(config.UnitCount)
|
cfv.assert_type_int(config.UnitCount)
|
||||||
@@ -53,6 +59,8 @@ function supervisor.load_config()
|
|||||||
cfv.assert_type_int(config.FacilityTankMode)
|
cfv.assert_type_int(config.FacilityTankMode)
|
||||||
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
cfv.assert_range(config.FacilityTankMode, 0, 8)
|
||||||
|
|
||||||
|
cfv.assert_type_bool(config.ExtChargeIdling)
|
||||||
|
|
||||||
cfv.assert_channel(config.SVR_Channel)
|
cfv.assert_channel(config.SVR_Channel)
|
||||||
cfv.assert_channel(config.PLC_Channel)
|
cfv.assert_channel(config.PLC_Channel)
|
||||||
cfv.assert_channel(config.RTU_Channel)
|
cfv.assert_channel(config.RTU_Channel)
|
||||||
@@ -73,7 +81,7 @@ function supervisor.load_config()
|
|||||||
|
|
||||||
if type(config.AuthKey) == "string" then
|
if type(config.AuthKey) == "string" then
|
||||||
local len = string.len(config.AuthKey)
|
local len = string.len(config.AuthKey)
|
||||||
cfv.assert_eq(len == 0 or len >= 8, true)
|
cfv.assert(len == 0 or len >= 8)
|
||||||
end
|
end
|
||||||
|
|
||||||
cfv.assert_type_int(config.LogMode)
|
cfv.assert_type_int(config.LogMode)
|
||||||
@@ -81,6 +89,11 @@ function supervisor.load_config()
|
|||||||
cfv.assert_type_str(config.LogPath)
|
cfv.assert_type_str(config.LogPath)
|
||||||
cfv.assert_type_bool(config.LogDebug)
|
cfv.assert_type_bool(config.LogDebug)
|
||||||
|
|
||||||
|
cfv.assert_type_int(config.FrontPanelTheme)
|
||||||
|
cfv.assert_range(config.FrontPanelTheme, 1, 2)
|
||||||
|
cfv.assert_type_int(config.ColorMode)
|
||||||
|
cfv.assert_range(config.ColorMode, 1, themes.COLOR_MODE.NUM_MODES)
|
||||||
|
|
||||||
return cfv.valid()
|
return cfv.valid()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,6 @@ local logic = require("supervisor.unitlogic")
|
|||||||
local plc = require("supervisor.session.plc")
|
local plc = require("supervisor.session.plc")
|
||||||
local rsctl = require("supervisor.session.rsctl")
|
local rsctl = require("supervisor.session.rsctl")
|
||||||
|
|
||||||
---@class reactor_control_unit
|
|
||||||
local unit = {}
|
|
||||||
|
|
||||||
local WASTE_MODE = types.WASTE_MODE
|
local WASTE_MODE = types.WASTE_MODE
|
||||||
local WASTE = types.WASTE_PRODUCT
|
local WASTE = types.WASTE_PRODUCT
|
||||||
local ALARM = types.ALARM
|
local ALARM = types.ALARM
|
||||||
@@ -55,17 +52,27 @@ local AISTATE = {
|
|||||||
---@field id ALARM alarm ID
|
---@field id ALARM alarm ID
|
||||||
---@field tier integer alarm urgency tier (0 = highest)
|
---@field tier integer alarm urgency tier (0 = highest)
|
||||||
|
|
||||||
|
-- burn rate to idle at
|
||||||
|
local IDLE_RATE = 0.01
|
||||||
|
|
||||||
|
---@class reactor_control_unit
|
||||||
|
local unit = {}
|
||||||
|
|
||||||
-- create a new reactor unit
|
-- create a new reactor unit
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param reactor_id integer reactor unit number
|
---@param reactor_id integer reactor unit number
|
||||||
---@param num_boilers integer number of boilers expected
|
---@param num_boilers integer number of boilers expected
|
||||||
---@param num_turbines integer number of turbines expected
|
---@param num_turbines integer number of turbines expected
|
||||||
function unit.new(reactor_id, num_boilers, num_turbines)
|
---@param ext_idle boolean extended idling mode
|
||||||
|
function unit.new(reactor_id, num_boilers, num_turbines, ext_idle)
|
||||||
|
-- time (ms) to idle for auto idling
|
||||||
|
local IDLE_TIME = util.trinary(ext_idle, 60000, 10000)
|
||||||
|
|
||||||
---@class _unit_self
|
---@class _unit_self
|
||||||
local self = {
|
local self = {
|
||||||
r_id = reactor_id,
|
r_id = reactor_id,
|
||||||
plc_s = nil, ---@class plc_session_struct
|
plc_s = nil, ---@type plc_session_struct
|
||||||
plc_i = nil, ---@class plc_session
|
plc_i = nil, ---@type plc_session
|
||||||
num_boilers = num_boilers,
|
num_boilers = num_boilers,
|
||||||
num_turbines = num_turbines,
|
num_turbines = num_turbines,
|
||||||
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
types = { DT_KEYS = DT_KEYS, AISTATE = AISTATE },
|
||||||
@@ -77,13 +84,15 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
tanks = {},
|
tanks = {},
|
||||||
snas = {},
|
snas = {},
|
||||||
envd = {},
|
envd = {},
|
||||||
sna_prod_rate = 0,
|
|
||||||
-- redstone control
|
-- redstone control
|
||||||
io_ctl = nil, ---@type rs_controller
|
io_ctl = nil, ---@type rs_controller
|
||||||
valves = {}, ---@type unit_valves
|
valves = {}, ---@type unit_valves
|
||||||
emcool_opened = false,
|
emcool_opened = false,
|
||||||
-- auto control
|
-- auto control
|
||||||
auto_engaged = false,
|
auto_engaged = false,
|
||||||
|
auto_idle = false,
|
||||||
|
auto_idling = false,
|
||||||
|
auto_idle_start = 0,
|
||||||
auto_was_alarmed = false,
|
auto_was_alarmed = false,
|
||||||
ramp_target_br100 = 0,
|
ramp_target_br100 = 0,
|
||||||
-- state tracking
|
-- state tracking
|
||||||
@@ -99,6 +108,8 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
status_text = { "UNKNOWN", "awaiting connection..." },
|
status_text = { "UNKNOWN", "awaiting connection..." },
|
||||||
-- logic for alarms
|
-- logic for alarms
|
||||||
had_reactor = false,
|
had_reactor = false,
|
||||||
|
turbine_flow_stable = false,
|
||||||
|
turbine_stability_data = {},
|
||||||
last_rate_change_ms = 0,
|
last_rate_change_ms = 0,
|
||||||
---@type rps_status
|
---@type rps_status
|
||||||
last_rps_trips = {
|
last_rps_trips = {
|
||||||
@@ -136,7 +147,8 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
},
|
},
|
||||||
damage = 0,
|
damage = 0,
|
||||||
temp = 0,
|
temp = 0,
|
||||||
waste = 0
|
waste = 0,
|
||||||
|
high_temp_lim = 1150
|
||||||
},
|
},
|
||||||
---@class alarm_monitors
|
---@class alarm_monitors
|
||||||
alarms = {
|
alarms = {
|
||||||
@@ -252,11 +264,12 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
table.insert(self.db.annunciator.TurbineOverSpeed, false)
|
table.insert(self.db.annunciator.TurbineOverSpeed, false)
|
||||||
table.insert(self.db.annunciator.GeneratorTrip, false)
|
table.insert(self.db.annunciator.GeneratorTrip, false)
|
||||||
table.insert(self.db.annunciator.TurbineTrip, false)
|
table.insert(self.db.annunciator.TurbineTrip, false)
|
||||||
|
table.insert(self.turbine_stability_data, { time_state = 0, time_tanks = 0, rotation = 1 })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PRIVATE FUNCTIONS --
|
-- PRIVATE FUNCTIONS --
|
||||||
|
|
||||||
--#region time derivative utility functions
|
--#region Time Derivative Utility Functions
|
||||||
|
|
||||||
-- compute a change with respect to time of the given value
|
-- compute a change with respect to time of the given value
|
||||||
---@param key string value key
|
---@param key string value key
|
||||||
@@ -331,7 +344,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
--#region redstone I/O
|
--#region Redstone I/O
|
||||||
|
|
||||||
-- create a generic valve interface
|
-- create a generic valve interface
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -398,8 +411,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
---@class reactor_unit
|
---@class reactor_unit
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- ADD/LINK DEVICES --
|
--#region Add/Link Devices
|
||||||
--#region
|
|
||||||
|
|
||||||
-- link the PLC
|
-- link the PLC
|
||||||
---@param plc_session plc_session_struct
|
---@param plc_session plc_session_struct
|
||||||
@@ -489,7 +501,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
-- UPDATE SESSION --
|
--#region Update Session
|
||||||
|
|
||||||
-- update (iterate) this unit
|
-- update (iterate) this unit
|
||||||
function public.update()
|
function public.update()
|
||||||
@@ -532,6 +544,13 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
-- re-engage auto lock if it reconnected without it
|
-- 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
|
if self.auto_engaged and not self.plc_i.is_auto_locked() then self.plc_i.auto_lock(true) end
|
||||||
|
|
||||||
|
-- stop idling when completed
|
||||||
|
if self.auto_idling and (((util.time_ms() - self.auto_idle_start) > IDLE_TIME) or not self.auto_idle) then
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": completed idling period"))
|
||||||
|
self.auto_idling = false
|
||||||
|
self.plc_i.auto_set_burn(0, false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- update deltas
|
-- update deltas
|
||||||
@@ -557,8 +576,9 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- AUTO CONTROL OPERATIONS --
|
--#endregion
|
||||||
--#region
|
|
||||||
|
--#region Auto Control Operations
|
||||||
|
|
||||||
-- engage automatic control
|
-- engage automatic control
|
||||||
function public.auto_engage()
|
function public.auto_engage()
|
||||||
@@ -579,6 +599,23 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- set automatic control idling mode to change behavior when given a burn rate command of zero<br>
|
||||||
|
-- - enabling it will hold the reactor at 0.01 mB/t for a period when commanded zero before disabling
|
||||||
|
-- - disabling it will stop the reactor when commanded zero
|
||||||
|
---@param idle boolean true to enable, false to disable (and stop)
|
||||||
|
function public.auto_set_idle(idle)
|
||||||
|
if idle and not self.auto_idle then
|
||||||
|
self.auto_idling = false
|
||||||
|
self.auto_idle_start = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
if idle ~= self.auto_idle then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": idling mode changed to ", idle))
|
||||||
|
end
|
||||||
|
|
||||||
|
self.auto_idle = idle
|
||||||
|
end
|
||||||
|
|
||||||
-- get the actual limit of this unit<br>
|
-- get the actual limit of this unit<br>
|
||||||
-- if it is degraded or not ready, the limit will be 0
|
-- if it is degraded or not ready, the limit will be 0
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -598,7 +635,35 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
if self.auto_engaged then
|
if self.auto_engaged then
|
||||||
if self.plc_i ~= nil then
|
if self.plc_i ~= nil then
|
||||||
log.debug(util.c("UNIT ", self.r_id, ": commit br100 of ", self.db.control.br100, " with ramp set to ", ramp))
|
log.debug(util.c("UNIT ", self.r_id, ": commit br100 of ", self.db.control.br100, " with ramp set to ", ramp))
|
||||||
self.plc_i.auto_set_burn(self.db.control.br100 / 100, ramp)
|
|
||||||
|
local rate = self.db.control.br100 / 100
|
||||||
|
|
||||||
|
if self.auto_idle then
|
||||||
|
if rate <= IDLE_RATE then
|
||||||
|
if self.auto_idle_start == 0 then
|
||||||
|
self.auto_idling = true
|
||||||
|
self.auto_idle_start = util.time_ms()
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": started idling at ", IDLE_RATE, " mB/t"))
|
||||||
|
|
||||||
|
rate = IDLE_RATE
|
||||||
|
elseif (util.time_ms() - self.auto_idle_start) > IDLE_TIME then
|
||||||
|
if self.auto_idling then
|
||||||
|
self.auto_idling = false
|
||||||
|
log.info(util.c("UNIT ", self.r_id, ": completed idling period"))
|
||||||
|
end
|
||||||
|
else
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": continuing idle at ", IDLE_RATE, " mB/t"))
|
||||||
|
|
||||||
|
rate = IDLE_RATE
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self.auto_idling = false
|
||||||
|
self.auto_idle_start = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.plc_i.auto_set_burn(rate, ramp)
|
||||||
|
|
||||||
if ramp then self.ramp_target_br100 = self.db.control.br100 end
|
if ramp then self.ramp_target_br100 = self.db.control.br100 end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -645,8 +710,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
-- OPERATIONS --
|
--#region Operations
|
||||||
--#region
|
|
||||||
|
|
||||||
-- queue a command to disable the reactor
|
-- queue a command to disable the reactor
|
||||||
function public.disable()
|
function public.disable()
|
||||||
@@ -726,8 +790,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
|
|
||||||
--#endregion
|
--#endregion
|
||||||
|
|
||||||
-- READ STATES/PROPERTIES --
|
--#region Read States/Properties
|
||||||
--#region
|
|
||||||
|
|
||||||
-- check if an alarm of at least a certain priority level is tripped
|
-- check if an alarm of at least a certain priority level is tripped
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -857,13 +920,15 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
status.tanks[tank.get_device_idx()] = { tank.is_faulted(), db.formed, db.state, db.tanks }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- basic SNA statistical information
|
-- SNA statistical information
|
||||||
local total_peak = 0
|
local total_peak, total_avail, total_out = 0, 0, 0
|
||||||
for i = 1, #self.snas do
|
for i = 1, #self.snas do
|
||||||
local db = self.snas[i].get_db() ---@type sna_session_db
|
local db = self.snas[i].get_db() ---@type sna_session_db
|
||||||
total_peak = total_peak + db.state.peak_production
|
total_peak = total_peak + db.state.peak_production
|
||||||
|
total_avail = total_avail + db.state.production_rate
|
||||||
|
total_out = total_out + math.min(db.tanks.input.amount / 10, db.state.production_rate)
|
||||||
end
|
end
|
||||||
status.sna = { #self.snas, public.get_sna_rate(), total_peak }
|
status.sna = { #self.snas, total_peak, total_avail, total_out }
|
||||||
|
|
||||||
-- radiation monitors (environment detectors)
|
-- radiation monitors (environment detectors)
|
||||||
status.envds = {}
|
status.envds = {}
|
||||||
@@ -876,7 +941,7 @@ function unit.new(reactor_id, num_boilers, num_turbines)
|
|||||||
return status
|
return status
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the current total [max] production rate is
|
-- get the current total max production rate
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@return number total_avail_rate
|
---@return number total_avail_rate
|
||||||
function public.get_sna_rate()
|
function public.get_sna_rate()
|
||||||
|
|||||||
@@ -39,6 +39,21 @@ local ALARM_LIMS = const.ALARM_LIMITS
|
|||||||
---@class unit_logic_extension
|
---@class unit_logic_extension
|
||||||
local logic = {}
|
local logic = {}
|
||||||
|
|
||||||
|
-- compute Mekanism's rotation rate for a turbine
|
||||||
|
---@param turbine turbinev_session_db
|
||||||
|
local function turbine_rotation(turbine)
|
||||||
|
local build = turbine.build
|
||||||
|
|
||||||
|
local inner_vol = build.steam_cap / const.mek.TURBINE_GAS_PER_TANK
|
||||||
|
local disp_rate = (build.dispersers * const.mek.TURBINE_DISPERSER_FLOW) * inner_vol
|
||||||
|
local vent_rate = build.vents * const.mek.TURBINE_VENT_FLOW
|
||||||
|
|
||||||
|
local max_rate = math.min(disp_rate, vent_rate)
|
||||||
|
local flow = math.min(max_rate, turbine.tanks.steam.amount)
|
||||||
|
|
||||||
|
return (flow * (turbine.tanks.steam.amount / build.steam_cap)) / max_rate
|
||||||
|
end
|
||||||
|
|
||||||
-- update the annunciator
|
-- update the annunciator
|
||||||
---@param self _unit_self
|
---@param self _unit_self
|
||||||
function logic.update_annunciator(self)
|
function logic.update_annunciator(self)
|
||||||
@@ -54,9 +69,7 @@ function logic.update_annunciator(self)
|
|||||||
-- variables for boiler, or reactor if no boilers used
|
-- variables for boiler, or reactor if no boilers used
|
||||||
local total_boil_rate = 0.0
|
local total_boil_rate = 0.0
|
||||||
|
|
||||||
-------------
|
--#region Reactor
|
||||||
-- REACTOR --
|
|
||||||
-------------
|
|
||||||
|
|
||||||
annunc.AutoControl = self.auto_engaged
|
annunc.AutoControl = self.auto_engaged
|
||||||
|
|
||||||
@@ -83,6 +96,11 @@ function logic.update_annunciator(self)
|
|||||||
-- some alarms wait until the burn rate has stabilized, so keep track of that
|
-- some alarms wait until the burn rate has stabilized, so keep track of that
|
||||||
if math.abs(_get_dt(DT_KEYS.ReactorBurnR)) > 0 then
|
if math.abs(_get_dt(DT_KEYS.ReactorBurnR)) > 0 then
|
||||||
self.last_rate_change_ms = util.time_ms()
|
self.last_rate_change_ms = util.time_ms()
|
||||||
|
self.turbine_flow_stable = false
|
||||||
|
|
||||||
|
for t = 1, self.num_turbines do
|
||||||
|
self.turbine_stability_data[t] = { time_state = 0, time_tanks = 0, rotation = 1 }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- record reactor stats
|
-- record reactor stats
|
||||||
@@ -115,7 +133,15 @@ function logic.update_annunciator(self)
|
|||||||
self.last_heartbeat = plc_db.last_status_update
|
self.last_heartbeat = plc_db.last_status_update
|
||||||
end
|
end
|
||||||
|
|
||||||
local flow_low = util.trinary(plc_db.mek_status.ccool_type == types.FLUID.SODIUM, ANNUNC_LIMS.RCSFlowLow_NA, ANNUNC_LIMS.RCSFlowLow_H2O)
|
local flow_low = ANNUNC_LIMS.RCSFlowLow_H2O
|
||||||
|
local high_temp = plc_db.max_op_temp_H2O
|
||||||
|
|
||||||
|
if plc_db.mek_status.ccool_type == types.FLUID.SODIUM then
|
||||||
|
flow_low = ANNUNC_LIMS.RCSFlowLow_NA
|
||||||
|
high_temp = plc_db.max_op_temp_Na
|
||||||
|
end
|
||||||
|
|
||||||
|
self.plc_cache.high_temp_lim = math.min(high_temp + ANNUNC_LIMS.OpTempTolerance, 1200)
|
||||||
|
|
||||||
-- update other annunciator fields
|
-- update other annunciator fields
|
||||||
annunc.ReactorSCRAM = plc_db.rps_tripped
|
annunc.ReactorSCRAM = plc_db.rps_tripped
|
||||||
@@ -124,7 +150,7 @@ function logic.update_annunciator(self)
|
|||||||
annunc.RCPTrip = plc_db.rps_tripped and (plc_db.rps_status.ex_hcool or plc_db.rps_status.low_cool)
|
annunc.RCPTrip = plc_db.rps_tripped and (plc_db.rps_status.ex_hcool or plc_db.rps_status.low_cool)
|
||||||
annunc.RCSFlowLow = _get_dt(DT_KEYS.ReactorCCool) < flow_low
|
annunc.RCSFlowLow = _get_dt(DT_KEYS.ReactorCCool) < flow_low
|
||||||
annunc.CoolantLevelLow = plc_db.mek_status.ccool_fill < ANNUNC_LIMS.CoolantLevelLow
|
annunc.CoolantLevelLow = plc_db.mek_status.ccool_fill < ANNUNC_LIMS.CoolantLevelLow
|
||||||
annunc.ReactorTempHigh = plc_db.mek_status.temp > ANNUNC_LIMS.ReactorTempHigh
|
annunc.ReactorTempHigh = plc_db.mek_status.temp >= self.plc_cache.high_temp_lim
|
||||||
annunc.ReactorHighDeltaT = _get_dt(DT_KEYS.ReactorTemp) > ANNUNC_LIMS.ReactorHighDeltaT
|
annunc.ReactorHighDeltaT = _get_dt(DT_KEYS.ReactorTemp) > ANNUNC_LIMS.ReactorHighDeltaT
|
||||||
annunc.FuelInputRateLow = _get_dt(DT_KEYS.ReactorFuel) < -1.0 or plc_db.mek_status.fuel_fill <= ANNUNC_LIMS.FuelLevelLow
|
annunc.FuelInputRateLow = _get_dt(DT_KEYS.ReactorFuel) < -1.0 or plc_db.mek_status.fuel_fill <= ANNUNC_LIMS.FuelLevelLow
|
||||||
annunc.WasteLineOcclusion = _get_dt(DT_KEYS.ReactorWaste) > 1.0 or plc_db.mek_status.waste_fill >= ANNUNC_LIMS.WasteLevelHigh
|
annunc.WasteLineOcclusion = _get_dt(DT_KEYS.ReactorWaste) > 1.0 or plc_db.mek_status.waste_fill >= ANNUNC_LIMS.WasteLevelHigh
|
||||||
@@ -143,9 +169,9 @@ function logic.update_annunciator(self)
|
|||||||
self.plc_cache.ok = false
|
self.plc_cache.ok = false
|
||||||
end
|
end
|
||||||
|
|
||||||
---------------
|
--#endregion
|
||||||
-- MISC RTUs --
|
|
||||||
---------------
|
--#region Misc RTUs
|
||||||
|
|
||||||
local max_rad, any_faulted = 0, false
|
local max_rad, any_faulted = 0, false
|
||||||
|
|
||||||
@@ -170,9 +196,9 @@ function logic.update_annunciator(self)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------
|
--#endregion
|
||||||
-- BOILERS --
|
|
||||||
-------------
|
--#region Boilers
|
||||||
|
|
||||||
local boilers_ready = num_boilers == #self.boilers
|
local boilers_ready = num_boilers == #self.boilers
|
||||||
|
|
||||||
@@ -230,9 +256,9 @@ function logic.update_annunciator(self)
|
|||||||
boiler_water_dt_sum = _get_dt(DT_KEYS.ReactorCCool)
|
boiler_water_dt_sum = _get_dt(DT_KEYS.ReactorCCool)
|
||||||
end
|
end
|
||||||
|
|
||||||
---------------------------
|
--#endregion
|
||||||
-- COOLANT FEED MISMATCH --
|
|
||||||
---------------------------
|
--#region Coolant Feed Mismatch
|
||||||
|
|
||||||
-- check coolant feed mismatch if using boilers, otherwise calculate with reactor
|
-- check coolant feed mismatch if using boilers, otherwise calculate with reactor
|
||||||
local cfmismatch = false
|
local cfmismatch = false
|
||||||
@@ -263,9 +289,9 @@ function logic.update_annunciator(self)
|
|||||||
|
|
||||||
annunc.CoolantFeedMismatch = cfmismatch
|
annunc.CoolantFeedMismatch = cfmismatch
|
||||||
|
|
||||||
--------------
|
--#endregion
|
||||||
-- TURBINES --
|
|
||||||
--------------
|
--#region Turbines
|
||||||
|
|
||||||
local turbines_ready = num_turbines == #self.turbines
|
local turbines_ready = num_turbines == #self.turbines
|
||||||
|
|
||||||
@@ -276,6 +302,7 @@ function logic.update_annunciator(self)
|
|||||||
local total_flow_rate = 0
|
local total_flow_rate = 0
|
||||||
local total_input_rate = 0
|
local total_input_rate = 0
|
||||||
local max_water_return_rate = 0
|
local max_water_return_rate = 0
|
||||||
|
local turbines_stable = true
|
||||||
|
|
||||||
-- recompute blade count on the chance that it may have changed
|
-- recompute blade count on the chance that it may have changed
|
||||||
self.db.control.blade_count = 0
|
self.db.control.blade_count = 0
|
||||||
@@ -284,12 +311,14 @@ function logic.update_annunciator(self)
|
|||||||
for i = 1, #self.turbines do
|
for i = 1, #self.turbines do
|
||||||
local session = self.turbines[i] ---@type unit_session
|
local session = self.turbines[i] ---@type unit_session
|
||||||
local turbine = session.get_db() ---@type turbinev_session_db
|
local turbine = session.get_db() ---@type turbinev_session_db
|
||||||
|
local idx = session.get_device_idx()
|
||||||
|
|
||||||
annunc.RCSFault = annunc.RCSFault or (not turbine.formed) or session.is_faulted()
|
annunc.RCSFault = annunc.RCSFault or (not turbine.formed) or session.is_faulted()
|
||||||
|
annunc.TurbineOnline[idx] = true
|
||||||
|
|
||||||
-- update ready state
|
-- update ready state
|
||||||
-- - must be formed
|
-- - must be formed
|
||||||
-- - must have received build, state, and tanks at least once
|
-- - must have received build, state, and tanks at least once
|
||||||
turbines_ready = turbines_ready and turbine.formed and
|
turbines_ready = turbines_ready and turbine.formed and
|
||||||
(turbine.build.last_update > 0) and
|
(turbine.build.last_update > 0) and
|
||||||
(turbine.state.last_update > 0) and
|
(turbine.state.last_update > 0) and
|
||||||
@@ -298,11 +327,56 @@ function logic.update_annunciator(self)
|
|||||||
total_flow_rate = total_flow_rate + turbine.state.flow_rate
|
total_flow_rate = total_flow_rate + turbine.state.flow_rate
|
||||||
total_input_rate = total_input_rate + turbine.state.steam_input_rate
|
total_input_rate = total_input_rate + turbine.state.steam_input_rate
|
||||||
max_water_return_rate = max_water_return_rate + turbine.build.max_water_output
|
max_water_return_rate = max_water_return_rate + turbine.build.max_water_output
|
||||||
|
|
||||||
self.db.control.blade_count = self.db.control.blade_count + turbine.build.blades
|
self.db.control.blade_count = self.db.control.blade_count + turbine.build.blades
|
||||||
|
|
||||||
annunc.TurbineOnline[session.get_device_idx()] = true
|
local last = self.turbine_stability_data[i]
|
||||||
|
|
||||||
|
if (not self.turbine_flow_stable) and (turbine.state.steam_input_rate > 0) then
|
||||||
|
local rotation = turbine_rotation(turbine)
|
||||||
|
local rotation_stable = false
|
||||||
|
|
||||||
|
-- see if data updated, and if so, check rotation speed change
|
||||||
|
-- minimal change indicates the turbine is converging on a flow rate
|
||||||
|
if last.time_tanks < turbine.tanks.last_update then
|
||||||
|
if last.time_tanks > 0 then
|
||||||
|
rotation_stable = math.abs(rotation - last.rotation) < 0.00000003
|
||||||
|
end
|
||||||
|
|
||||||
|
last.time_tanks = turbine.tanks.last_update
|
||||||
|
last.rotation = rotation
|
||||||
|
end
|
||||||
|
|
||||||
|
-- flow is stable if the flow rate is at the input rate or at the max (±1 mB/t)
|
||||||
|
local flow_stable = false
|
||||||
|
if last.time_state < turbine.state.last_update then
|
||||||
|
if (last.time_state > 0) and (turbine.state.flow_rate > 0) then
|
||||||
|
flow_stable = math.abs(turbine.state.flow_rate - math.min(turbine.state.steam_input_rate, turbine.build.max_flow_rate)) < 2
|
||||||
|
end
|
||||||
|
|
||||||
|
last.time_state = turbine.state.last_update
|
||||||
|
end
|
||||||
|
|
||||||
|
if rotation_stable then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached rotational stability (", rotation, ")"))
|
||||||
|
end
|
||||||
|
|
||||||
|
if flow_stable then
|
||||||
|
log.debug(util.c("UNIT ", self.r_id, ": turbine ", idx, " reached flow stability (", turbine.state.flow_rate, " mB/t)"))
|
||||||
|
end
|
||||||
|
|
||||||
|
turbines_stable = turbines_stable and (rotation_stable or flow_stable)
|
||||||
|
else
|
||||||
|
last.time_state = 0
|
||||||
|
last.time_tanks = 0
|
||||||
|
last.rotation = 1
|
||||||
|
|
||||||
|
turbines_stable = false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.turbine_flow_stable = self.turbine_flow_stable or turbines_stable
|
||||||
|
|
||||||
-- check for boil rate mismatch (> 4% error) either between reactor and turbine or boiler and turbine
|
-- check for boil rate mismatch (> 4% error) either between reactor and turbine or boiler and turbine
|
||||||
annunc.BoilRateMismatch = math.abs(total_boil_rate - total_input_rate) > (0.04 * total_boil_rate)
|
annunc.BoilRateMismatch = math.abs(total_boil_rate - total_input_rate) > (0.04 * total_boil_rate)
|
||||||
|
|
||||||
@@ -340,6 +414,8 @@ function logic.update_annunciator(self)
|
|||||||
annunc.TurbineTrip[idx] = has_steam and db.state.flow_rate == 0
|
annunc.TurbineTrip[idx] = has_steam and db.state.flow_rate == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--#endregion
|
||||||
|
|
||||||
-- update auto control ready state for this unit
|
-- update auto control ready state for this unit
|
||||||
self.db.control.ready = plc_ready and boilers_ready and turbines_ready
|
self.db.control.ready = plc_ready and boilers_ready and turbines_ready
|
||||||
end
|
end
|
||||||
@@ -474,7 +550,8 @@ function logic.update_alarms(self)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- High Temperature
|
-- High Temperature
|
||||||
_update_alarm_state(self, plc_cache.temp >= ALARM_LIMS.HIGH_TEMP, self.alarms.ReactorHighTemp)
|
local high_temp = math.min(math.max(self.plc_cache.high_temp_lim, 1100), 1199.995)
|
||||||
|
_update_alarm_state(self, plc_cache.temp >= high_temp, self.alarms.ReactorHighTemp)
|
||||||
|
|
||||||
-- Waste Leak
|
-- Waste Leak
|
||||||
_update_alarm_state(self, plc_cache.waste >= 1.0, self.alarms.ReactorWasteLeak)
|
_update_alarm_state(self, plc_cache.waste >= 1.0, self.alarms.ReactorWasteLeak)
|
||||||
@@ -508,11 +585,25 @@ function logic.update_alarms(self)
|
|||||||
|
|
||||||
local rcs_trans = any_low or any_over or gen_trip or annunc.RCPTrip or annunc.MaxWaterReturnFeed
|
local rcs_trans = any_low or any_over or gen_trip or annunc.RCPTrip or annunc.MaxWaterReturnFeed
|
||||||
|
|
||||||
-- annunciator indicators for these states may not indicate a real issue when:
|
if plc_cache.active then
|
||||||
-- > flow is ramping up right after reactor start
|
-- these conditions may not indicate an issue when flow is changing after a burn rate change
|
||||||
-- > flow is ramping down after reactor shutdown
|
if self.num_boilers == 0 then
|
||||||
if ((util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS) and plc_cache.active then
|
if (util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS then
|
||||||
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
|
rcs_trans = rcs_trans or annunc.BoilRateMismatch
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.turbine_flow_stable then
|
||||||
|
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.CoolantFeedMismatch or annunc.SteamFeedMismatch
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if (util.time_ms() - self.last_rate_change_ms) > FLOW_STABILITY_DELAY_MS then
|
||||||
|
rcs_trans = rcs_trans or annunc.RCSFlowLow or annunc.BoilRateMismatch or annunc.CoolantFeedMismatch
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.turbine_flow_stable then
|
||||||
|
rcs_trans = rcs_trans or annunc.SteamFeedMismatch
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if _update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) then
|
if _update_alarm_state(self, rcs_trans, self.alarms.RCSTransient) then
|
||||||
@@ -636,11 +727,11 @@ function logic.update_status_text(self)
|
|||||||
self.status_text[2] = "elevated level of radiation"
|
self.status_text[2] = "elevated level of radiation"
|
||||||
end
|
end
|
||||||
elseif is_active(self.alarms.ReactorOverTemp) then
|
elseif is_active(self.alarms.ReactorOverTemp) then
|
||||||
self.status_text = { "CORE OVER TEMP", "reactor core temperature >=1200K" }
|
self.status_text = { "CORE OVER TEMP", "reactor core temp damaging" }
|
||||||
elseif is_active(self.alarms.ReactorWasteLeak) then
|
elseif is_active(self.alarms.ReactorWasteLeak) then
|
||||||
self.status_text = { "WASTE LEAK", "radioactive waste leak detected" }
|
self.status_text = { "WASTE LEAK", "radioactive waste leak detected" }
|
||||||
elseif is_active(self.alarms.ReactorHighTemp) then
|
elseif is_active(self.alarms.ReactorHighTemp) then
|
||||||
self.status_text = { "CORE TEMP HIGH", "reactor core temperature >1150K" }
|
self.status_text = { "CORE TEMP HIGH", "reactor core temperature high" }
|
||||||
elseif is_active(self.alarms.ReactorHighWaste) then
|
elseif is_active(self.alarms.ReactorHighWaste) then
|
||||||
self.status_text = { "WASTE LEVEL HIGH", "waste accumulating in reactor" }
|
self.status_text = { "WASTE LEVEL HIGH", "waste accumulating in reactor" }
|
||||||
elseif is_active(self.alarms.TurbineTrip) then
|
elseif is_active(self.alarms.TurbineTrip) then
|
||||||
@@ -666,7 +757,9 @@ function logic.update_status_text(self)
|
|||||||
elseif annunc.WasteLineOcclusion then
|
elseif annunc.WasteLineOcclusion then
|
||||||
self.status_text[2] = "insufficient waste output rate"
|
self.status_text[2] = "insufficient waste output rate"
|
||||||
elseif (util.time_ms() - self.last_rate_change_ms) <= FLOW_STABILITY_DELAY_MS then
|
elseif (util.time_ms() - self.last_rate_change_ms) <= FLOW_STABILITY_DELAY_MS then
|
||||||
self.status_text[2] = "awaiting flow stability"
|
self.status_text[2] = "awaiting coolant flow stability"
|
||||||
|
elseif not self.turbine_flow_stable then
|
||||||
|
self.status_text[2] = "awaiting turbine flow stability"
|
||||||
else
|
else
|
||||||
self.status_text[2] = "system nominal"
|
self.status_text[2] = "system nominal"
|
||||||
end
|
end
|
||||||
@@ -730,6 +823,8 @@ end
|
|||||||
function logic.handle_redstone(self)
|
function logic.handle_redstone(self)
|
||||||
local AISTATE = self.types.AISTATE
|
local AISTATE = self.types.AISTATE
|
||||||
local annunc = self.db.annunciator
|
local annunc = self.db.annunciator
|
||||||
|
local cache = self.plc_cache
|
||||||
|
local rps = cache.rps_status
|
||||||
|
|
||||||
-- check if an alarm is active (tripped or ack'd)
|
-- check if an alarm is active (tripped or ack'd)
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
@@ -741,18 +836,18 @@ function logic.handle_redstone(self)
|
|||||||
|
|
||||||
-- reactor controls
|
-- reactor controls
|
||||||
if self.plc_s ~= nil then
|
if self.plc_s ~= nil then
|
||||||
if (not self.plc_cache.rps_status.manual) and self.io_ctl.digital_read(IO.R_SCRAM) then
|
if (not rps.manual) and self.io_ctl.digital_read(IO.R_SCRAM) then
|
||||||
-- reactor SCRAM requested but not yet done; perform it
|
-- reactor SCRAM requested but not yet done; perform it
|
||||||
self.plc_s.in_queue.push_command(PLC_S_CMDS.SCRAM)
|
self.plc_s.in_queue.push_command(PLC_S_CMDS.SCRAM)
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.plc_cache.rps_trip and self.io_ctl.digital_read(IO.R_RESET) then
|
if cache.rps_trip and self.io_ctl.digital_read(IO.R_RESET) then
|
||||||
-- reactor RPS reset requested but not yet done; perform it
|
-- reactor RPS reset requested but not yet done; perform it
|
||||||
self.plc_s.in_queue.push_command(PLC_S_CMDS.RPS_RESET)
|
self.plc_s.in_queue.push_command(PLC_S_CMDS.RPS_RESET)
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not self.auto_engaged) and (not self.plc_cache.active) and
|
if (not self.auto_engaged) and (not cache.active) and
|
||||||
(not self.plc_cache.rps_trip) and self.io_ctl.digital_read(IO.R_ENABLE) then
|
(not cache.rps_trip) and self.io_ctl.digital_read(IO.R_ENABLE) then
|
||||||
-- reactor enable requested and allowable, but not yet done; perform it
|
-- reactor enable requested and allowable, but not yet done; perform it
|
||||||
self.plc_s.in_queue.push_command(PLC_S_CMDS.ENABLE)
|
self.plc_s.in_queue.push_command(PLC_S_CMDS.ENABLE)
|
||||||
end
|
end
|
||||||
@@ -761,25 +856,23 @@ function logic.handle_redstone(self)
|
|||||||
-- check for request to ack all alarms
|
-- check for request to ack all alarms
|
||||||
if self.io_ctl.digital_read(IO.U_ACK) then
|
if self.io_ctl.digital_read(IO.U_ACK) then
|
||||||
for i = 1, #self.db.alarm_states do
|
for i = 1, #self.db.alarm_states do
|
||||||
if self.db.alarm_states[i] == ALARM_STATE.TRIPPED then
|
if self.db.alarm_states[i] == ALARM_STATE.TRIPPED then self.db.alarm_states[i] = ALARM_STATE.ACKED end
|
||||||
self.db.alarm_states[i] = ALARM_STATE.ACKED
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- write reactor status outputs
|
-- write reactor status outputs
|
||||||
self.io_ctl.digital_write(IO.R_ACTIVE, self.plc_cache.active)
|
self.io_ctl.digital_write(IO.R_ACTIVE, cache.active)
|
||||||
self.io_ctl.digital_write(IO.R_AUTO_CTRL, self.auto_engaged)
|
self.io_ctl.digital_write(IO.R_AUTO_CTRL, self.auto_engaged)
|
||||||
self.io_ctl.digital_write(IO.R_SCRAMMED, self.plc_cache.rps_trip)
|
self.io_ctl.digital_write(IO.R_SCRAMMED, cache.rps_trip)
|
||||||
self.io_ctl.digital_write(IO.R_AUTO_SCRAM, self.plc_cache.rps_status.automatic)
|
self.io_ctl.digital_write(IO.R_AUTO_SCRAM, rps.automatic)
|
||||||
self.io_ctl.digital_write(IO.R_HIGH_DMG, self.plc_cache.rps_status.high_dmg)
|
self.io_ctl.digital_write(IO.R_HIGH_DMG, rps.high_dmg)
|
||||||
self.io_ctl.digital_write(IO.R_HIGH_TEMP, self.plc_cache.rps_status.high_temp)
|
self.io_ctl.digital_write(IO.R_HIGH_TEMP, rps.high_temp)
|
||||||
self.io_ctl.digital_write(IO.R_LOW_COOLANT, self.plc_cache.rps_status.low_cool)
|
self.io_ctl.digital_write(IO.R_LOW_COOLANT, rps.low_cool)
|
||||||
self.io_ctl.digital_write(IO.R_EXCESS_HC, self.plc_cache.rps_status.ex_hcool)
|
self.io_ctl.digital_write(IO.R_EXCESS_HC, rps.ex_hcool)
|
||||||
self.io_ctl.digital_write(IO.R_EXCESS_WS, self.plc_cache.rps_status.ex_waste)
|
self.io_ctl.digital_write(IO.R_EXCESS_WS, rps.ex_waste)
|
||||||
self.io_ctl.digital_write(IO.R_INSUFF_FUEL, self.plc_cache.rps_status.no_fuel)
|
self.io_ctl.digital_write(IO.R_INSUFF_FUEL, rps.no_fuel)
|
||||||
self.io_ctl.digital_write(IO.R_PLC_FAULT, self.plc_cache.rps_status.fault)
|
self.io_ctl.digital_write(IO.R_PLC_FAULT, rps.fault)
|
||||||
self.io_ctl.digital_write(IO.R_PLC_TIMEOUT, self.plc_cache.rps_status.timeout)
|
self.io_ctl.digital_write(IO.R_PLC_TIMEOUT, rps.timeout)
|
||||||
|
|
||||||
-- write unit outputs
|
-- write unit outputs
|
||||||
|
|
||||||
@@ -797,13 +890,28 @@ function logic.handle_redstone(self)
|
|||||||
-- Emergency Coolant --
|
-- Emergency Coolant --
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
local enable_emer_cool = self.plc_cache.rps_status.low_cool or
|
local boiler_water_low = false
|
||||||
(self.auto_engaged and annunc.CoolantLevelLow and is_active(self.alarms.ReactorOverTemp))
|
for i = 1, #annunc.WaterLevelLow do boiler_water_low = boiler_water_low or annunc.WaterLevelLow[i] end
|
||||||
|
|
||||||
|
local enable_emer_cool = rps.low_cool or
|
||||||
|
(self.auto_engaged and
|
||||||
|
(annunc.CoolantLevelLow or (boiler_water_low and rps.ex_hcool)) and
|
||||||
|
is_active(self.alarms.ReactorOverTemp))
|
||||||
|
|
||||||
|
if enable_emer_cool and not self.emcool_opened then
|
||||||
|
log.debug(util.c(">> Emergency Coolant Enable Detail Report (Unit ", self.r_id, ") <<"))
|
||||||
|
log.debug(util.c("| CoolantLevelLow[", annunc.CoolantLevelLow, "] CoolantLevelLowLow[", rps.low_cool, "] ExcessHeatedCoolant[", rps.ex_hcool, "]"))
|
||||||
|
log.debug(util.c("| ReactorOverTemp[", AISTATE_NAMES[self.alarms.ReactorOverTemp.state], "]"))
|
||||||
|
|
||||||
|
for i = 1, #annunc.WaterLevelLow do
|
||||||
|
log.debug(util.c("| WaterLevelLow(", i, ")[", annunc.WaterLevelLow[i], "]"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- don't turn off emergency coolant on sufficient coolant level since it might drop again
|
-- don't turn off emergency coolant on sufficient coolant level since it might drop again
|
||||||
-- turn off once system is OK again
|
-- turn off once system is OK again
|
||||||
-- if auto control is engaged, alarm check will SCRAM on reactor over temp so that's covered
|
-- if auto control is engaged, alarm check will SCRAM on reactor over temp so that's covered
|
||||||
if not self.plc_cache.rps_trip then
|
if not cache.rps_trip then
|
||||||
-- set turbines to not dump steam
|
-- set turbines to not dump steam
|
||||||
for i = 1, #self.turbines do
|
for i = 1, #self.turbines do
|
||||||
local session = self.turbines[i] ---@type unit_session
|
local session = self.turbines[i] ---@type unit_session
|
||||||
|
|||||||
@@ -1,16 +1,28 @@
|
|||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
local rsio = require("scada-common.rsio")
|
local rsio = require("scada-common.rsio")
|
||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local testutils = require("test.testutils")
|
local testutils = require("test.testutils")
|
||||||
|
|
||||||
|
local IO = rsio.IO
|
||||||
|
local IO_LVL = rsio.IO_LVL
|
||||||
|
local IO_MODE = rsio.IO_MODE
|
||||||
|
|
||||||
local print = util.print
|
local print = util.print
|
||||||
local println = util.println
|
local println = util.println
|
||||||
|
|
||||||
local IO = rsio.IO
|
-- list of inverted digital signals<br>
|
||||||
local IO_LVL = rsio.IO_LVL
|
-- only using the key for a quick lookup, value just can't be nil
|
||||||
local IO_MODE = rsio.IO_MODE
|
local DIG_INV = {
|
||||||
|
[IO.F_SCRAM] = 0,
|
||||||
|
[IO.R_SCRAM] = 0,
|
||||||
|
[IO.WASTE_PU] = 0,
|
||||||
|
[IO.WASTE_PO] = 0,
|
||||||
|
[IO.WASTE_POPL] = 0,
|
||||||
|
[IO.WASTE_AM] = 0,
|
||||||
|
[IO.U_EMER_COOL] = 0
|
||||||
|
}
|
||||||
|
|
||||||
println("starting RSIO tester")
|
println("starting RSIO tester")
|
||||||
println("")
|
println("")
|
||||||
@@ -50,8 +62,8 @@ testutils.pause()
|
|||||||
|
|
||||||
println(">>> checking invalid ports:")
|
println(">>> checking invalid ports:")
|
||||||
|
|
||||||
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "")
|
testutils.test_func("rsio.to_string", rsio.to_string, { -1, 100, false }, "UNKNOWN")
|
||||||
testutils.test_func_nil("rsio.to_string", rsio.to_string, "")
|
testutils.test_func_nil("rsio.to_string", rsio.to_string, "UNKNOWN")
|
||||||
testutils.test_func("rsio.get_io_mode", rsio.get_io_mode, { -1, 100, false }, IO_MODE.ANALOG_IN)
|
testutils.test_func("rsio.get_io_mode", rsio.get_io_mode, { -1, 100, false }, IO_MODE.ANALOG_IN)
|
||||||
testutils.test_func_nil("rsio.get_io_mode", rsio.get_io_mode, IO_MODE.ANALOG_IN)
|
testutils.test_func_nil("rsio.get_io_mode", rsio.get_io_mode, IO_MODE.ANALOG_IN)
|
||||||
|
|
||||||
@@ -100,46 +112,35 @@ println(">>> checking port I/O:")
|
|||||||
|
|
||||||
print("rsio.digital_is_active(...): ")
|
print("rsio.digital_is_active(...): ")
|
||||||
|
|
||||||
-- check input ports
|
-- check all digital ports
|
||||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.LOW) == true, "IO_F_SCRAM_HIGH")
|
for i = 1, rsio.NUM_PORTS do
|
||||||
assert(rsio.digital_is_active(IO.F_SCRAM, IO_LVL.HIGH) == false, "IO_F_SCRAM_LOW")
|
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.LOW) == true, "IO_R_SCRAM_HIGH")
|
local high = DIG_INV[i] == nil
|
||||||
assert(rsio.digital_is_active(IO.R_SCRAM, IO_LVL.HIGH) == false, "IO_R_SCRAM_LOW")
|
assert(rsio.digital_is_active(i, IO_LVL.LOW) == not high, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.LOW) == false, "IO_R_ENABLE_HIGH")
|
assert(rsio.digital_is_active(i, IO_LVL.HIGH) == high, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||||
assert(rsio.digital_is_active(IO.R_ENABLE, IO_LVL.HIGH) == true, "IO_R_ENABLE_LOW")
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- non-inputs should always return LOW
|
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.LOW) == nil, "ANA_DIG_READ_LOW")
|
||||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.LOW) == false, "IO_OUT_READ_LOW")
|
assert(rsio.digital_is_active(IO.F_MATRIX_CHG, IO_LVL.HIGH) == nil, "ANA_DIG_READ_HIGH")
|
||||||
assert(rsio.digital_is_active(IO.F_ALARM, IO_LVL.HIGH) == false, "IO_OUT_READ_HIGH")
|
|
||||||
|
|
||||||
println("PASS")
|
println("PASS")
|
||||||
|
|
||||||
-- check output ports
|
-- check digital write
|
||||||
|
|
||||||
print("rsio.digital_write(...): ")
|
print("rsio.digital_write_active(...): ")
|
||||||
|
|
||||||
-- check output ports
|
-- check all digital ports
|
||||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.LOW, "IO_F_ALARM_LOW")
|
for i = 1, rsio.NUM_PORTS do
|
||||||
assert(rsio.digital_write_active(IO.F_ALARM, true) == IO_LVL.HIGH, "IO_F_ALARM_HIGH")
|
if rsio.get_io_mode(i) == IO_MODE.DIGITAL_IN or rsio.get_io_mode(i) == IO_MODE.DIGITAL_OUT then
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.HIGH, "IO_WASTE_PU_HIGH")
|
local high = DIG_INV[i] == nil
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PU, true) == IO_LVL.LOW, "IO_WASTE_PU_LOW")
|
assert(rsio.digital_write_active(i, not high) == IO_LVL.LOW, "IO_" .. rsio.to_string(i) .. "_LOW")
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.HIGH, "IO_WASTE_PO_HIGH")
|
assert(rsio.digital_write_active(i, high) == IO_LVL.HIGH, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
||||||
assert(rsio.digital_write_active(IO.WASTE_PO, true) == IO_LVL.LOW, "IO_WASTE_PO_LOW")
|
end
|
||||||
assert(rsio.digital_write_active(IO.WASTE_POPL, true) == IO_LVL.HIGH, "IO_WASTE_POPL_HIGH")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_POPL, true) == IO_LVL.LOW, "IO_WASTE_POPL_LOW")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_AM, true) == IO_LVL.HIGH, "IO_WASTE_AM_HIGH")
|
|
||||||
assert(rsio.digital_write_active(IO.WASTE_AM, true) == IO_LVL.LOW, "IO_WASTE_AM_LOW")
|
|
||||||
|
|
||||||
-- check all reactor output ports (all are active high)
|
|
||||||
for i = IO.R_ALARM, (IO.R_PLC_TIMEOUT - IO.R_ALARM + 1) do
|
|
||||||
assert(rsio.to_string(i) ~= "", "REACTOR_IO_BAD_PORT")
|
|
||||||
assert(rsio.digital_write_active(i, false) == IO_LVL.LOW, "IO_" .. rsio.to_string(i) .. "_LOW")
|
|
||||||
assert(rsio.digital_write_active(i, true) == IO_LVL.HIGH, "IO_" .. rsio.to_string(i) .. "_HIGH")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- non-outputs should always return false
|
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, true) == false, "ANA_DIG_WRITE_TRUE")
|
||||||
assert(rsio.digital_write_active(IO.F_SCRAM, false) == IO_LVL.LOW, "IO_IN_WRITE_FALSE")
|
assert(rsio.digital_write_active(IO.F_MATRIX_CHG, false) == false, "ANA_DIG_WRITE_FALSE")
|
||||||
assert(rsio.digital_write_active(IO.F_SCRAM, true) == IO_LVL.LOW, "IO_IN_WRITE_TRUE")
|
|
||||||
|
|
||||||
println("PASS")
|
println("PASS")
|
||||||
|
|
||||||
|
|||||||
56
test/watch_psil_allocs.lua
Normal file
56
test/watch_psil_allocs.lua
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
-- add this to psil:
|
||||||
|
|
||||||
|
--[[
|
||||||
|
-- count the number of subscribers in this PSIL instance
|
||||||
|
---@return integer count
|
||||||
|
function public.count()
|
||||||
|
local c = 0
|
||||||
|
for _, val in pairs(ic) do
|
||||||
|
for _ = 1, #val.subscribers do c = c + 1 end
|
||||||
|
end
|
||||||
|
return c
|
||||||
|
end
|
||||||
|
]]--
|
||||||
|
|
||||||
|
|
||||||
|
-- add this to coordinator iocontrol front panel heartbeat function:
|
||||||
|
|
||||||
|
--[[
|
||||||
|
if io.facility then
|
||||||
|
local count = io.facility.ps.count()
|
||||||
|
|
||||||
|
count = count + io.facility.env_d_ps.count()
|
||||||
|
|
||||||
|
for x = 1, #io.facility.induction_ps_tbl do
|
||||||
|
count = count + io.facility.induction_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = 1, #io.facility.sps_ps_tbl do
|
||||||
|
count = count + io.facility.sps_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = 1, #io.facility.tank_ps_tbl do
|
||||||
|
count = count + io.facility.tank_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #io.units do
|
||||||
|
local entry = io.units[i] ---@type ioctl_unit
|
||||||
|
|
||||||
|
count = count + entry.unit_ps.count()
|
||||||
|
|
||||||
|
for x = 1, #entry.boiler_ps_tbl do
|
||||||
|
count = count + entry.boiler_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = 1, #entry.turbine_ps_tbl do
|
||||||
|
count = count + entry.turbine_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
|
||||||
|
for x = 1, #entry.tank_ps_tbl do
|
||||||
|
count = count + entry.tank_ps_tbl[x].count()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
log.debug(count)
|
||||||
|
end
|
||||||
|
]]--
|
||||||
Reference in New Issue
Block a user