Add support to handle emergency; add support to send p25 pages

pull/1/head
firealarmss 1 year ago
parent 1e8d253c11
commit 14d54836ec

@ -30,8 +30,11 @@ namespace WhackerLinkConsoleV2.Controls
{ {
private readonly SelectedChannelsManager _selectedChannelsManager; private readonly SelectedChannelsManager _selectedChannelsManager;
private bool _pttState; private bool _pttState;
private bool _emergency;
private string _lastSrcId = "0"; private string _lastSrcId = "0";
public FlashingBackgroundManager _flashingBackgroundManager;
public event EventHandler<ChannelBox> PTTButtonClicked; public event EventHandler<ChannelBox> PTTButtonClicked;
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
@ -61,6 +64,23 @@ namespace WhackerLinkConsoleV2.Controls
} }
} }
public bool Emergency
{
get => _emergency;
set
{
_emergency = value;
Dispatcher.Invoke(() =>
{
if (value)
_flashingBackgroundManager.Start();
else
_flashingBackgroundManager.Stop();
});
}
}
public string VoiceChannel { get; set; } public string VoiceChannel { get; set; }
public bool IsEditMode { get; set; } public bool IsEditMode { get; set; }
@ -81,6 +101,7 @@ namespace WhackerLinkConsoleV2.Controls
InitializeComponent(); InitializeComponent();
DataContext = this; DataContext = this;
_selectedChannelsManager = selectedChannelsManager; _selectedChannelsManager = selectedChannelsManager;
_flashingBackgroundManager = new FlashingBackgroundManager(this);
ChannelName = channelName; ChannelName = channelName;
SystemName = $"System: {systemName}"; SystemName = $"System: {systemName}";
LastSrcId = $"Last SRC: {LastSrcId}"; LastSrcId = $"Last SRC: {LastSrcId}";

@ -0,0 +1,17 @@
<Window x:Class="WhackerLinkConsoleV2.DigitalPageWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WhackerLinkConsoleV2"
mc:Ignorable="d"
Title="P25 Page" Height="200" Width="300" WindowStartupLocation="CenterOwner">
<Grid>
<Button Content="Send" Name="SendPageButton" HorizontalAlignment="Center" Margin="0,97,0,0" VerticalAlignment="Top" Click="SendPageButton_Click"/>
<TextBox HorizontalAlignment="Center" Name="DstIdText" Margin="0,74,0,0" TextWrapping="Wrap" Text="" MaxLength="12" VerticalAlignment="Top" Width="120"/>
<Label Content="Dst ID:" HorizontalAlignment="Left" Margin="40,70,0,0" VerticalAlignment="Top"/>
<ComboBox HorizontalAlignment="Center" Name="SystemCombo" Margin="0,47,0,0" VerticalAlignment="Top" Width="120"/>
<Label Content="System:" HorizontalAlignment="Left" Margin="35,44,0,0" VerticalAlignment="Top"/>
</Grid>
</Window>

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using WhackerLinkLib.Models.Radio;
namespace WhackerLinkConsoleV2
{
/// <summary>
/// Interaction logic for DigitalPageWindow.xaml
/// </summary>
public partial class DigitalPageWindow : Window
{
public List<Codeplug.System> systems = new List<Codeplug.System>();
public string DstId = string.Empty;
public Codeplug.System RadioSystem = null;
public DigitalPageWindow(List<Codeplug.System> systems)
{
InitializeComponent();
this.systems = systems;
SystemCombo.DisplayMemberPath = "Name";
SystemCombo.ItemsSource = systems;
SystemCombo.SelectedIndex = 0;
}
private void SendPageButton_Click(object sender, RoutedEventArgs e)
{
RadioSystem = SystemCombo.SelectedItem as Codeplug.System;
DstId = DstIdText.Text;
DialogResult = true;
Close();
}
}
}

