diff --git a/dvmconsole/Controls/ChannelBox.xaml.cs b/dvmconsole/Controls/ChannelBox.xaml.cs
index de53d5d..cff31b0 100644
--- a/dvmconsole/Controls/ChannelBox.xaml.cs
+++ b/dvmconsole/Controls/ChannelBox.xaml.cs
@@ -154,6 +154,11 @@ namespace dvmconsole.Controls
///
public event PropertyChangedEventHandler PropertyChanged;
+ ///
+ /// Last Packet Time
+ ///
+ public DateTime LastPktTime = DateTime.Now;
+
///
/// Flag indicating whether or not this channel is receiving.
///
diff --git a/dvmconsole/MainWindow.DMR.cs b/dvmconsole/MainWindow.DMR.cs
index e9c932a..9c91a7c 100644
--- a/dvmconsole/MainWindow.DMR.cs
+++ b/dvmconsole/MainWindow.DMR.cs
@@ -261,6 +261,8 @@ namespace dvmconsole
Buffer.BlockCopy(e.Data, 20, data, 0, FneSystemBase.DMR_FRAME_LENGTH_BYTES);
byte bits = e.Data[15];
+ channel.LastPktTime = pktTime;
+
// is this a new call stream?
if (e.StreamId != systemStatuses[cpgChannel.Name + e.Slot].RxStreamId)
{
@@ -290,18 +292,13 @@ namespace dvmconsole
channel.Background = ChannelBox.GREEN_GRADIENT;
}
- string alias = string.Empty;
-
- try
+ // reset the channel state if we're not Rx
+ if (!channel.IsReceiving)
{
- alias = AliasTools.GetAliasByRid(system.RidAlias, (int)e.SrcId);
+ channel.Background = ChannelBox.BLUE_GRADIENT;
+ channel.VolumeMeterLevel = 0;
+ continue;
}
- catch (Exception) { }
-
- if (string.IsNullOrEmpty(alias))
- channel.LastSrcId = "Last ID: " + e.SrcId;
- else
- channel.LastSrcId = "Last: " + alias;
// if we can, use the PI LC from the PI voice header as to keep all options intact
if ((e.FrameType == FrameType.DATA_SYNC) && (e.DataType == DMRDataType.VOICE_PI_HEADER))
@@ -322,6 +319,19 @@ namespace dvmconsole
callHistoryWindow.ChannelUnkeyed(cpgChannel.Name, (int)e.SrcId);
}
+ string alias = string.Empty;
+
+ try
+ {
+ alias = AliasTools.GetAliasByRid(system.RidAlias, (int)e.SrcId);
+ }
+ catch (Exception) { }
+
+ if (string.IsNullOrEmpty(alias))
+ channel.LastSrcId = "Last ID: " + e.SrcId;
+ else
+ channel.LastSrcId = "Last: " + alias;
+
if (e.FrameType == FrameType.VOICE_SYNC || e.FrameType == FrameType.VOICE)
{
byte[] ambe = new byte[FneSystemBase.DMR_AMBE_LENGTH_BYTES];
diff --git a/dvmconsole/MainWindow.P25.cs b/dvmconsole/MainWindow.P25.cs
index 663b270..28a0892 100644
--- a/dvmconsole/MainWindow.P25.cs
+++ b/dvmconsole/MainWindow.P25.cs
@@ -458,6 +458,7 @@ namespace dvmconsole
channel.Decoder = new MBEDecoder(MBE_MODE.IMBE_88BIT);
SlotStatus slot = systemStatuses[cpgChannel.Name];
+ channel.LastPktTime = pktTime;
// if this is an LDU1 see if this is the first LDU with HDU encryption data
if (e.DUID == P25DUID.LDU1)
@@ -498,6 +499,14 @@ namespace dvmconsole
Log.WriteLine($"({system.Name}) P25D: Traffic *CALL ENC PARMS * PEER {e.PeerId} SRC_ID {e.SrcId} TGID {e.DstId} ALGID {channel.algId} KID {channel.kId} [STREAM ID {e.StreamId}]");
}
+ // reset the channel state if we're not Rx
+ if (!channel.IsReceiving)
+ {
+ channel.Background = ChannelBox.BLUE_GRADIENT;
+ channel.VolumeMeterLevel = 0;
+ continue;
+ }
+
// is the call over?
if (((e.DUID == P25DUID.TDU) || (e.DUID == P25DUID.TDULC)) && (slot.RxType != fnecore.FrameType.TERMINATOR))
{
@@ -507,7 +516,7 @@ namespace dvmconsole
channel.Background = ChannelBox.BLUE_GRADIENT;
channel.VolumeMeterLevel = 0;
callHistoryWindow.ChannelUnkeyed(cpgChannel.Name, (int)e.SrcId);
- return;
+ continue;
}
// do background updates here -- this catches late entry
diff --git a/dvmconsole/MainWindow.xaml.cs b/dvmconsole/MainWindow.xaml.cs
index 2a222cf..0448ba0 100644
--- a/dvmconsole/MainWindow.xaml.cs
+++ b/dvmconsole/MainWindow.xaml.cs
@@ -40,6 +40,7 @@ using fnecore.DMR;
using fnecore.P25;
using fnecore.P25.KMM;
using fnecore.P25.LC.TSBK;
+using System.Net.Sockets;
namespace dvmconsole
{
@@ -123,9 +124,12 @@ namespace dvmconsole
private bool selectAll = false;
+ private CancellationTokenSource maintainenceCancelToken = new CancellationTokenSource();
+ private Task maintainenceTask = null;
+
/*
- ** Properties
- */
+ ** Properties
+ */
///
/// Codeplug
@@ -133,8 +137,8 @@ namespace dvmconsole
public Codeplug Codeplug { get; set; }
/*
- ** Methods
- */
+ ** Methods
+ */
///
/// Initializes a new instance of the class.
@@ -418,6 +422,20 @@ namespace dvmconsole
DisableCommandControls();
systemStatusBox.Background = ChannelBox.RED_GRADIENT;
systemStatusBox.ConnectionState = "Disconnected";
+
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
+ {
+ if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
+ continue;
+
+ if (channel.IsReceiving || channel.IsReceivingEncrypted)
+ {
+ channel.IsReceiving = false;
+ channel.IsReceivingEncrypted = false;
+ channel.Background = ChannelBox.BLUE_GRADIENT;
+ channel.VolumeMeterLevel = 0;
+ }
+ }
});
};
@@ -900,6 +918,22 @@ namespace dvmconsole
{
isShuttingDown = true;
+ // stop maintainence task
+ if (maintainenceTask != null)
+ {
+ maintainenceCancelToken.Cancel();
+
+ try
+ {
+ maintainenceTask.GetAwaiter().GetResult();
+ }
+ catch (OperationCanceledException) { /* stub */ }
+ finally
+ {
+ maintainenceCancelToken.Dispose();
+ }
+ }
+
waveIn.StopRecording();
fneSystemManager.ClearAll();
@@ -920,6 +954,57 @@ namespace dvmconsole
Application.Current.Shutdown();
}
+ ///
+ /// Internal maintainence routine.
+ ///
+ private async void Maintainence()
+ {
+ CancellationToken ct = maintainenceCancelToken.Token;
+ while (!isShuttingDown)
+ {
+ foreach (ChannelBox channel in selectedChannelsManager.GetSelectedChannels())
+ {
+ if (channel.SystemName == PLAYBACKSYS || channel.ChannelName == PLAYBACKCHNAME || channel.DstId == PLAYBACKTG)
+ continue;
+
+ Codeplug.System system = Codeplug.GetSystemForChannel(channel.ChannelName);
+ if (system == null)
+ continue;
+
+ Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channel.ChannelName);
+ if (cpgChannel == null)
+ continue;
+
+ PeerSystem fne = fneSystemManager.GetFneSystem(system.Name);
+ if (fne == null)
+ continue;
+
+ // check if the channel is stuck reporting Rx
+ if (channel.IsReceiving)
+ {
+ DateTime now = DateTime.Now;
+ TimeSpan dt = now - channel.LastPktTime;
+ if (dt.TotalMilliseconds > 2000) // 2 seconds is more then enough time -- the interpacket time for P25 is ~180ms and DMR is ~60ms
+ {
+ Log.WriteLine($"({system.Name}) P25D: Traffic *CALL TIMEOUT * TGID {channel.DstId} ALGID {channel.algId} KID {channel.kId}");
+ Dispatcher.Invoke(() =>
+ {
+ channel.IsReceiving = false;
+ channel.Background = ChannelBox.BLUE_GRADIENT;
+ channel.VolumeMeterLevel = 0;
+ });
+ }
+ }
+ }
+
+ try
+ {
+ await Task.Delay(1000, ct);
+ }
+ catch (TaskCanceledException) { /* stub */ }
+ }
+ }
+
/** NAudio Events */
///
@@ -1097,6 +1182,8 @@ namespace dvmconsole
btnGlobalPttDefaultBg = btnGlobalPtt.Background;
+ maintainenceTask = Task.Factory.StartNew(Maintainence, maintainenceCancelToken.Token);
+
// set window configuration
if (settingsManager.Maximized)
{
diff --git a/fnecore b/fnecore
index 78e74f9..054954f 160000
--- a/fnecore
+++ b/fnecore
@@ -1 +1 @@
-Subproject commit 78e74f9074270c530c86447b01ab06e7527d81bb
+Subproject commit 054954fad3e330e665cbbcb1eca67c927e26e425