You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

644 lines
20 KiB

// SPDX-License-Identifier: AGPL-3.0-only
/**
* Digital Voice Modem - Desktop Dispatch Console
* AGPLv3 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Desktop Dispatch Console
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
*
* Copyright (C) 2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL
*
*/
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using fnecore.P25;
namespace dvmconsole.Controls
{
/// <summary>
///
/// </summary>
public partial class ChannelBox : UserControl, INotifyPropertyChanged
{
public readonly static LinearGradientBrush GRAY_GRADIENT;
public readonly static LinearGradientBrush DARK_GRAY_GRADIENT; // Delected/Disconnected Color
public readonly static LinearGradientBrush BLUE_GRADIENT; // Selected Channel Color
public readonly static LinearGradientBrush RED_GRADIENT; // Playback Selected Color
public readonly static LinearGradientBrush GREEN_GRADIENT; // Clear Rx Color
public readonly static LinearGradientBrush ORANGE_GRADIENT; // Encrypted Rx Color
private readonly SelectedChannelsManager selectedChannelsManager;
private readonly AudioManager audioManager;
private bool pttState;
private bool pageState;
private bool holdState;
private bool emergency;
private string lastSrcId = "0";
private double volume = 1.0;
private bool isSelected;
public FlashingBackgroundManager flashingBackgroundManager;
public byte[] netLDU1 = new byte[9 * 25];
public byte[] netLDU2 = new byte[9 * 25];
public int p25N { get; set; } = 0;
public int p25SeqNo { get; set; } = 0;
public int p25Errs { get; set; } = 0;
public byte[] mi = new byte[P25Defines.P25_MI_LENGTH]; // Message Indicator
public byte algId = 0; // Algorithm ID
public ushort kId = 0; // Key ID
public List<byte[]> chunkedPcm = new List<byte[]>();
public bool ExternalVocoderEnabled = false;
public AmbeVocoder ExtFullRateVocoder = null;
public AmbeVocoder ExtHalfRateVocoder = null;
public MBEEncoder Encoder = null;
public MBEDecoder Decoder = null;
public MBEToneDetector ToneDetector = new MBEToneDetector();
public P25Crypto Crypter = new P25Crypto();
private bool pttToggleMode = false;
/*
** Properties
*/
/// <summary>
/// Textual name of channel.
/// </summary>
public string ChannelName { get; set; }
/// <summary>
/// Textual name of system channel belongs to.
/// </summary>
public string SystemName { get; set; }
/// <summary>
/// Destination ID.
/// </summary>
public string DstId { get; set; }
/// <summary>
/// Gets or sets the <see cref="Brush"/> that fills the area between the bounds of the control border.
/// </summary>
public new Brush Background
{
get => ControlBorder.Background;
set => ControlBorder.Background = value;
}
/*
** Events
*/
/// <summary>
/// Event action that handles the PTT button being clicked.
/// </summary>
public event EventHandler<ChannelBox> PTTButtonClicked;
/// <summary>
/// Event action that handles the PTT button being pressed.
/// </summary>
public event EventHandler<ChannelBox> PTTButtonPressed;
/// <summary>
/// Event action that handles the PTT button being released.
/// </summary>
public event EventHandler<ChannelBox> PTTButtonReleased;
/// <summary>
/// Event action that handles the page button being clicked.
/// </summary>
public event EventHandler<ChannelBox> PageButtonClicked;
/// <summary>
/// Event action that handles the hold channel button being clicked.
/// </summary>
public event EventHandler<ChannelBox> HoldChannelButtonClicked;
/// <summary>
/// Event action that occurs when a property changes on this control.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Flag indicating whether or not this channel is receiving.
/// </summary>
public bool IsReceiving { get; set; } = false;
/// <summary>
/// Flag indicating whether or not this channel is receiving encrypted.
/// </summary>
public bool IsReceivingEncrypted { get; set; } = false;
/// <summary>
/// Last Source ID received.
/// </summary>
public string LastSrcId
{
get => lastSrcId;
set
{
if (lastSrcId != value)
{
lastSrcId = value;
OnPropertyChanged(nameof(LastSrcId));
}
}
}
/// <summary>
/// Flag indicating the current PTT state of this channel.
/// </summary>
public bool PttState
{
get => pttState;
set
{
pttState = value;
UpdatePTTColor();
}
}
/// <summary>
/// Flag indicating the current page state of this channel.
/// </summary>
public bool PageState
{
get => pageState;
set
{
pageState = value;
UpdatePageColor();
}
}
/// <summary>
/// Flag indicating the hold state of this channel.
/// </summary>
public bool HoldState
{
get => holdState;
set
{
holdState = value;
UpdateHoldColor();
}
}
/// <summary>
/// Flag indicating the channel is in toggle PTT or regular PTT.
/// </summary>
public bool PTTToggleMode
{
get => pttToggleMode;
set => pttToggleMode = value;
}
/// <summary>
/// Flag indicating the emergency state of this channel.
/// </summary>
public bool Emergency
{
get => emergency;
set
{
emergency = value;
Dispatcher.Invoke(() =>
{
if (value)
flashingBackgroundManager.Start();
else
flashingBackgroundManager.Stop();
});
}
}
/// <summary>
///
/// </summary>
public string VoiceChannel { get; set; }
/// <summary>
/// Flag indicating whether or not edit mode is enabled.
/// </summary>
public bool IsEditMode { get; set; }
/// <summary>
/// Flag indicating whether or not this channel is selected.
/// </summary>
public bool IsSelected
{
get => isSelected;
set
{
isSelected = value;
if (!isSelected)
DisableControls();
else
EnableControls();
UpdateBackground();
}
}
/// <summary>
/// Current volume for this channel.
/// </summary>
public double Volume
{
get => volume;
set
{
if (volume != value)
{
volume = value;
OnPropertyChanged(nameof(Volume));
audioManager.SetTalkgroupVolume(DstId, (float)value);
}
}
}
/// <summary>
///
/// </summary>
public uint TxStreamId { get; internal set; }
/*
** Methods
*/
/// <summary>
/// Static initialize for the <see cref="ChannelBox" class. />
/// </summary>
static ChannelBox()
{
GRAY_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0F0F0F0"), 0.485));
GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C2C2C2"), 0.517));
DARK_GRAY_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0979797"), 0.485));
DARK_GRAY_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0686767"), 0.517));
BLUE_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0150189"), 0.485));
BLUE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F00B004B"), 0.517));
RED_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FF0000"), 0.485));
RED_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C60000"), 0.517));
GREEN_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F000AF00"), 0.485));
GREEN_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0008E00"), 0.517));
ORANGE_GRADIENT = new LinearGradientBrush
{
StartPoint = new Point(0.5, 0),
EndPoint = new Point(0.5, 1)
};
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0FFAF00"), 0.485));
ORANGE_GRADIENT.GradientStops.Add(new GradientStop((Color)ColorConverter.ConvertFromString("#F0C68700"), 0.517));
}
/// <summary>
/// Initializes a new instance of the <see cref="ChannelBox"/> class.
/// </summary>
/// <param name="selectedChannelsManager"></param>
/// <param name="audioManager"></param>
/// <param name="channelName"></param>
/// <param name="systemName"></param>
/// <param name="dstId"></param>
/// <param name="pttToggleMode"></param>
public ChannelBox(SelectedChannelsManager selectedChannelsManager, AudioManager audioManager, string channelName, string systemName, string dstId, bool pttToggleMode = false)
{
InitializeComponent();
DataContext = this;
this.selectedChannelsManager = selectedChannelsManager;
this.audioManager = audioManager;
flashingBackgroundManager = new FlashingBackgroundManager(this);
ChannelName = channelName;
DstId = dstId;
SystemName = $"System: {systemName}";
LastSrcId = $"Last ID: {LastSrcId}";
UpdateBackground();
MouseLeftButtonDown += ChannelBox_MouseLeftButtonDown;
PttButton.PreviewMouseLeftButtonDown += PttButton_MouseLeftButtonDown;
PttButton.PreviewMouseLeftButtonUp += PttButton_MouseLeftButtonUp;
PttButton.MouseRightButtonDown += PttButton_MouseRightButtonDown;
this.pttToggleMode = pttToggleMode;
PttButton.Background = GRAY_GRADIENT;
PageSelectButton.Background = GRAY_GRADIENT;
ChannelMarkerBtn.Background = GRAY_GRADIENT;
DisableControls();
if (SystemName == MainWindow.PLAYBACKSYS || ChannelName == MainWindow.PLAYBACKCHNAME || DstId == MainWindow.PLAYBACKTG)
{
PttButton.IsEnabled = false;
PageSelectButton.IsEnabled = false;
ChannelMarkerBtn.IsEnabled = false;
}
// initialize external AMBE vocoder
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
// if the assembly executing directory contains the external DVSI USB-3000 interface DLL
// setup the external vocoder code
if (File.Exists(Path.Combine(new string[] { Path.GetDirectoryName(path), "AMBE.DLL" })))
{
ExternalVocoderEnabled = true;
ExtFullRateVocoder = new AmbeVocoder();
ExtHalfRateVocoder = new AmbeVocoder(false);
}
}
/// <summary>
/// Helper to enable controls.
/// </summary>
private void EnableControls()
{
PttButton.IsEnabled = true;
ChannelMarkerBtn.IsEnabled = true;
PageSelectButton.IsEnabled = true;
VolumeSlider.IsEnabled = true;
}
/// <summary>
/// Helper to disable controls.
/// </summary>
private void DisableControls()
{
PttButton.IsEnabled = false;
ChannelMarkerBtn.IsEnabled = false;
PageSelectButton.IsEnabled = false;
VolumeSlider.IsEnabled = false;
}
/// <summary>
///
/// </summary>
private void UpdatePTTColor()
{
if (IsEditMode)
return;
if (PttState)
PttButton.Background = RED_GRADIENT;
else
PttButton.Background = GRAY_GRADIENT;
}
/// <summary>
///
/// </summary>
private void UpdatePageColor()
{
if (IsEditMode)
return;
if (PageState)
PageSelectButton.Background = ORANGE_GRADIENT;
else
PageSelectButton.Background = GRAY_GRADIENT;
}
private void UpdateHoldColor()
{
if (IsEditMode)
return;
if (HoldState)
ChannelMarkerBtn.Background = ORANGE_GRADIENT;
else
ChannelMarkerBtn.Background = GRAY_GRADIENT;
}
/// <summary>
///
/// </summary>
private void UpdateBackground()
{
if (SystemName == MainWindow.PLAYBACKSYS || ChannelName == MainWindow.PLAYBACKCHNAME || DstId == MainWindow.PLAYBACKTG)
{
ControlBorder.Background = IsSelected ? RED_GRADIENT : DARK_GRAY_GRADIENT;
return;
}
ControlBorder.Background = IsSelected ? BLUE_GRADIENT : DARK_GRAY_GRADIENT;
}
/// <summary>
///
/// </summary>
/// <param name="propertyName"></param>
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/** WPF Events */
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ChannelBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (IsEditMode)
return;
IsSelected = !IsSelected;
ControlBorder.Background = IsSelected ? BLUE_GRADIENT : DARK_GRAY_GRADIENT;
if (IsSelected)
selectedChannelsManager.AddSelectedChannel(this);
else
selectedChannelsManager.RemoveSelectedChannel(this);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PttButton_Click(object sender, RoutedEventArgs e)
{
if (!IsSelected)
return;
PttState = !PttState;
PTTButtonClicked?.Invoke(sender, this);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private async void PttButton_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (pttToggleMode)
return;
if (PttState)
await Task.Delay(500);
PttButton_Click(sender, e);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private async void PttButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!IsSelected)
return;
if (PttState)
await Task.Delay(500);
if (pttToggleMode)
PttButton_Click(sender, e);
else
{
PttState = true;
PTTButtonPressed?.Invoke(sender, this);
}
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <exception cref="NotImplementedException"></exception>
private void PttButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (pttToggleMode)
return;
if (!IsSelected)
return;
PTTButtonReleased?.Invoke(sender, this);
PttState = false;
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PageSelectButton_Click(object sender, RoutedEventArgs e)
{
if (!IsSelected)
return;
PageState = !PageState;
PageButtonClicked?.Invoke(sender, this);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void VolumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Volume = e.NewValue;
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ChannelMarkerBtn_Click(object sender, RoutedEventArgs e)
{
if (!IsSelected)
return;
HoldState = !HoldState;
HoldChannelButtonClicked.Invoke(sender, this);
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PttButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (!IsSelected || PttState)
return;
((Button)sender).Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF3FA0FF"));
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PttButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
if (!IsSelected || PttState)
return;
((Button)sender).Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFDDDDDD"));
}
} // public partial class ChannelBox : UserControl, INotifyPropertyChanged
} // namespace dvmconsole.Controls

Powered by TurnKey Linux.