Add tab support (#12)

pull/15/head
Lorenzo L. Romero 1 month ago committed by GitHub
parent 1b90f403f2
commit 8eda11c88c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -29,12 +29,14 @@ systems:
#aliasPath: "Full/Path/To/alias.yml" #aliasPath: "Full/Path/To/alias.yml"
# #
# Zones # Zones (each zone becomes a tab)
# #
zones: zones:
# Textual name of the zone # Textual name of the zone (this becomes the tab name)
- name: "Zone 1" - name: "Primary"
# List of channels # Background color of the tab in the tab bar, in hex
tabColor: "#FF0000"
# List of channels in this zone/tab
channels: channels:
# Textual name of channel # Textual name of channel
- name: "Channel 1" - name: "Channel 1"
@ -57,7 +59,8 @@ zones:
system: "System 1" system: "System 1"
tgid: "15003" tgid: "15003"
- name: "Zone 2" - name: "Secondary"
tabColor: "#00FF00"
channels: channels:
- name: "Channel A" - name: "Channel A"
system: "System 1" system: "System 1"
@ -65,6 +68,9 @@ zones:
- name: "Channel B" - name: "Channel B"
system: "System 1" system: "System 1"
tgid: "16002" tgid: "16002"
- name: "Emergency"
channels:
- name: "Channel C" - name: "Channel C"
system: "System 1" system: "System 1"
tgid: "16002" tgid: "16002"

@ -10,6 +10,7 @@
* Copyright (C) 2024-2025 Caleb, K4PHP * Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Steven Jennison, KD8RHO * Copyright (C) 2025 Steven Jennison, KD8RHO
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
* *
*/ */
@ -45,7 +46,7 @@ namespace dvmconsole
/// </summary> /// </summary>
public List<System> Systems { get; set; } public List<System> Systems { get; set; }
/// <summary> /// <summary>
/// List of zones. /// List of zones (each zone becomes a tab).
/// </summary> /// </summary>
public List<Zone> Zones { get; set; } public List<Zone> Zones { get; set; }
@ -140,6 +141,10 @@ namespace dvmconsole
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary> /// <summary>
/// Tab Color in Hex (#RRGGBB)
/// </summary>
public string TabColor { get; set; }
/// <summary>
/// List of channels in the zone. /// List of channels in the zone.
/// </summary> /// </summary>
public List<Channel> Channels { get; set; } public List<Channel> Channels { get; set; }

@ -751,9 +751,14 @@ namespace dvmconsole.Controls
await Task.Delay(500); await Task.Delay(500);
if (pttToggleMode) if (pttToggleMode)
PttButton_Click(sender, e); {
// Toggle mode: toggle PttState and invoke clicked event
PttState = !PttState;
PTTButtonClicked?.Invoke(sender, this);
}
else else
{ {
// Normal mode: set PttState to true and invoke pressed event
PTTButtonPressed?.Invoke(sender, this); PTTButtonPressed?.Invoke(sender, this);
PttState = true; PttState = true;
} }

@ -8,6 +8,7 @@
* @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0) * @license AGPLv3 License (https://opensource.org/licenses/AGPL-3.0)
* *
* Copyright (C) 2025 Caleb, K4PHP * Copyright (C) 2025 Caleb, K4PHP
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
* *
*/ */
@ -91,51 +92,56 @@ namespace dvmconsole
{ {
KeyStatusItems.Clear(); KeyStatusItems.Clear();
foreach (var child in mainWindow.channelsCanvas.Children) // Iterate through all canvases (all tabs plus the original) so we see
// keys for channels on every tab, not just the original canvas.
foreach (var canvas in mainWindow.GetAllCanvases())
{ {
if (child == null) foreach (var child in canvas.Children)
{ {
Log.WriteLine("A child in ChannelsCanvas.Children is null."); if (child == null)
continue; {
Log.WriteLine("A child in canvas.Children is null.");
continue;
}
if (!(child is ChannelBox channelBox))
{
continue;
}
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
Log.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
Log.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
if (cpgChannel.GetKeyId() == 0 || cpgChannel.GetAlgoId() == 0)
continue;
if (channelBox.Crypter == null)
{
Log.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
bool hasKey = channelBox.Crypter.HasKey();
KeyStatusItems.Add(new KeyStatusItem
{
ChannelName = channelBox.ChannelName,
AlgId = $"0x{cpgChannel.GetAlgoId():X2}",
KeyId = $"0x{cpgChannel.GetKeyId():X4}",
KeyStatus = hasKey ? "Key Available" : "No Key"
});
} }
if (!(child is ChannelBox channelBox))
{
continue;
}
Codeplug.System system = Codeplug.GetSystemForChannel(channelBox.ChannelName);
if (system == null)
{
Log.WriteLine($"System not found for {channelBox.ChannelName}");
continue;
}
Codeplug.Channel cpgChannel = Codeplug.GetChannelByName(channelBox.ChannelName);
if (cpgChannel == null)
{
Log.WriteLine($"Channel not found for {channelBox.ChannelName}");
continue;
}
if (cpgChannel.GetKeyId() == 0 || cpgChannel.GetAlgoId() == 0)
continue;
if (channelBox.Crypter == null)
{
Log.WriteLine($"Crypter is null for channel {channelBox.ChannelName}");
continue;
}
bool hasKey = channelBox.Crypter.HasKey();
KeyStatusItems.Add(new KeyStatusItem
{
ChannelName = channelBox.ChannelName,
AlgId = $"0x{cpgChannel.GetAlgoId():X2}",
KeyId = $"0x{cpgChannel.GetKeyId():X4}",
KeyStatus = hasKey ? "Key Available" : "No Key"
});
} }
}); });
} }