@ -0,0 +1,118 @@
/*
* WhackerLink - WhackerLinkConsoleV2
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2024 Caleb, K4PHP
*
*/
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
namespace WhackerLinkConsoleV2
{
public class FlashingBackgroundManager
{
private readonly Control _control;
private readonly Canvas _canvas;
private readonly UserControl _userControl;
private readonly Window _mainWindow;
private readonly DispatcherTimer _timer;
private Brush _originalControlBackground;
private Brush _originalCanvasBackground;
private Brush _originalUserControlBackground;
private Brush _originalMainWindowBackground;
private bool _isFlashing;
public FlashingBackgroundManager(Control control = null, Canvas canvas = null, UserControl userControl = null, Window mainWindow = null, int intervalMilliseconds = 450)
{
_control = control;
_canvas = canvas;
_userControl = userControl;
_mainWindow = mainWindow;
if (_control == null && _canvas == null && _userControl == null && _mainWindow == null)
throw new ArgumentException("At least one of control, canvas, userControl, or mainWindow must be provided.");
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(intervalMilliseconds)
};
_timer.Tick += OnTimerTick;
}
public void Start()
{
if (_isFlashing)
return;
if (_control != null)
_originalControlBackground = _control.Background;
if (_canvas != null)
_originalCanvasBackground = _canvas.Background;
if (_userControl != null)
_originalUserControlBackground = _userControl.Background;
if (_mainWindow != null)
_originalMainWindowBackground = _mainWindow.Background;
_isFlashing = true;
_timer.Start();
}
public void Stop()
{
if (!_isFlashing)
return;
_timer.Stop();
if (_control != null)
_control.Background = _originalControlBackground;
if (_canvas != null)
_canvas.Background = _originalCanvasBackground;
if (_userControl != null)
_userControl.Background = _originalUserControlBackground;
if (_mainWindow != null && _originalMainWindowBackground != null)
_mainWindow.Background = _originalMainWindowBackground;
_isFlashing = false;
}
private void OnTimerTick(object sender, EventArgs e)
{
Brush flashingColor = Brushes.Red;
if (_control != null)
_control.Background = _control.Background == Brushes.DarkRed ? _originalControlBackground : Brushes.DarkRed;
if (_canvas != null)
_canvas.Background = _canvas.Background == flashingColor ? _originalCanvasBackground : flashingColor;
if (_userControl != null)
_userControl.Background = _userControl.Background == Brushes.DarkRed ? _originalUserControlBackground : Brushes.DarkRed;
if (_mainWindow != null)
_mainWindow.Background = _mainWindow.Background == flashingColor ? _originalMainWindowBackground : flashingColor;
}
}
}

@ -22,6 +22,10 @@
<MenuItem Header="Add Alert Tone" Click="AddAlertTone_Click"/> <MenuItem Header="Add Alert Tone" Click="AddAlertTone_Click"/>
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="Page">
<MenuItem Header="P25 Page" Click="P25Page_Click" />
</MenuItem>
<MenuItem Header="Clear Emergency" Click="ClearEmergency_Click" />
</Menu> </Menu>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Grid.Row="1"> <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Grid.Row="1">

