mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#183 RTU front panel
This commit is contained in:
parent
df45f6c984
commit
d143015cc7
@ -20,7 +20,7 @@ local sounder = require("coordinator.sounder")
|
|||||||
|
|
||||||
local apisessions = require("coordinator.session.apisessions")
|
local apisessions = require("coordinator.session.apisessions")
|
||||||
|
|
||||||
local COORDINATOR_VERSION = "v0.13.3"
|
local COORDINATOR_VERSION = "v0.13.4"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
@ -451,19 +451,13 @@ function element.new(args)
|
|||||||
function public.show()
|
function public.show()
|
||||||
protected.window.setVisible(true)
|
protected.window.setVisible(true)
|
||||||
protected.start_anim()
|
protected.start_anim()
|
||||||
|
for _, child in pairs(self.children) do child.show() end
|
||||||
for i = 1, #self.children do
|
|
||||||
self.children[i].show()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- hide the element
|
-- hide the element
|
||||||
function public.hide()
|
function public.hide()
|
||||||
protected.stop_anim()
|
protected.stop_anim()
|
||||||
for i = 1, #self.children do
|
for _, child in pairs(self.children) do child.hide() end
|
||||||
self.children[i].hide()
|
|
||||||
end
|
|
||||||
|
|
||||||
protected.window.setVisible(false)
|
protected.window.setVisible(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ local function indicator_led(args)
|
|||||||
args.height = 1
|
args.height = 1
|
||||||
|
|
||||||
-- determine width
|
-- determine width
|
||||||
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2
|
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
|
||||||
|
|
||||||
-- flasher state
|
-- flasher state
|
||||||
local flash_on = true
|
local flash_on = true
|
||||||
@ -89,8 +89,10 @@ local function indicator_led(args)
|
|||||||
|
|
||||||
-- write label and initial indicator light
|
-- write label and initial indicator light
|
||||||
e.on_update(false)
|
e.on_update(false)
|
||||||
|
if string.len(args.label) > 0 then
|
||||||
e.window.setCursorPos(3, 1)
|
e.window.setCursorPos(3, 1)
|
||||||
e.window.write(args.label)
|
e.window.write(args.label)
|
||||||
|
end
|
||||||
|
|
||||||
return e.get()
|
return e.get()
|
||||||
end
|
end
|
||||||
|
@ -37,7 +37,7 @@ local function indicator_led_pair(args)
|
|||||||
args.height = 1
|
args.height = 1
|
||||||
|
|
||||||
-- determine width
|
-- determine width
|
||||||
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2
|
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
|
||||||
|
|
||||||
-- flasher state
|
-- flasher state
|
||||||
local flash_on = true
|
local flash_on = true
|
||||||
@ -103,8 +103,10 @@ local function indicator_led_pair(args)
|
|||||||
|
|
||||||
-- write label and initial indicator light
|
-- write label and initial indicator light
|
||||||
e.on_update(1)
|
e.on_update(1)
|
||||||
|
if string.len(args.label) > 0 then
|
||||||
e.window.setCursorPos(3, 1)
|
e.window.setCursorPos(3, 1)
|
||||||
e.window.write(args.label)
|
e.window.write(args.label)
|
||||||
|
end
|
||||||
|
|
||||||
return e.get()
|
return e.get()
|
||||||
end
|
end
|
||||||
|
@ -24,7 +24,7 @@ local function indicator_led_rgb(args)
|
|||||||
args.height = 1
|
args.height = 1
|
||||||
|
|
||||||
-- determine width
|
-- determine width
|
||||||
args.width = math.max(args.min_label_width or 1, string.len(args.label)) + 2
|
args.width = math.max(args.min_label_width or 0, string.len(args.label)) + 2
|
||||||
|
|
||||||
-- create new graphics element base object
|
-- create new graphics element base object
|
||||||
local e = element.new(args)
|
local e = element.new(args)
|
||||||
@ -48,8 +48,10 @@ local function indicator_led_rgb(args)
|
|||||||
|
|
||||||
-- write label and initial indicator light
|
-- write label and initial indicator light
|
||||||
e.on_update(1)
|
e.on_update(1)
|
||||||
|
if string.len(args.label) > 0 then
|
||||||
e.window.setCursorPos(3, 1)
|
e.window.setCursorPos(3, 1)
|
||||||
e.window.write(args.label)
|
e.window.write(args.label)
|
||||||
|
end
|
||||||
|
|
||||||
return e.get()
|
return e.get()
|
||||||
end
|
end
|
||||||
|
4
imgen.py
4
imgen.py
@ -70,7 +70,7 @@ def make_manifest(size):
|
|||||||
},
|
},
|
||||||
"depends" : {
|
"depends" : {
|
||||||
"reactor-plc" : [ "system", "common", "graphics" ],
|
"reactor-plc" : [ "system", "common", "graphics" ],
|
||||||
"rtu" : [ "system", "common" ],
|
"rtu" : [ "system", "common", "graphics" ],
|
||||||
"supervisor" : [ "system", "common" ],
|
"supervisor" : [ "system", "common" ],
|
||||||
"coordinator" : [ "system", "common", "graphics" ],
|
"coordinator" : [ "system", "common", "graphics" ],
|
||||||
"pocket" : [ "system", "common", "graphics" ]
|
"pocket" : [ "system", "common", "graphics" ]
|
||||||
@ -108,7 +108,7 @@ f = open("install_manifest.json", "w")
|
|||||||
json.dump(final_manifest, f)
|
json.dump(final_manifest, f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
if sys.argv[1] == "shields":
|
if len(sys.argv) > 1 and sys.argv[1] == "shields":
|
||||||
# write all the JSON files for shields.io
|
# write all the JSON files for shields.io
|
||||||
for key, version in final_manifest["versions"].items():
|
for key, version in final_manifest["versions"].items():
|
||||||
f = open("./shields/" + key + ".json", "w")
|
f = open("./shields/" + key + ".json", "w")
|
||||||
|
File diff suppressed because one or more lines are too long
@ -17,7 +17,7 @@ local coreio = require("pocket.coreio")
|
|||||||
local pocket = require("pocket.pocket")
|
local pocket = require("pocket.pocket")
|
||||||
local renderer = require("pocket.renderer")
|
local renderer = require("pocket.renderer")
|
||||||
|
|
||||||
local POCKET_VERSION = "alpha-v0.2.2"
|
local POCKET_VERSION = "alpha-v0.2.3"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
@ -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.1.10"
|
local R_PLC_VERSION = "v1.1.11"
|
||||||
|
|
||||||
local println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
|
75
rtu/databus.lua
Normal file
75
rtu/databus.lua
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
--
|
||||||
|
-- Data Bus - Central Communication Linking for RTU Front Panel
|
||||||
|
--
|
||||||
|
|
||||||
|
local psil = require("scada-common.psil")
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local databus = {}
|
||||||
|
|
||||||
|
local dbus_iface = {
|
||||||
|
ps = psil.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
---@enum RTU_UNIT_HW_STATE
|
||||||
|
local RTU_UNIT_HW_STATE = {
|
||||||
|
OFFLINE = 1,
|
||||||
|
FAULTED = 2,
|
||||||
|
UNFORMED = 3,
|
||||||
|
OK = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
databus.RTU_UNIT_HW_STATE = RTU_UNIT_HW_STATE
|
||||||
|
|
||||||
|
-- call to toggle heartbeat signal
|
||||||
|
function databus.heartbeat() dbus_iface.ps.toggle("heartbeat") end
|
||||||
|
|
||||||
|
-- transmit firmware versions across the bus
|
||||||
|
---@param rtu_v string RTU version
|
||||||
|
---@param comms_v string comms version
|
||||||
|
function databus.tx_versions(rtu_v, comms_v)
|
||||||
|
dbus_iface.ps.publish("version", rtu_v)
|
||||||
|
dbus_iface.ps.publish("comms_version", comms_v)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transmit hardware status for modem connection state
|
||||||
|
---@param has_modem boolean
|
||||||
|
function databus.tx_hw_modem(has_modem)
|
||||||
|
dbus_iface.ps.publish("has_modem", has_modem)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transmit unit hardware type across the bus
|
||||||
|
---@param uid integer unit ID
|
||||||
|
---@param type RTU_UNIT_TYPE
|
||||||
|
function databus.tx_unit_hw_type(uid, type)
|
||||||
|
dbus_iface.ps.publish("unit_type_" .. uid, type)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transmit unit hardware status across the bus
|
||||||
|
---@param uid integer unit ID
|
||||||
|
---@param status RTU_UNIT_HW_STATE
|
||||||
|
function databus.tx_unit_hw_status(uid, status)
|
||||||
|
dbus_iface.ps.publish("unit_hw_" .. uid, status)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transmit thread (routine) statuses
|
||||||
|
---@param thread string thread name
|
||||||
|
---@param ok boolean thread state
|
||||||
|
function databus.tx_rt_status(thread, ok)
|
||||||
|
dbus_iface.ps.publish(util.c("routine__", thread), ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- transmit supervisor link state across the bus
|
||||||
|
---@param state integer
|
||||||
|
function databus.tx_link_state(state)
|
||||||
|
dbus_iface.ps.publish("link_state", state)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- link a function to receive data from the bus
|
||||||
|
---@param field string field name
|
||||||
|
---@param func function function to link
|
||||||
|
function databus.rx_field(field, func)
|
||||||
|
dbus_iface.ps.subscribe(field, func)
|
||||||
|
end
|
||||||
|
|
||||||
|
return databus
|
123
rtu/panel/front_panel.lua
Normal file
123
rtu/panel/front_panel.lua
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
--
|
||||||
|
-- Main SCADA Coordinator GUI
|
||||||
|
--
|
||||||
|
|
||||||
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
|
local databus = require("rtu.databus")
|
||||||
|
|
||||||
|
local style = require("rtu.panel.style")
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local Div = require("graphics.elements.div")
|
||||||
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local LED = require("graphics.elements.indicators.led")
|
||||||
|
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||||
|
|
||||||
|
local TEXT_ALIGN = core.graphics.TEXT_ALIGN
|
||||||
|
|
||||||
|
local cpair = core.graphics.cpair
|
||||||
|
|
||||||
|
local UNIT_TYPE_LABELS = {
|
||||||
|
"UNKNOWN",
|
||||||
|
"REDSTONE",
|
||||||
|
"BOILER",
|
||||||
|
"TURBINE",
|
||||||
|
"IND MATRIX",
|
||||||
|
"SPS",
|
||||||
|
"SNA",
|
||||||
|
"ENV DETECTOR"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
-- create new main view
|
||||||
|
---@param panel table main displaybox
|
||||||
|
---@param units table unit list
|
||||||
|
local function init(panel, units)
|
||||||
|
TextBox{parent=panel,y=1,text="RTU GATEWAY",alignment=TEXT_ALIGN.CENTER,height=1,fg_bg=style.header}
|
||||||
|
|
||||||
|
--
|
||||||
|
-- system indicators
|
||||||
|
--
|
||||||
|
|
||||||
|
local system = Div{parent=panel,width=14,height=18,x=2,y=3}
|
||||||
|
|
||||||
|
local on = LED{parent=system,label="POWER",colors=cpair(colors.green,colors.red)}
|
||||||
|
local heartbeat = LED{parent=system,label="HEARTBEAT",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
on.update(true)
|
||||||
|
system.line_break()
|
||||||
|
|
||||||
|
databus.rx_field("heartbeat", heartbeat.update)
|
||||||
|
|
||||||
|
local modem = LED{parent=system,label="MODEM",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
local network = RGBLED{parent=system,label="NETWORK",colors={colors.green,colors.red,colors.orange,colors.yellow,colors.gray}}
|
||||||
|
network.update(5)
|
||||||
|
system.line_break()
|
||||||
|
|
||||||
|
databus.rx_field("has_modem", modem.update)
|
||||||
|
databus.rx_field("link_state", network.update)
|
||||||
|
|
||||||
|
local rt_main = LED{parent=system,label="RT MAIN",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
local rt_comm = LED{parent=system,label="RT COMMS",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
system.line_break()
|
||||||
|
|
||||||
|
databus.rx_field("routine__main", rt_main.update)
|
||||||
|
databus.rx_field("routine__comms", rt_comm.update)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- about label
|
||||||
|
--
|
||||||
|
|
||||||
|
local about = Div{parent=panel,width=15,height=3,x=1,y=18,fg_bg=cpair(colors.lightGray,colors.ivory)}
|
||||||
|
local fw_v = TextBox{parent=about,x=1,y=1,text="FW: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
|
||||||
|
local comms_v = TextBox{parent=about,x=1,y=2,text="NT: v00.00.00",alignment=TEXT_ALIGN.LEFT,height=1}
|
||||||
|
|
||||||
|
databus.rx_field("version", function (version) fw_v.set_value(util.c("FW: ", version)) end)
|
||||||
|
databus.rx_field("comms_version", function (version) comms_v.set_value(util.c("NT: v", version)) end)
|
||||||
|
|
||||||
|
--
|
||||||
|
-- unit status list
|
||||||
|
--
|
||||||
|
|
||||||
|
local threads = Div{parent=panel,width=8,height=18,x=17,y=3}
|
||||||
|
|
||||||
|
-- display up to 16 units
|
||||||
|
local list_length = math.min(#units, 16)
|
||||||
|
|
||||||
|
-- show routine statuses
|
||||||
|
for i = 1, list_length do
|
||||||
|
TextBox{parent=threads,x=1,y=i,text=util.sprintf("%02d",i),height=1}
|
||||||
|
local rt_unit = LED{parent=threads,x=4,y=i,label="RT",colors=cpair(colors.green,colors.green_off)}
|
||||||
|
databus.rx_field("routine__unit_" .. i, rt_unit.update)
|
||||||
|
end
|
||||||
|
|
||||||
|
local unit_hw_statuses = Div{parent=panel,height=18,x=25,y=3}
|
||||||
|
|
||||||
|
-- show hardware statuses
|
||||||
|
for i = 1, list_length do
|
||||||
|
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||||
|
|
||||||
|
-- hardware status
|
||||||
|
local unit_hw = RGBLED{parent=unit_hw_statuses,y=i,label="",colors={colors.red,colors.orange,colors.yellow,colors.green}}
|
||||||
|
|
||||||
|
databus.rx_field("unit_hw_" .. i, unit_hw.update)
|
||||||
|
|
||||||
|
-- unit name identifier (type + index)
|
||||||
|
local name = util.c(UNIT_TYPE_LABELS[unit.type + 1], " ", unit.index)
|
||||||
|
local name_box = TextBox{parent=unit_hw_statuses,y=i,x=3,text=name,height=1}
|
||||||
|
|
||||||
|
databus.rx_field("unit_type_" .. i, function (t)
|
||||||
|
name_box.set_value(util.c(UNIT_TYPE_LABELS[t + 1], " ", unit.index))
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- assignment (unit # or facility)
|
||||||
|
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=cpair(colors.lightGray,colors.ivory)}
|
||||||
|
end
|
||||||
|
|
||||||
|
return panel
|
||||||
|
end
|
||||||
|
|
||||||
|
return init
|
41
rtu/panel/style.lua
Normal file
41
rtu/panel/style.lua
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
--
|
||||||
|
-- Graphics Style Options
|
||||||
|
--
|
||||||
|
|
||||||
|
local core = require("graphics.core")
|
||||||
|
|
||||||
|
local style = {}
|
||||||
|
|
||||||
|
local cpair = core.graphics.cpair
|
||||||
|
|
||||||
|
-- GLOBAL --
|
||||||
|
|
||||||
|
-- remap global colors
|
||||||
|
colors.ivory = colors.pink
|
||||||
|
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 },
|
||||||
|
{ 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
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
81
rtu/renderer.lua
Normal file
81
rtu/renderer.lua
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
--
|
||||||
|
-- Graphics Rendering Control
|
||||||
|
--
|
||||||
|
|
||||||
|
local panel_view = require("rtu.panel.front_panel")
|
||||||
|
local style = require("rtu.panel.style")
|
||||||
|
|
||||||
|
local flasher = require("graphics.flasher")
|
||||||
|
|
||||||
|
local DisplayBox = require("graphics.elements.displaybox")
|
||||||
|
|
||||||
|
local renderer = {}
|
||||||
|
|
||||||
|
local ui = {
|
||||||
|
display = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
-- start the UI
|
||||||
|
---@param units table RTU units
|
||||||
|
function renderer.start_ui(units)
|
||||||
|
if ui.display == nil then
|
||||||
|
-- reset terminal
|
||||||
|
term.setTextColor(colors.white)
|
||||||
|
term.setBackgroundColor(colors.black)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
|
||||||
|
-- set overridden colors
|
||||||
|
for i = 1, #style.colors do
|
||||||
|
term.setPaletteColor(style.colors[i].c, style.colors[i].hex)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start flasher callback task
|
||||||
|
flasher.run()
|
||||||
|
|
||||||
|
-- init front panel view
|
||||||
|
ui.display = DisplayBox{window=term.current(),fg_bg=style.root}
|
||||||
|
panel_view(ui.display, units)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- close out the UI
|
||||||
|
function renderer.close_ui()
|
||||||
|
-- stop blinking indicators
|
||||||
|
flasher.clear()
|
||||||
|
|
||||||
|
if ui.display ~= nil then
|
||||||
|
-- hide to stop animation callbacks
|
||||||
|
ui.display.hide()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- clear root UI elements
|
||||||
|
ui.display = nil
|
||||||
|
|
||||||
|
-- restore colors
|
||||||
|
for i = 1, #style.colors do
|
||||||
|
local r, g, b = term.nativePaletteColor(style.colors[i].c)
|
||||||
|
term.setPaletteColor(style.colors[i].c, r, g, b)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reset terminal
|
||||||
|
term.setTextColor(colors.white)
|
||||||
|
term.setBackgroundColor(colors.black)
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- is the UI ready?
|
||||||
|
---@nodiscard
|
||||||
|
---@return boolean ready
|
||||||
|
function renderer.ui_ready() return ui.display ~= nil end
|
||||||
|
|
||||||
|
-- handle a mouse event
|
||||||
|
---@param event mouse_interaction
|
||||||
|
function renderer.handle_mouse(event)
|
||||||
|
if ui.display ~= nil then
|
||||||
|
ui.display.handle_mouse(event)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return renderer
|
@ -4,6 +4,7 @@ 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 databus = require("rtu.databus")
|
||||||
local modbus = require("rtu.modbus")
|
local modbus = require("rtu.modbus")
|
||||||
|
|
||||||
local rtu = {}
|
local rtu = {}
|
||||||
@ -14,8 +15,6 @@ local ESTABLISH_ACK = comms.ESTABLISH_ACK
|
|||||||
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE
|
local SCADA_MGMT_TYPE = comms.SCADA_MGMT_TYPE
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
|
|
||||||
local println_ts = util.println_ts
|
|
||||||
|
|
||||||
-- create a new RTU unit
|
-- create a new RTU unit
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
function rtu.init_unit()
|
function rtu.init_unit()
|
||||||
@ -325,6 +324,9 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
|||||||
---@param units table RTU units
|
---@param units table RTU units
|
||||||
---@param rtu_state rtu_state
|
---@param rtu_state rtu_state
|
||||||
function public.handle_packet(packet, units, rtu_state)
|
function public.handle_packet(packet, units, rtu_state)
|
||||||
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
|
local function println_ts(message) if not rtu_state.fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
if packet.scada_frame.local_port() == local_port then
|
if packet.scada_frame.local_port() == local_port then
|
||||||
-- check sequence number
|
-- check sequence number
|
||||||
if self.r_seq_num == nil then
|
if self.r_seq_num == nil then
|
||||||
@ -416,6 +418,9 @@ function rtu.comms(version, modem, local_port, server_port, range, conn_watchdog
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.last_est_ack = est_ack
|
self.last_est_ack = est_ack
|
||||||
|
|
||||||
|
-- report link state
|
||||||
|
databus.tx_link_state(est_ack + 1)
|
||||||
else
|
else
|
||||||
log.debug("SCADA_MGMT establish packet length mismatch")
|
log.debug("SCADA_MGMT establish packet length mismatch")
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
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 mqueue = require("scada-common.mqueue")
|
||||||
@ -13,7 +14,9 @@ local types = require("scada-common.types")
|
|||||||
local util = require("scada-common.util")
|
local util = require("scada-common.util")
|
||||||
|
|
||||||
local config = require("rtu.config")
|
local config = require("rtu.config")
|
||||||
|
local databus = require("rtu.databus")
|
||||||
local modbus = require("rtu.modbus")
|
local modbus = require("rtu.modbus")
|
||||||
|
local renderer = require("rtu.renderer")
|
||||||
local rtu = require("rtu.rtu")
|
local rtu = require("rtu.rtu")
|
||||||
local threads = require("rtu.threads")
|
local threads = require("rtu.threads")
|
||||||
|
|
||||||
@ -25,9 +28,10 @@ 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 = "v0.13.5"
|
local RTU_VERSION = "v1.0.0"
|
||||||
|
|
||||||
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 println = util.println
|
local println = util.println
|
||||||
local println_ts = util.println_ts
|
local println_ts = util.println_ts
|
||||||
@ -71,6 +75,9 @@ local function main()
|
|||||||
-- startup
|
-- startup
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
-- record firmware versions and ID
|
||||||
|
databus.tx_versions(RTU_VERSION, comms.version)
|
||||||
|
|
||||||
-- mount connected devices
|
-- mount connected devices
|
||||||
ppm.mount_all()
|
ppm.mount_all()
|
||||||
|
|
||||||
@ -79,6 +86,7 @@ local function main()
|
|||||||
-- RTU system state flags
|
-- RTU system state flags
|
||||||
---@class rtu_state
|
---@class rtu_state
|
||||||
rtu_state = {
|
rtu_state = {
|
||||||
|
fp_ok = false,
|
||||||
linked = false,
|
linked = false,
|
||||||
shutdown = false
|
shutdown = false
|
||||||
},
|
},
|
||||||
@ -111,6 +119,8 @@ local function main()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_hw_modem(true)
|
||||||
|
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
-- interpret config and init units
|
-- interpret config and init units
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
@ -250,6 +260,8 @@ local function main()
|
|||||||
log.info(util.c("configure> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for ", for_message))
|
log.info(util.c("configure> initialized RTU unit #", #units, ": redstone_io (redstone) [1] for ", for_message))
|
||||||
|
|
||||||
unit.uid = #units
|
unit.uid = #units
|
||||||
|
|
||||||
|
databus.tx_unit_hw_status(unit.uid, RTU_UNIT_HW_STATE.OK)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -409,6 +421,20 @@ local function main()
|
|||||||
log.info(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
|
log.info(util.c("configure> initialized RTU unit #", #units, ": ", name, " (", types.rtu_type_to_string(rtu_type), ") [", index, "] for ", for_message))
|
||||||
|
|
||||||
rtu_unit.uid = #units
|
rtu_unit.uid = #units
|
||||||
|
|
||||||
|
-- report hardware status
|
||||||
|
if rtu_unit.type == RTU_UNIT_TYPE.VIRTUAL then
|
||||||
|
databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.OFFLINE)
|
||||||
|
else
|
||||||
|
if rtu_unit.is_multiblock then
|
||||||
|
databus.tx_unit_hw_status(rtu_unit.uid, util.trinary(rtu_unit.formed == true, RTU_UNIT_HW_STATE.OK, RTU_UNIT_HW_STATE.UNFORMED))
|
||||||
|
elseif faulted then
|
||||||
|
databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.FAULTED)
|
||||||
|
else
|
||||||
|
databus.tx_unit_hw_status(rtu_unit.uid, RTU_UNIT_HW_STATE.OK)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- we made it through all that trusting-user-to-write-a-config-file chaos
|
-- we made it through all that trusting-user-to-write-a-config-file chaos
|
||||||
@ -419,9 +445,23 @@ local function main()
|
|||||||
-- start system
|
-- start system
|
||||||
----------------------------------------
|
----------------------------------------
|
||||||
|
|
||||||
|
local rtu_state = __shared_memory.rtu_state
|
||||||
|
|
||||||
log.debug("boot> running configure()")
|
log.debug("boot> running configure()")
|
||||||
|
|
||||||
if configure() then
|
if configure() then
|
||||||
|
-- start UI
|
||||||
|
local message
|
||||||
|
rtu_state.fp_ok, message = pcall(renderer.start_ui, units)
|
||||||
|
|
||||||
|
if not rtu_state.fp_ok then
|
||||||
|
renderer.close_ui()
|
||||||
|
println_ts(util.c("UI error: ", message))
|
||||||
|
println("init> running without front panel")
|
||||||
|
log.error(util.c("GUI crashed with error ", message))
|
||||||
|
log.info("init> running in headless mode without front panel")
|
||||||
|
end
|
||||||
|
|
||||||
-- start connection watchdog
|
-- start connection watchdog
|
||||||
smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT)
|
smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT)
|
||||||
log.debug("startup> conn watchdog started")
|
log.debug("startup> conn watchdog started")
|
||||||
@ -451,6 +491,8 @@ local function main()
|
|||||||
println("configuration failed, exiting...")
|
println("configuration failed, exiting...")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
renderer.close_ui()
|
||||||
|
|
||||||
println_ts("exited")
|
println_ts("exited")
|
||||||
log.info("exited")
|
log.info("exited")
|
||||||
end
|
end
|
||||||
|
@ -4,6 +4,10 @@ local ppm = require("scada-common.ppm")
|
|||||||
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 databus = require("rtu.databus")
|
||||||
|
local modbus = require("rtu.modbus")
|
||||||
|
local renderer = require("rtu.renderer")
|
||||||
|
|
||||||
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
|
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
|
||||||
local envd_rtu = require("rtu.dev.envd_rtu")
|
local envd_rtu = require("rtu.dev.envd_rtu")
|
||||||
local imatrix_rtu = require("rtu.dev.imatrix_rtu")
|
local imatrix_rtu = require("rtu.dev.imatrix_rtu")
|
||||||
@ -11,13 +15,12 @@ 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 modbus = require("rtu.modbus")
|
local core = require("graphics.core")
|
||||||
|
|
||||||
local threads = {}
|
local threads = {}
|
||||||
|
|
||||||
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
local RTU_UNIT_TYPE = types.RTU_UNIT_TYPE
|
||||||
|
local UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
local println_ts = util.println_ts
|
|
||||||
|
|
||||||
local MAIN_CLOCK = 2 -- (2Hz, 40 ticks)
|
local MAIN_CLOCK = 2 -- (2Hz, 40 ticks)
|
||||||
local COMMS_SLEEP = 100 -- (100ms, 2 ticks)
|
local COMMS_SLEEP = 100 -- (100ms, 2 ticks)
|
||||||
@ -26,11 +29,15 @@ local COMMS_SLEEP = 100 -- (100ms, 2 ticks)
|
|||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param smem rtu_shared_memory
|
---@param smem rtu_shared_memory
|
||||||
function threads.thread__main(smem)
|
function threads.thread__main(smem)
|
||||||
|
-- print a log message to the terminal as long as the UI isn't running
|
||||||
|
local function println_ts(message) if not smem.rtu_state.fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
---@class parallel_thread
|
---@class parallel_thread
|
||||||
local public = {}
|
local public = {}
|
||||||
|
|
||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
|
databus.tx_rt_status("main", true)
|
||||||
log.debug("main thread start")
|
log.debug("main thread start")
|
||||||
|
|
||||||
-- main loop clock
|
-- main loop clock
|
||||||
@ -54,6 +61,9 @@ function threads.thread__main(smem)
|
|||||||
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
local event, param1, param2, param3, param4, param5 = util.pull_event()
|
||||||
|
|
||||||
if event == "timer" and loop_clock.is_clock(param1) then
|
if event == "timer" and loop_clock.is_clock(param1) then
|
||||||
|
-- blink heartbeat indicator
|
||||||
|
databus.heartbeat()
|
||||||
|
|
||||||
-- start next clock timer
|
-- start next clock timer
|
||||||
loop_clock.start()
|
loop_clock.start()
|
||||||
|
|
||||||
@ -82,6 +92,8 @@ function threads.thread__main(smem)
|
|||||||
if device == rtu_dev.modem then
|
if device == rtu_dev.modem then
|
||||||
println_ts("wireless modem disconnected!")
|
println_ts("wireless modem disconnected!")
|
||||||
log.warning("comms modem disconnected!")
|
log.warning("comms modem disconnected!")
|
||||||
|
|
||||||
|
databus.tx_hw_modem(false)
|
||||||
else
|
else
|
||||||
log.warning("non-comms modem disconnected")
|
log.warning("non-comms modem disconnected")
|
||||||
end
|
end
|
||||||
@ -91,10 +103,11 @@ function threads.thread__main(smem)
|
|||||||
if units[i].device == device then
|
if units[i].device == device then
|
||||||
-- we are going to let the PPM prevent crashes
|
-- we are going to let the PPM prevent crashes
|
||||||
-- return fault flags/codes to MODBUS queries
|
-- return fault flags/codes to MODBUS queries
|
||||||
local unit = units[i]
|
local unit = units[i] ---@type rtu_unit_registry_entry
|
||||||
local type_name = types.rtu_type_to_string(unit.type)
|
local type_name = types.rtu_type_to_string(unit.type)
|
||||||
println_ts(util.c("lost the ", type_name, " on interface ", unit.name))
|
println_ts(util.c("lost the ", type_name, " on interface ", unit.name))
|
||||||
log.warning(util.c("lost the ", type_name, " unit peripheral on interface ", unit.name))
|
log.warning(util.c("lost the ", type_name, " unit peripheral on interface ", unit.name))
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OFFLINE)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -113,6 +126,8 @@ function threads.thread__main(smem)
|
|||||||
|
|
||||||
println_ts("wireless modem reconnected.")
|
println_ts("wireless modem reconnected.")
|
||||||
log.info("comms modem reconnected")
|
log.info("comms modem reconnected")
|
||||||
|
|
||||||
|
databus.tx_hw_modem(true)
|
||||||
else
|
else
|
||||||
log.info("wired modem reconnected")
|
log.info("wired modem reconnected")
|
||||||
end
|
end
|
||||||
@ -153,34 +168,49 @@ function threads.thread__main(smem)
|
|||||||
resend_advert = false
|
resend_advert = false
|
||||||
log.error(util.c("virtual device '", unit.name, "' cannot init to an unknown type (", type, ")"))
|
log.error(util.c("virtual device '", unit.name, "' cannot init to an unknown type (", type, ")"))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_unit_hw_type(unit.uid, unit.type)
|
||||||
end
|
end
|
||||||
|
|
||||||
if unit.type == RTU_UNIT_TYPE.BOILER_VALVE then
|
if unit.type == RTU_UNIT_TYPE.BOILER_VALVE then
|
||||||
unit.rtu = boilerv_rtu.new(device)
|
unit.rtu = boilerv_rtu.new(device)
|
||||||
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
elseif unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then
|
elseif unit.type == RTU_UNIT_TYPE.TURBINE_VALVE then
|
||||||
unit.rtu = turbinev_rtu.new(device)
|
unit.rtu = turbinev_rtu.new(device)
|
||||||
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
elseif unit.type == RTU_UNIT_TYPE.IMATRIX then
|
elseif unit.type == RTU_UNIT_TYPE.IMATRIX then
|
||||||
unit.rtu = imatrix_rtu.new(device)
|
unit.rtu = imatrix_rtu.new(device)
|
||||||
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
elseif unit.type == RTU_UNIT_TYPE.SPS then
|
elseif unit.type == RTU_UNIT_TYPE.SPS then
|
||||||
unit.rtu = sps_rtu.new(device)
|
unit.rtu = sps_rtu.new(device)
|
||||||
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
-- if not formed, indexing the multiblock functions would have resulted in a PPM fault
|
||||||
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
unit.formed = util.trinary(device.__p_is_faulted(), false, nil)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
elseif unit.type == RTU_UNIT_TYPE.SNA then
|
elseif unit.type == RTU_UNIT_TYPE.SNA then
|
||||||
unit.rtu = sna_rtu.new(device)
|
unit.rtu = sna_rtu.new(device)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
||||||
elseif unit.type == RTU_UNIT_TYPE.ENV_DETECTOR then
|
elseif unit.type == RTU_UNIT_TYPE.ENV_DETECTOR then
|
||||||
unit.rtu = envd_rtu.new(device)
|
unit.rtu = envd_rtu.new(device)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
||||||
else
|
else
|
||||||
log.error(util.c("failed to identify reconnected RTU unit type (", unit.name, ")"), true)
|
log.error(util.c("failed to identify reconnected RTU unit type (", unit.name, ")"), true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if unit.is_multiblock and (unit.formed == false) then
|
if unit.is_multiblock then
|
||||||
|
if (unit.formed == false) then
|
||||||
log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing"))
|
log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing"))
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
|
end
|
||||||
|
elseif device.__p_is_faulted() then
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.FAULTED)
|
||||||
|
else
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
||||||
end
|
end
|
||||||
|
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
unit.modbus_io = modbus.new(unit.rtu, true)
|
||||||
@ -199,6 +229,9 @@ function threads.thread__main(smem)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
elseif event == "mouse_click" then
|
||||||
|
-- handle a monitor touch event
|
||||||
|
renderer.handle_mouse(core.events.click(param1, param2, param3))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
@ -220,6 +253,8 @@ function threads.thread__main(smem)
|
|||||||
log.fatal(util.strval(result))
|
log.fatal(util.strval(result))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_rt_status("main", false)
|
||||||
|
|
||||||
if not rtu_state.shutdown then
|
if not rtu_state.shutdown then
|
||||||
log.info("main thread restarting in 5 seconds...")
|
log.info("main thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
@ -239,6 +274,7 @@ function threads.thread__comms(smem)
|
|||||||
|
|
||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
|
databus.tx_rt_status("comms", true)
|
||||||
log.debug("comms thread start")
|
log.debug("comms thread start")
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
@ -294,6 +330,8 @@ function threads.thread__comms(smem)
|
|||||||
log.fatal(util.strval(result))
|
log.fatal(util.strval(result))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_rt_status("comms", false)
|
||||||
|
|
||||||
if not rtu_state.shutdown then
|
if not rtu_state.shutdown then
|
||||||
log.info("comms thread restarting in 5 seconds...")
|
log.info("comms thread restarting in 5 seconds...")
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
@ -314,7 +352,8 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
|
|
||||||
-- execute thread
|
-- execute thread
|
||||||
function public.exec()
|
function public.exec()
|
||||||
log.debug(util.c("rtu unit thread start -> ", types.rtu_type_to_string(unit.type), "(", unit.name, ")"))
|
databus.tx_rt_status("unit_" .. unit.uid, true)
|
||||||
|
log.debug(util.c("rtu unit thread start -> ", types.rtu_type_to_string(unit.type), " (", unit.name, ")"))
|
||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local rtu_state = smem.rtu_state
|
local rtu_state = smem.rtu_state
|
||||||
@ -348,6 +387,13 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
-- received a packet
|
-- received a packet
|
||||||
local _, reply = unit.modbus_io.handle_packet(msg.message)
|
local _, reply = unit.modbus_io.handle_packet(msg.message)
|
||||||
rtu_comms.send_modbus(reply)
|
rtu_comms.send_modbus(reply)
|
||||||
|
|
||||||
|
-- check if there was a problem and update the hardware state if so
|
||||||
|
local frame = reply.get()
|
||||||
|
if unit.formed and (bit.band(frame.func_code, types.MODBUS_FCODE.ERROR_FLAG) ~= 0) and
|
||||||
|
(frame.data[1] == types.MODBUS_EXCODE.SERVER_DEVICE_FAIL) then
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.FAULTED)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -361,7 +407,14 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
|
|
||||||
last_f_check = util.time_ms()
|
last_f_check = util.time_ms()
|
||||||
|
|
||||||
if unit.formed == nil then unit.formed = is_formed end
|
if unit.formed == nil then
|
||||||
|
unit.formed = is_formed
|
||||||
|
if is_formed then databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK) end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not unit.formed then
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
|
end
|
||||||
|
|
||||||
if (not unit.formed) and is_formed then
|
if (not unit.formed) and is_formed then
|
||||||
-- newly re-formed
|
-- newly re-formed
|
||||||
@ -400,21 +453,25 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
unit.formed = device.isFormed()
|
unit.formed = device.isFormed()
|
||||||
unit.modbus_io = modbus.new(unit.rtu, true)
|
unit.modbus_io = modbus.new(unit.rtu, true)
|
||||||
else
|
else
|
||||||
log.error("illegal remount of non-multiblock RTU attempted for " .. short_name, true)
|
log.error("illegal remount of non-multiblock RTU or type change attempted for " .. short_name, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
if unit.formed and faulted then
|
if unit.formed and faulted then
|
||||||
-- something is still wrong = can't mark as formed yet
|
-- something is still wrong = can't mark as formed yet
|
||||||
unit.formed = false
|
unit.formed = false
|
||||||
|
log.info(util.c("assuming ", unit.name, " is not formed due to PPM faults while initializing"))
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.UNFORMED)
|
||||||
else
|
else
|
||||||
rtu_comms.send_remounted(unit.uid)
|
rtu_comms.send_remounted(unit.uid)
|
||||||
|
databus.tx_unit_hw_status(unit.uid, UNIT_HW_STATE.OK)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local type_name = types.rtu_type_to_string(unit.type)
|
||||||
|
log.info(util.c("reconnected the ", type_name, " on interface ", unit.name))
|
||||||
else
|
else
|
||||||
-- fully lost the peripheral now :(
|
-- fully lost the peripheral now :(
|
||||||
log.error(util.c(unit.name, " lost (failed reconnect)"))
|
log.error(util.c(unit.name, " lost (failed reconnect)"))
|
||||||
end
|
end
|
||||||
|
|
||||||
log.info(util.c("reconnected the ", unit.type, " on interface ", unit.name))
|
|
||||||
else
|
else
|
||||||
log.error("failed to get interface of previously connected RTU unit " .. detail_name, true)
|
log.error("failed to get interface of previously connected RTU unit " .. detail_name, true)
|
||||||
end
|
end
|
||||||
@ -444,8 +501,10 @@ function threads.thread__unit_comms(smem, unit)
|
|||||||
log.fatal(util.strval(result))
|
log.fatal(util.strval(result))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
databus.tx_rt_status("unit_" .. unit.uid, false)
|
||||||
|
|
||||||
if not rtu_state.shutdown then
|
if not rtu_state.shutdown then
|
||||||
log.info(util.c("rtu unit thread ", types.rtu_type_to_string(unit.type), "(", unit.name, " restarting in 5 seconds..."))
|
log.info(util.c("rtu unit thread ", types.rtu_type_to_string(unit.type), " (", unit.name, ") restarting in 5 seconds..."))
|
||||||
util.psleep(5)
|
util.psleep(5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user