@ -9,6 +9,7 @@
* *
* Copyright (C) 2024-2025 Caleb, K4PHP * Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
* *
*/ */
@ -305,6 +306,9 @@ namespace dvmconsole
channel.IsReceiving = true; channel.IsReceiving = true;
channel.PeerId = e.PeerId; channel.PeerId = e.PeerId;
channel.RxStreamId = e.StreamId; channel.RxStreamId = e.StreamId;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
systemStatuses[cpgChannel.Name + e.Slot].RxStart = pktTime; systemStatuses[cpgChannel.Name + e.Slot].RxStart = pktTime;
Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} [STREAM ID {e.StreamId}]");
@ -354,6 +358,9 @@ namespace dvmconsole
channel.IsReceiving = false; channel.IsReceiving = false;
channel.PeerId = 0; channel.PeerId = 0;
channel.RxStreamId = 0; channel.RxStreamId = 0;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
TimeSpan callDuration = pktTime - systemStatuses[cpgChannel.Name + e.Slot].RxStart; TimeSpan callDuration = pktTime - systemStatuses[cpgChannel.Name + e.Slot].RxStart;
Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} DUR {callDuration} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) DMRD: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} TS {e.Slot} DUR {callDuration} [STREAM ID {e.StreamId}]");

@ -9,6 +9,7 @@
* *
* Copyright (C) 2024-2025 Caleb, K4PHP * Copyright (C) 2024-2025 Caleb, K4PHP
* Copyright (C) 2025 Bryan Biedenkapp, N2PLL * Copyright (C) 2025 Bryan Biedenkapp, N2PLL
* Copyright (C) 2025 Lorenzo L Romero, K2LLR
* *
*/ */
@ -521,6 +522,9 @@ namespace dvmconsole
channel.IsReceiving = true; channel.IsReceiving = true;
channel.PeerId = e.PeerId; channel.PeerId = e.PeerId;
channel.RxStreamId = e.StreamId; channel.RxStreamId = e.StreamId;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
slot.RxStart = pktTime; slot.RxStart = pktTime;
Log.WriteLine($"({system.Name}) P25D: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} ALGID {channel.algId} KID {channel.kId} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) P25D: Traffic *CALL START * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} ALGID {channel.algId} KID {channel.kId} [STREAM ID {e.StreamId}]");
@ -553,6 +557,9 @@ namespace dvmconsole
channel.IsReceiving = false; channel.IsReceiving = false;
channel.PeerId = 0; channel.PeerId = 0;
channel.RxStreamId = 0; channel.RxStreamId = 0;
// Update tab audio indicator
Dispatcher.Invoke(() => UpdateTabAudioIndicatorForChannel(channel));
TimeSpan callDuration = pktTime - slot.RxStart; TimeSpan callDuration = pktTime - slot.RxStart;
Log.WriteLine($"({system.Name}) P25D: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} DUR {callDuration} [STREAM ID {e.StreamId}]"); Log.WriteLine($"({system.Name}) P25D: Traffic *CALL END * PEER {e.PeerId} SYS {system.Name} SRC_ID {e.SrcId} TGID {e.DstId} DUR {callDuration} [STREAM ID {e.StreamId}]");