@ -55,6 +55,8 @@ namespace WhackerLinkConsoleV2
private SettingsManager _settingsManager = new SettingsManager(); private SettingsManager _settingsManager = new SettingsManager();
private SelectedChannelsManager _selectedChannelsManager; private SelectedChannelsManager _selectedChannelsManager;
private FlashingBackgroundManager _flashingManager;
private WaveFilePlaybackManager _emergencyAlertPlayback;
private WebSocketManager _webSocketManager = new WebSocketManager(); private WebSocketManager _webSocketManager = new WebSocketManager();
private readonly WaveInEvent _waveIn; private readonly WaveInEvent _waveIn;
@ -66,6 +68,8 @@ namespace WhackerLinkConsoleV2
InitializeComponent(); InitializeComponent();
_settingsManager.LoadSettings(); _settingsManager.LoadSettings();
_selectedChannelsManager = new SelectedChannelsManager(); _selectedChannelsManager = new SelectedChannelsManager();
_flashingManager = new FlashingBackgroundManager(null, ChannelsCanvas, null, this);
_emergencyAlertPlayback = new WaveFilePlaybackManager(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "emergency.wav"));
_waveIn = new WaveInEvent _waveIn = new WaveInEvent
{ {
@ -171,6 +175,7 @@ namespace WhackerLinkConsoleV2
IWebSocketHandler handler = _webSocketManager.GetWebSocketHandler(system.Name); IWebSocketHandler handler = _webSocketManager.GetWebSocketHandler(system.Name);
handler.OnVoiceChannelResponse += HandleVoiceResponse; handler.OnVoiceChannelResponse += HandleVoiceResponse;
handler.OnVoiceChannelRelease += HandleVoiceRelease; handler.OnVoiceChannelRelease += HandleVoiceRelease;
handler.OnEmergencyAlarmResponse += HandleEmergencyAlarmResponse;
handler.OnAudioData += HandleReceivedAudio; handler.OnAudioData += HandleReceivedAudio;
handler.OnUnitRegistrationResponse += (response) => handler.OnUnitRegistrationResponse += (response) =>
@ -352,9 +357,20 @@ namespace WhackerLinkConsoleV2
} }
} }
private void P25Page_Click(object sender, RoutedEventArgs e)
{
DigitalPageWindow pageWindow = new DigitalPageWindow(Codeplug.Systems);
pageWindow.Owner = this;
if (pageWindow.ShowDialog() == true)
{
IWebSocketHandler handler = _webSocketManager.GetWebSocketHandler(pageWindow.RadioSystem.Name);
handler.SendMessage(PacketFactory.CreateCallAlertRequest(pageWindow.RadioSystem.Rid, pageWindow.DstId));
}
}
private void SelectWidgets_Click(object sender, RoutedEventArgs e) private void SelectWidgets_Click(object sender, RoutedEventArgs e)
{ {
var widgetSelectionWindow = new WidgetSelectionWindow(); WidgetSelectionWindow widgetSelectionWindow = new WidgetSelectionWindow();
widgetSelectionWindow.Owner = this; widgetSelectionWindow.Owner = this;
if (widgetSelectionWindow.ShowDialog() == true) if (widgetSelectionWindow.ShowDialog() == true)
{ {
@ -367,6 +383,33 @@ namespace WhackerLinkConsoleV2
} }
} }
private void HandleEmergencyAlarmResponse(EMRG_ALRM_RSP response)
{
bool forUs = false;
foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
{
Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
if (response.DstId == cpgChannel.Tgid)
{
forUs = true;
channel.Emergency = true;
channel.LastSrcId = response.SrcId;
}
}
if (forUs)
{
Dispatcher.Invoke(() =>
{
_flashingManager.Start();
_emergencyAlertPlayback.Start();
});
}
}
private void HandleReceivedAudio(byte[] audioData, VoiceChannel voiceChannel) private void HandleReceivedAudio(byte[] audioData, VoiceChannel voiceChannel)
{ {
foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels()) foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
@ -625,5 +668,16 @@ namespace WhackerLinkConsoleV2
_settingsManager.SaveSettings(); _settingsManager.SaveSettings();
base.OnClosing(e); base.OnClosing(e);
} }
private void ClearEmergency_Click(object sender, RoutedEventArgs e)
{
_emergencyAlertPlayback.Stop();
_flashingManager.Stop();
foreach (ChannelBox channel in _selectedChannelsManager.GetSelectedChannels())
{
channel.Emergency = false;
}
}
} }
} }

@ -0,0 +1,98 @@
/*
* WhackerLink - WhackerLinkConsoleV2
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2024 Caleb, K4PHP
*
*/
using NAudio.Wave;
using System.Windows.Threading;
namespace WhackerLinkConsoleV2
{
public class WaveFilePlaybackManager
{
private readonly string _waveFilePath;
private readonly DispatcherTimer _timer;
private WaveOutEvent _waveOut;
private AudioFileReader _audioFileReader;
private bool _isPlaying;
public WaveFilePlaybackManager(string waveFilePath, int intervalMilliseconds = 500)
{
if (string.IsNullOrEmpty(waveFilePath))
throw new ArgumentNullException(nameof(waveFilePath), "Wave file path cannot be null or empty.");
_waveFilePath = waveFilePath;
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(intervalMilliseconds)
};
_timer.Tick += OnTimerTick;
}
public void Start()
{
if (_isPlaying)
return;
InitializeAudio();
_isPlaying = true;
_timer.Start();
}
public void Stop()
{
if (!_isPlaying)
return;
_timer.Stop();
DisposeAudio();
_isPlaying = false;
}
private void OnTimerTick(object sender, EventArgs e)
{
PlayAudio();
}
private void InitializeAudio()
{
_audioFileReader = new AudioFileReader(_waveFilePath);
_waveOut = new WaveOutEvent();
_waveOut.Init(_audioFileReader);
}
private void PlayAudio()
{
if (_waveOut != null && _waveOut.PlaybackState != PlaybackState.Playing)
{
_waveOut.Stop();
_audioFileReader.Position = 0;
_waveOut.Play();
}
}
private void DisposeAudio()
{
_waveOut?.Stop();
_waveOut?.Dispose();
_audioFileReader?.Dispose();
_waveOut = null;
_audioFileReader = null;
}
}
}
Loading…
Cancel
Save

Powered by TurnKey Linux.