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
|
||||
-- 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
|
||||
config.LOG_PATH = "/log.txt"
|
||||
-- log mode
|
||||
|
@ -37,6 +37,12 @@ function databus.tx_hw_modem(has_modem)
|
||||
databus.ps.publish("has_modem", has_modem)
|
||||
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
|
||||
---@param uid integer unit ID
|
||||
---@param type RTU_UNIT_TYPE
|
||||
|
@ -14,6 +14,7 @@ local core = require("graphics.core")
|
||||
local Div = require("graphics.elements.div")
|
||||
local TextBox = require("graphics.elements.textbox")
|
||||
|
||||
local DataIndicator = require("graphics.elements.indicators.data")
|
||||
local LED = require("graphics.elements.indicators.led")
|
||||
local RGBLED = require("graphics.elements.indicators.ledrgb")
|
||||
|
||||
@ -21,17 +22,7 @@ local TEXT_ALIGN = core.TEXT_ALIGN
|
||||
|
||||
local cpair = core.cpair
|
||||
|
||||
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" }
|
||||
|
||||
|
||||
-- create new front panel view
|
||||
@ -72,6 +63,10 @@ local function init(panel, units)
|
||||
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=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
|
||||
--
|
||||
|
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 ppm = require("scada-common.ppm")
|
||||
local log = require("scada-common.log")
|
||||
local types = require("scada-common.types")
|
||||
local util = require("scada-common.util")
|
||||
|
||||
local config = require("rtu.config")
|
||||
local databus = require("rtu.databus")
|
||||
local modbus = require("rtu.modbus")
|
||||
|
||||
@ -155,6 +157,48 @@ function rtu.init_unit()
|
||||
return protected
|
||||
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
|
||||
---@nodiscard
|
||||
---@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 units table RTU units
|
||||
---@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
|
||||
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
|
||||
-- request for capabilities again
|
||||
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
|
||||
-- not supported
|
||||
log.debug("received unsupported SCADA_MGMT message type " .. packet.type)
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
require("/initenv").init_env()
|
||||
|
||||
local audio = require("scada-common.audio")
|
||||
local comms = require("scada-common.comms")
|
||||
local crash = require("scada-common.crash")
|
||||
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 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_HW_STATE = databus.RTU_UNIT_HW_STATE
|
||||
@ -96,6 +97,9 @@ local function main()
|
||||
return
|
||||
end
|
||||
|
||||
-- generate alarm tones
|
||||
audio.generate_tones()
|
||||
|
||||
---@class rtu_shared_memory
|
||||
local __shared_memory = {
|
||||
-- RTU system state flags
|
||||
@ -106,6 +110,11 @@ local function main()
|
||||
shutdown = false
|
||||
},
|
||||
|
||||
-- RTU gateway devices (not RTU units)
|
||||
rtu_dev = {
|
||||
sounders = {}
|
||||
},
|
||||
|
||||
-- system objects
|
||||
rtu_sys = {
|
||||
nic = network.nic(modem),
|
||||
@ -481,6 +490,18 @@ local function main()
|
||||
log.info("startup> running in headless mode without front panel")
|
||||
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
|
||||
smem_sys.conn_watchdog = util.new_watchdog(config.COMMS_TIMEOUT)
|
||||
log.debug("startup> conn watchdog started")
|
||||
|
@ -8,6 +8,7 @@ local util = require("scada-common.util")
|
||||
local databus = require("rtu.databus")
|
||||
local modbus = require("rtu.modbus")
|
||||
local renderer = require("rtu.renderer")
|
||||
local rtu = require("rtu.rtu")
|
||||
|
||||
local boilerv_rtu = require("rtu.dev.boilerv_rtu")
|
||||
local dynamicv_rtu = require("rtu.dev.dynamicv_rtu")
|
||||
@ -47,6 +48,7 @@ function threads.thread__main(smem)
|
||||
|
||||
-- load in from shared memory
|
||||
local rtu_state = smem.rtu_state
|
||||
local sounders = smem.rtu_dev.sounders
|
||||
local nic = smem.rtu_sys.nic
|
||||
local rtu_comms = smem.rtu_sys.rtu_comms
|
||||
local conn_watchdog = smem.rtu_sys.conn_watchdog
|
||||
@ -110,6 +112,18 @@ function threads.thread__main(smem)
|
||||
else
|
||||
log.warning("non-comms modem disconnected")
|
||||
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
|
||||
for i = 1, #units do
|
||||
-- find disconnected device
|
||||
@ -147,6 +161,13 @@ function threads.thread__main(smem)
|
||||
else
|
||||
log.info("wired modem reconnected")
|
||||
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
|
||||
-- relink lost peripheral to correct unit entry
|
||||
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
|
||||
-- handle a mouse event
|
||||
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
|
||||
|
||||
-- check for termination request
|
||||
@ -299,6 +329,7 @@ function threads.thread__comms(smem)
|
||||
|
||||
-- load in from shared memory
|
||||
local rtu_state = smem.rtu_state
|
||||
local sounders = smem.rtu_dev.sounders
|
||||
local rtu_comms = smem.rtu_sys.rtu_comms
|
||||
local units = smem.rtu_sys.units
|
||||
|
||||
@ -321,8 +352,8 @@ function threads.thread__comms(smem)
|
||||
-- received data
|
||||
elseif msg.qtype == mqueue.TYPE.PACKET then
|
||||
-- received a packet
|
||||
-- handle the packet (rtu_state passed to allow setting link flag)
|
||||
rtu_comms.handle_packet(msg.message, units, rtu_state)
|
||||
-- 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, sounders)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -14,7 +14,7 @@ local max_distance = nil ---@type number|nil maximum acceptable t
|
||||
---@class comms
|
||||
local comms = {}
|
||||
|
||||
comms.version = "2.1.2"
|
||||
comms.version = "2.2.0"
|
||||
|
||||
---@enum PROTOCOL
|
||||
local PROTOCOL = {
|
||||
@ -46,7 +46,8 @@ local SCADA_MGMT_TYPE = {
|
||||
KEEP_ALIVE = 1, -- keep alive packet w/ RTT
|
||||
CLOSE = 2, -- close a connection
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user