@ -15,10 +15,11 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="24"/> <RowDefinition Height="24"/>
<RowDefinition Height="52" /> <RowDefinition Height="52" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Menu materialDesign:MenuAssist.TopLevelMenuItemHeight="24" Grid.ColumnSpan="2"> <Menu materialDesign:MenuAssist.TopLevelMenuItemHeight="24" Grid.ColumnSpan="2" Background="{DynamicResource MaterialDesignPaper}">
<MenuItem Header="_File"> <MenuItem Header="_File">
<MenuItem Header="_Open Codeplug..." Click="OpenCodeplug_Click" /> <MenuItem Header="_Open Codeplug..." Click="OpenCodeplug_Click" />
<Separator /> <Separator />
@ -63,8 +64,8 @@
</MenuItem> </MenuItem>
</Menu> </Menu>
<ToolBarTray Grid.Row="1"> <ToolBarTray Grid.Row="1" Background="{DynamicResource MaterialDesignPaper}">
<ToolBar ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}"> <ToolBar ClipToBounds="False" Style="{StaticResource MaterialDesignToolBar}" Background="{DynamicResource MaterialDesignPaper}">
<!-- Application Banner --> <!-- Application Banner -->
<Image Width="224" Height="50" VerticalAlignment="Center" HorizontalAlignment="Left" Source="/dvmconsole;component/Assets/logo.png" Stretch="UniformToFill"> <Image Width="224" Height="50" VerticalAlignment="Center" HorizontalAlignment="Left" Source="/dvmconsole;component/Assets/logo.png" Stretch="UniformToFill">
<Image.RenderTransform> <Image.RenderTransform>
@ -167,8 +168,13 @@
</ToolBar> </ToolBar>
</ToolBarTray> </ToolBarTray>
<!-- Channel Widget Canvas --> <!-- Tab Control for Resource Views -->
<ScrollViewer x:Name="canvasScrollViewer" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Grid.Row="3" Grid.ColumnSpan="2"> <TabControl x:Name="resourceTabs" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="2" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
materialDesign:ColorZoneAssist.Mode="PrimaryDark" Style="{StaticResource MaterialDesignFilledTabControl}">
</TabControl>
<!-- Channel Widget Canvas (will be moved to first tab) -->
<ScrollViewer x:Name="canvasScrollViewer" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Visibility="Collapsed">
<Canvas x:Name="channelsCanvas" VerticalAlignment="Top"> <Canvas x:Name="channelsCanvas" VerticalAlignment="Top">
<Canvas.Background> <Canvas.Background>
<ImageBrush x:Name="channelsCanvasBg" ImageSource="/dvmconsole;component/Assets/bg_main_hd_light.png" Stretch="UniformToFill" /> <ImageBrush x:Name="channelsCanvasBg" ImageSource="/dvmconsole;component/Assets/bg_main_hd_light.png" Stretch="UniformToFill" />

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save

Powered by TurnKey Linux.