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