mirror of
https://github.com/MikaylaFischler/cc-mek-scada.git
synced 2024-08-30 18:22:34 +00:00
#264 WIP RTU alarm sounders
This commit is contained in:
parent
4192ea426c
commit
92d1945bea
@ -15,6 +15,10 @@ config.COMMS_TIMEOUT = 5
|
|||||||
-- all devices on the same network must use the same key
|
-- all devices on the same network must use the same key
|
||||||
-- config.AUTH_KEY = "SCADAfacility123"
|
-- config.AUTH_KEY = "SCADAfacility123"
|
||||||
|
|
||||||
|
-- alarm sounder volume (0.0 to 3.0, 1.0 being standard max volume, this is the option given to to speaker.play())
|
||||||
|
-- note: alarm sine waves are at half saturation, so that multiple will be required to reach full scale
|
||||||
|
config.SOUNDER_VOLUME = 1.0
|
||||||
|
|
||||||
-- log path
|
-- log path
|
||||||
config.LOG_PATH = "/log.txt"
|
config.LOG_PATH = "/log.txt"
|
||||||
-- log mode
|
-- log mode
|
||||||
|
@ -37,6 +37,12 @@ function databus.tx_hw_modem(has_modem)
|
|||||||
databus.ps.publish("has_modem", has_modem)
|
databus.ps.publish("has_modem", has_modem)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- transmit the number of speakers connected
|
||||||
|
---@param count integer
|
||||||
|
function databus.tx_hw_spkr_count(count)
|
||||||
|
databus.ps.publish("speaker_count", count)
|
||||||
|
end
|
||||||
|
|
||||||
-- transmit unit hardware type across the bus
|
-- transmit unit hardware type across the bus
|
||||||
---@param uid integer unit ID
|
---@param uid integer unit ID
|
||||||
---@param type RTU_UNIT_TYPE
|
---@param type RTU_UNIT_TYPE
|
||||||
|
@ -14,6 +14,7 @@ local core = require("graphics.core")
|
|||||||
local Div = require("graphics.elements.div")
|
local Div = require("graphics.elements.div")
|
||||||
local TextBox = require("graphics.elements.textbox")
|
local TextBox = require("graphics.elements.textbox")
|
||||||
|
|
||||||
|
local DataIndicator = require("graphics.elements.indicators.data")
|
||||||
local LED = require("graphics.elements.indicators.led")
|
local LED = require("graphics.elements.indicators.led")
|
||||||
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||||
|
|
||||||
@ -21,17 +22,7 @@ local TEXT_ALIGN = core.TEXT_ALIGN
|
|||||||
|
|
||||||
local cpair = core.cpair
|
local cpair = core.cpair
|
||||||
|
|
||||||
local UNIT_TYPE_LABELS = {
|
local UNIT_TYPE_LABELS = { "UNKNOWN", "REDSTONE", "BOILER", "TURBINE", "DYNAMIC TANK", "IND MATRIX", "SPS", "SNA", "ENV DETECTOR" }
|
||||||
"UNKNOWN",
|
|
||||||
"REDSTONE",
|
|
||||||
"BOILER",
|
|
||||||
"TURBINE",
|
|
||||||
"DYNAMIC TANK",
|
|
||||||
"IND MATRIX",
|
|
||||||
"SPS",
|
|
||||||
"SNA",
|
|
||||||
"ENV DETECTOR"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-- create new front panel view
|
-- create new front panel view
|
||||||
@ -72,6 +63,10 @@ local function init(panel, units)
|
|||||||
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=cpair(colors.lightGray,colors.ivory)}
|
TextBox{parent=system,x=9,y=4,width=6,height=1,text=comp_id,fg_bg=cpair(colors.lightGray,colors.ivory)}
|
||||||
|
|
||||||
|
TextBox{parent=system,x=1,y=14,text="SPEAKERS",height=1,width=8,fg_bg=style.label}
|
||||||
|
local speaker_count = DataIndicator{parent=system,x=10,y=14,label="",format="%3d",value=0,width=3,fg_bg=cpair(colors.gray,colors.white)}
|
||||||
|
speaker_count.register(databus.ps, "speaker_count", speaker_count.update)
|
||||||
|
|
||||||
--
|
--
|
||||||
-- about label
|
-- about label
|
||||||
--
|
--
|
||||||
|
63
rtu/rtu.lua
63
rtu/rtu.lua
@ -1,9 +1,11 @@
|
|||||||
|
local audio = require("scada-common.audio")
|
||||||
local comms = require("scada-common.comms")
|
local comms = require("scada-common.comms")
|
||||||
local ppm = require("scada-common.ppm")
|
local ppm = require("scada-common.ppm")
|
||||||
local log = require("scada-common.log")
|
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 config = require("rtu.config")
|
||||||
local databus = require("rtu.databus")
|
local databus = require("rtu.databus")
|
||||||
local modbus = require("rtu.modbus")
|
local modbus = require("rtu.modbus")
|
||||||
|
|
||||||
@ -155,6 +157,48 @@ function rtu.init_unit()
|
|||||||
return protected
|
return protected
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- create an alarm speaker sounder
|
||||||
|
---@param speaker table device peripheral
|
||||||
|
function rtu.init_sounder(speaker)
|
||||||
|
---@class rtu_speaker_sounder
|
||||||
|
local spkr_ctl = {
|
||||||
|
speaker = speaker,
|
||||||
|
name = ppm.get_iface(speaker),
|
||||||
|
playing = false,
|
||||||
|
stream = audio.new_stream(),
|
||||||
|
play = function () end,
|
||||||
|
stop = function () end,
|
||||||
|
continue = function () end
|
||||||
|
}
|
||||||
|
|
||||||
|
-- continue audio stream if playing
|
||||||
|
function spkr_ctl.continue()
|
||||||
|
if spkr_ctl.playing then
|
||||||
|
if spkr_ctl.speaker ~= nil and spkr_ctl.stream.has_next_block() then
|
||||||
|
local success = spkr_ctl.speaker.playAudio(spkr_ctl.stream.get_next_block(), config.SOUNDER_VOLUME)
|
||||||
|
if not success then log.error(util.c("rtu_sounder(", spkr_ctl.name, "): error playing audio")) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- start audio stream playback
|
||||||
|
function spkr_ctl.play()
|
||||||
|
if not spkr_ctl.playing then
|
||||||
|
spkr_ctl.playing = true
|
||||||
|
return spkr_ctl.continue()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- stop audio stream playback
|
||||||
|
function spkr_ctl.stop()
|
||||||
|
spkr_ctl.playing = false
|
||||||
|
spkr_ctl.speaker.stop()
|
||||||
|
spkr_ctl.stream.stop()
|
||||||
|
end
|
||||||
|
|
||||||
|
return spkr_ctl
|
||||||
|
end
|
||||||
|
|
||||||
-- RTU Communications
|
-- RTU Communications
|
||||||
---@nodiscard
|
---@nodiscard
|
||||||
---@param version string RTU version
|
---@param version string RTU version
|
||||||
@ -312,7 +356,8 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
|
|||||||
---@param packet modbus_frame|mgmt_frame
|
---@param packet modbus_frame|mgmt_frame
|
||||||
---@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)
|
---@param sounders table speaker alarm sounders
|
||||||
|
function public.handle_packet(packet, units, rtu_state, sounders)
|
||||||
-- print a log message to the terminal as long as the UI isn't running
|
-- 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
|
local function println_ts(message) if not rtu_state.fp_ok then util.println_ts(message) end end
|
||||||
|
|
||||||
@ -447,6 +492,22 @@ function rtu.comms(version, nic, rtu_channel, svr_channel, range, conn_watchdog)
|
|||||||
elseif packet.type == SCADA_MGMT_TYPE.RTU_ADVERT then
|
elseif packet.type == SCADA_MGMT_TYPE.RTU_ADVERT then
|
||||||
-- request for capabilities again
|
-- request for capabilities again
|
||||||
public.send_advertisement(units)
|
public.send_advertisement(units)
|
||||||
|
elseif packet.type == SCADA_MGMT_TYPE.RTU_TONE_ALARM then
|
||||||
|
-- alarm tone update from supervisor
|
||||||
|
if (packet.length == 1) and type(packet.data[1] == "table") and (#packet.data[1] == 8) then
|
||||||
|
local states = packet.data[1]
|
||||||
|
|
||||||
|
for i = 1, #sounders do
|
||||||
|
local s = sounders[i] ---@type rtu_speaker_sounder
|
||||||
|
|
||||||
|
-- set tone states
|
||||||
|
for id = 1, #states do s.stream.set_active(id, states[id]) end
|
||||||
|
|
||||||
|
-- re-compute output if needed, then play audio if available
|
||||||
|
if s.stream.is_recompute_needed() then s.stream.compute_buffer() end
|
||||||
|
if s.stream.has_next_block() then s.play() else s.stop() end
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
-- not supported
|
-- not supported
|
||||||
log.debug("received unsupported SCADA_MGMT message type " .. packet.type)
|
log.debug("received unsupported SCADA_MGMT message type " .. packet.type)
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
require("/initenv").init_env()
|
require("/initenv").init_env()
|
||||||
|
|
||||||
|
local audio = require("scada-common.audio")
|
||||||
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")
|
||||||
@ -30,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.5.5"
|
local RTU_VERSION = "v1.6.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 RTU_UNIT_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||||
@ -96,6 +97,9 @@ local function main()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- generate alarm tones
|
||||||
|
audio.generate_tones()
|
||||||
|
|
||||||
---@class rtu_shared_memory
|
---@class rtu_shared_memory
|
||||||
local __shared_memory = {
|
local __shared_memory = {
|
||||||
-- RTU system state flags
|
-- RTU system state flags
|
||||||
@ -106,6 +110,11 @@ local function main()
|
|||||||
shutdown = false
|
shutdown = false
|
||||||
},
|
},
|
||||||
|
|
||||||
|
-- RTU gateway devices (not RTU units)
|
||||||
|
rtu_dev = {
|
||||||
|
sounders = {}
|
||||||
|
},
|
||||||
|
|
||||||
-- system objects
|
-- system objects
|
||||||
rtu_sys = {
|
rtu_sys = {
|
||||||
nic = network.nic(modem),
|
nic = network.nic(modem),
|
||||||
@ -481,6 +490,18 @@ local function main()
|
|||||||
log.info("startup> running in headless mode without front panel")
|
log.info("startup> running in headless mode without front panel")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- find and setup all speakers
|
||||||
|
local speakers = ppm.get_all_devices("speaker")
|
||||||
|
for _, s in pairs(speakers) do
|
||||||
|
local sounder = rtu.init_sounder(s)
|
||||||
|
|
||||||
|
table.insert(__shared_memory.rtu_dev.sounders, sounder)
|
||||||
|
|
||||||
|
log.debug(util.c("startup> added speaker, attached as ", sounder.name))
|
||||||
|
end
|
||||||
|
|
||||||
|
databus.tx_hw_spkr_count(#__shared_memory.rtu_dev.sounders)
|
||||||
|
|
||||||
-- 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")
|
||||||
|
@ -8,6 +8,7 @@ local util = require("scada-common.util")
|
|||||||
local databus = require("rtu.databus")
|
local databus = require("rtu.databus")
|
||||||
local modbus = require("rtu.modbus")
|
local modbus = require("rtu.modbus")
|
||||||
local renderer = require("rtu.renderer")
|
local renderer = require("rtu.renderer")
|
||||||
|
local rtu = require("rtu.rtu")
|
||||||
|
|
||||||
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
|
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
|
||||||
local dynamicv_rtu = require("rtu.dev.dynamicv_rtu")
|
local dynamicv_rtu = require("rtu.dev.dynamicv_rtu")
|
||||||
@ -47,6 +48,7 @@ function threads.thread__main(smem)
|
|||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local rtu_state = smem.rtu_state
|
local rtu_state = smem.rtu_state
|
||||||
|
local sounders = smem.rtu_dev.sounders
|
||||||
local nic = smem.rtu_sys.nic
|
local nic = smem.rtu_sys.nic
|
||||||
local rtu_comms = smem.rtu_sys.rtu_comms
|
local rtu_comms = smem.rtu_sys.rtu_comms
|
||||||
local conn_watchdog = smem.rtu_sys.conn_watchdog
|
local conn_watchdog = smem.rtu_sys.conn_watchdog
|
||||||
@ -110,6 +112,18 @@ function threads.thread__main(smem)
|
|||||||
else
|
else
|
||||||
log.warning("non-comms modem disconnected")
|
log.warning("non-comms modem disconnected")
|
||||||
end
|
end
|
||||||
|
elseif type == "speaker" then
|
||||||
|
for i = 1, #sounders do
|
||||||
|
if sounders[i].speaker == device then
|
||||||
|
table.remove(sounders, i)
|
||||||
|
|
||||||
|
log.warning(util.c("speaker ", param1, " disconnected"))
|
||||||
|
println_ts("speaker disconnected")
|
||||||
|
|
||||||
|
databus.tx_hw_spkr_count(#sounders)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
for i = 1, #units do
|
for i = 1, #units do
|
||||||
-- find disconnected device
|
-- find disconnected device
|
||||||
@ -147,6 +161,13 @@ function threads.thread__main(smem)
|
|||||||
else
|
else
|
||||||
log.info("wired modem reconnected")
|
log.info("wired modem reconnected")
|
||||||
end
|
end
|
||||||
|
elseif type == "speaker" then
|
||||||
|
table.insert(sounders, rtu.init_sounder(device))
|
||||||
|
|
||||||
|
println_ts("speaker connected")
|
||||||
|
log.info(util.c("connected speaker ", param1))
|
||||||
|
|
||||||
|
databus.tx_hw_spkr_count(#sounders)
|
||||||
else
|
else
|
||||||
-- relink lost peripheral to correct unit entry
|
-- relink lost peripheral to correct unit entry
|
||||||
for i = 1, #units do
|
for i = 1, #units do
|
||||||
@ -252,6 +273,15 @@ function threads.thread__main(smem)
|
|||||||
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
|
elseif event == "mouse_click" or event == "mouse_up" or event == "mouse_drag" or event == "mouse_scroll" then
|
||||||
-- handle a mouse event
|
-- handle a mouse event
|
||||||
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
renderer.handle_mouse(core.events.new_mouse_event(event, param1, param2, param3))
|
||||||
|
elseif event == "speaker_audio_empty" then
|
||||||
|
-- handle empty speaker audio buffer
|
||||||
|
for i = 1, #sounders do
|
||||||
|
local sounder = sounders[i] ---@type rtu_speaker_sounder
|
||||||
|
if sounder.name == param1 then
|
||||||
|
sounder.continue()
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- check for termination request
|
-- check for termination request
|
||||||
@ -299,6 +329,7 @@ function threads.thread__comms(smem)
|
|||||||
|
|
||||||
-- load in from shared memory
|
-- load in from shared memory
|
||||||
local rtu_state = smem.rtu_state
|
local rtu_state = smem.rtu_state
|
||||||
|
local sounders = smem.rtu_dev.sounders
|
||||||
local rtu_comms = smem.rtu_sys.rtu_comms
|
local rtu_comms = smem.rtu_sys.rtu_comms
|
||||||
local units = smem.rtu_sys.units
|
local units = smem.rtu_sys.units
|
||||||
|
|
||||||
@ -321,8 +352,8 @@ function threads.thread__comms(smem)
|
|||||||
-- received data
|
-- received data
|
||||||
elseif msg.qtype == mqueue.TYPE.PACKET then
|
elseif msg.qtype == mqueue.TYPE.PACKET then
|
||||||
-- received a packet
|
-- received a packet
|
||||||
-- handle the packet (rtu_state passed to allow setting link flag)
|
-- handle the packet (rtu_state passed to allow setting link flag, sounders passed to manage alarm audio)
|
||||||
rtu_comms.handle_packet(msg.message, units, rtu_state)
|
rtu_comms.handle_packet(msg.message, units, rtu_state, sounders)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ local max_distance = nil ---@type number|nil maximum acceptable t
|
|||||||
---@class comms
|
---@class comms
|
||||||
local comms = {}
|
local comms = {}
|
||||||
|
|
||||||
comms.version = "2.1.2"
|
comms.version = "2.2.0"
|
||||||
|
|
||||||
---@enum PROTOCOL
|
---@enum PROTOCOL
|
||||||
local PROTOCOL = {
|
local PROTOCOL = {
|
||||||
@ -46,7 +46,8 @@ local SCADA_MGMT_TYPE = {
|
|||||||
KEEP_ALIVE = 1, -- keep alive packet w/ RTT
|
KEEP_ALIVE = 1, -- keep alive packet w/ RTT
|
||||||
CLOSE = 2, -- close a connection
|
CLOSE = 2, -- close a connection
|
||||||
RTU_ADVERT = 3, -- RTU capability advertisement
|
RTU_ADVERT = 3, -- RTU capability advertisement
|
||||||
RTU_DEV_REMOUNT = 4 -- RTU multiblock possbily changed (formed, unformed) due to PPM remount
|
RTU_DEV_REMOUNT = 4,-- RTU multiblock possbily changed (formed, unformed) due to PPM remount
|
||||||
|
RTU_TONE_ALARM = 5 -- instruct RTUs to play specified alarm tones
|
||||||
}
|
}
|
||||||
|
|
||||||
---@enum SCADA_CRDN_TYPE
|
---@enum SCADA_CRDN_TYPE
|
||||||
|
Loading…
Reference in New Issue
Block a user