Skip to content

Commit

Permalink
Using Standard view for Zoom, Pan and Tilt
Browse files Browse the repository at this point in the history
  • Loading branch information
asrulhadi committed Sep 21, 2023
1 parent 8a77d03 commit 45c6361
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 115 deletions.
42 changes: 6 additions & 36 deletions Models/Pelco.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private void PopulateFunction()
_ => (null, "null")
};
if (addTo is null) Debug.WriteLine($"Tak tahu nak tambah {mi.Name} kat mana");
//if (loc == "str2procB") Console.WriteLine("Adding to {1}: {0} => {2}", mi.Name, loc, nama);
if (loc == "str2procC") Console.WriteLine("Adding to {1}: {0} => {2}", mi.Name, loc, nama);
addTo?.Add(nama, mi);
}
}
Expand Down Expand Up @@ -91,48 +91,35 @@ The Checksum is calculated by performing the 8 bit (modulo 256) sum of the
payload bytes(bytes 2 through 6) in the message.
*/

public (byte[], Func<string, byte, byte[]>) Unknwon(string proc)
public (byte[], Func<string, byte, byte[]>) Unknown(string proc)
{
Debug.WriteLine("Function <{0}> tak dijumpai", proc);
byte[] r = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF };
byte[] r = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xBA, 0xBE };
return (r, (_, _) => r);
}
public (byte[], Func<string, byte, byte[]>) get([NotNull] Func<int, int, byte[]> proc, int addr, int data)
{
byte[] command = proc(addr, data);
var parser = get_parser_for(nameof(proc));
return (command, parser);
}
public (byte[], Func<string, byte, byte[]>) get(string proc, int addr)
{
if (!str2procA.ContainsKey(proc)) return Unknwon(proc);
if (!str2procA.ContainsKey(proc)) return Unknown(proc);
Debug.WriteLine("Command called: {0} with addr={1}", proc, addr);
byte[] command = (byte[])str2procA[proc]!.Invoke(this, new object[] { addr }) ?? new byte[] { };
return (command, Parse_General_Response);
}

public (byte[], Func<string, byte, byte[]>) get(string proc, int addr, int data)
{
if (!str2procC.ContainsKey(proc)) return Unknwon(proc);
if (!str2procC.ContainsKey(proc)) return Unknown(proc);
Debug.WriteLine("Command called: {0} with addr={1} data={2}", proc, addr, data);
byte[] command = (byte[])str2procC[proc]!.Invoke(this, new object[] { addr, data }) ?? new byte[] { };
return (command, Parse_General_Response);
}
public (byte[], Func<string, byte, byte[]>) get(string proc, int addr, bool cond)
{
if (!str2procB.ContainsKey(proc)) return Unknwon(proc);
if (!str2procB.ContainsKey(proc)) return Unknown(proc);
Debug.WriteLine("Command called: {0} with addr={1} cond={2}", proc, addr, cond);
byte[] command = (byte[])str2procB[proc]!.Invoke(this, new object[] { addr, cond }) ?? new byte[] { };
return (command, Parse_General_Response);
}

public (byte[], Func<string, byte, byte[]>) get([NotNull] Func<int, bool, byte[]> proc, int addr, bool cond)
{
byte[] command = proc(addr, cond);
var parser = get_parser_for(nameof(proc));
return (command, parser);
}

private byte SYNC = 0xff;

private byte[] command(int addr, int cmd, int data = 0)
Expand Down Expand Up @@ -907,23 +894,6 @@ public byte[] Query_Tilt_Response(int addr, int degrees)
return command(addr, 0x5b, degrees);
}

/* Query Zoom Position Response(0x5D)
The position is given as a ratio based on the device’s Zoom Limit setting.
This value can be converted into units of magnification by using the following formula:
current_magnification = (position / 65535) * zoom_limit
Where current_zoom_position and zoom_limit are given in units of magnification.
Example:
Given that the zoom limit of the device’s camera is X184,
position value is 1781, calculate the current magnification:
Current magnification = (1781 / 65535) * 184 = approximately X5.
Note: This message is sent in response to the Query Zoom Position(0x55) command.
*/
public byte[] Query_Zoom_Response(int addr, int value)
{
//'Extended Response' # ??? XXX
Expand Down
7 changes: 6 additions & 1 deletion TestingEO.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<GenerateAssemblyInfo>False</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
<None Remove=".gitignore" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.0.4" />
Expand Down
34 changes: 24 additions & 10 deletions ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using System.Threading.Tasks;

using Avalonia.Threading;

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

using DotNetty.Extensions;
Expand Down Expand Up @@ -62,12 +62,13 @@ public MainWindowViewModel()
this.WhenAnyValue(x => x.StatusChanged)
.Throttle(TimeSpan.FromMilliseconds(500))
.Subscribe(_ => ReceivedAll = ListInitC1.TrueForAll(d => d.Received) && ListInitC2.TrueForAll(d => d.Received));

//this.WhenAnyValue(x => x.ZoomLevel)
// //.Throttle(TimeSpan.FromMilliseconds(500))
// .Subscribe(x => Debug.WriteLine("ZoomLevel = {0}", x));

pelco.FunctionListReady = InitCommandList;
pelco.StartPopulate();

d = this.WhenAnyValue(x => x.ZoomLevel)
.Throttle(TimeSpan.FromMilliseconds(500))
.Subscribe(x => Debug.WriteLine("ZoomLevel = {0}", x));
}
public void InitReply()
{
Expand Down Expand Up @@ -176,6 +177,20 @@ [RelayCommand] private void PelcoPTZ(string cmd)
var camId = int.Parse(cmds[1].Split("=")[^1]);
var value = cmds.Length == 3 && int.TryParse(cmds[2], out int val) ? val : -1;
Debug.WriteLine($"{cmd} => {value}");
if (pelcoCmd.StartsWith("Set"))
{
// special case for degrees if value is negative
if (value < 0) value += 36000;
PelcoSpecific($"{cmds[0]}@{cmds[1]}@{value}");
}
else
{
if (pelcoCmd.StartsWith("Zoom")) PelcoSpecific(cmd);
else PelcoSpecific($"{cmd}@50");
// wait for a while - before send reset
Task.Delay(500).Wait();
PelcoSpecific($"Reset@CamId={camId}");
}
}
public void ConnectToEO()
{
Expand All @@ -200,7 +215,7 @@ public void ConnectToEO()
public async void CloseConnect()
{
if (!Connected) return;
await client.ShutdownAsync();
await client!.ShutdownAsync();
Connected = false;
InitReply();
client = null;
Expand Down Expand Up @@ -274,10 +289,9 @@ public void Closing(object? sender, CancelEventArgs e)
public ObservableCollection<string> ProcA { get; set; } = new();
public ObservableCollection<string> ProcB { get; set; } = new();
public ObservableCollection<string> ProcC { get; set; } = new();
[Reactive] public int StatusChanged { get; set; }
[Reactive] public bool ReceivedAll { get; set; } = false;
[Reactive] public bool Connected { get; set; } = false;
[Reactive] public double ZoomLevel { get; set; } = 0;
[ObservableProperty] private int statusChanged;
[ObservableProperty] private bool receivedAll = false;
[ObservableProperty] private bool connected = false;

#region InitC1
[Reactive] public DataChecked c1FF010059 { get; set; } // Response Pan Position
Expand Down
90 changes: 27 additions & 63 deletions Views/ControlPTZView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,41 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:icons="https://github.com/projektanker/icons.avalonia"
xmlns:v="using:TestingEO.Views"
xmlns:vm="using:TestingEO.ViewModels"
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
xmlns:iac="clr-namespace:Avalonia.Xaml.Interactions.Custom;assembly=Avalonia.Xaml.Interactions.Custom"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="TestingEO.Views.ControlPTZView"
x:CompileBindings="True"
x:DataType="vm:MainWindowViewModel"
>

<StackPanel Classes="V" HorizontalAlignment="Stretch" >
<StackPanel Classes="H">
<Label Content="Camera: " />
<ComboBox Name="Camera" SelectedIndex="0" MinWidth="120" >
<ComboBoxItem Content="Day" />
<ComboBoxItem Content="IR" />
</ComboBox>
</StackPanel>
<Grid ColumnDefinitions="Auto,Auto,*,Auto" RowDefinitions="*,*,*">
<Grid.Styles>
<Style Selector="RepeatButton">
<Setter Property="Interval" Value="1000" />
</Style>
</Grid.Styles>
<!-- Zoom -->
<Label Grid.Row="0" Grid.Column="0" Content="Zoom" />
<Button Grid.Row="0" Grid.Column="1" icons:Attached.Icon="mdi-minus-circle" Command="{Binding PelcoPTZCommand}" >
<Button.CommandParameter>
<MultiBinding StringFormat="Zoom_Wide@CamId={0}@Minus">
<Binding Path="#Camera.SelectedIndex" />
</MultiBinding>
</Button.CommandParameter>
</Button>
<Grid ColumnDefinitions="100,Auto,*,Auto" RowDefinitions="*">
<Label Grid.Column="0" Content="{Binding $parent[v:ControlPTZView].Text}" />
<Button Grid.Column="1" icons:Attached.Icon="mdi-minus-circle" Command="{Binding PelcoPTZCommand}" >
<Button.CommandParameter>
<MultiBinding StringFormat="\{0\}@CamId\=\{1\}">
<Binding Path="$parent[v:ControlPTZView].CmdN" />
<Binding Path="$parent[v:ControlPTZView].Camera" />
</MultiBinding>
</Button.CommandParameter>
</Button>

<Slider Name="ZoomV" Grid.Row="0" Grid.Column="2" Minimum="0" Maximum="65535" SmallChange="1" IsSnapToTickEnabled="True" TickFrequency="1" Value="{Binding ZoomLevel, Mode=OneWayToSource}" >
<!--
<i:Interaction.Behaviors>
<iac:ValueChangedTriggerBehavior Binding="{Binding $self.Value}">
<ia:InvokeCommandAction Command="{Binding PelcoPTZCommand}" >
<ia:InvokeCommandAction.CommandParameter>
<MultiBinding StringFormat="Zoom@CamId={0}@{1}">
<Binding Path="#Camera.SelectedIndex" />
<Binding Path="$self.Value" />
</MultiBinding>
</ia:InvokeCommandAction.CommandParameter>
</ia:InvokeCommandAction>
</iac:ValueChangedTriggerBehavior>
</i:Interaction.Behaviors>
-->
</Slider>
<Slider Name="Slider" Grid.Column="2"
Minimum="{Binding $parent[v:ControlPTZView].Min}" Maximum="{Binding $parent[v:ControlPTZView].Max}"
SmallChange="1" IsSnapToTickEnabled="True" TickFrequency="1" >
</Slider>

<Button Grid.Row="0" Grid.Column="3" icons:Attached.Icon="mdi-plus-circle" Command="{Binding PelcoPTZCommand}" >
<Button.CommandParameter>
<MultiBinding StringFormat="Zoom_Tele@CamId={0}@Plus">
<Binding Path="#Camera.SelectedIndex" />
</MultiBinding>
</Button.CommandParameter>
</Button>

<TextBox Grid.Row="0" Grid.Column="3" Text="{Binding #ZoomV.Value}">
<i:Interaction.Behaviors>
<iac:ValueChangedTriggerBehavior Binding="{Binding $self.Text}">
<ia:InvokeCommandAction Command="{Binding PelcoPTZCommand}" >
<ia:InvokeCommandAction.CommandParameter>
<MultiBinding StringFormat="Zoom@CamId={0}@{1}">
<Binding Path="#Camera.SelectedIndex" />
<Binding Path="$self.Text" />
</MultiBinding>
</ia:InvokeCommandAction.CommandParameter>
</ia:InvokeCommandAction>
</iac:ValueChangedTriggerBehavior>
</i:Interaction.Behaviors>
</TextBox>
</Grid>
</StackPanel>
<Button Grid.Column="3" icons:Attached.Icon="mdi-plus-circle" Command="{Binding PelcoPTZCommand}" >
<Button.CommandParameter>
<MultiBinding StringFormat="\{0\}@CamId\=\{1\}">
<Binding Path="$parent[v:ControlPTZView].CmdP" />
<Binding Path="$parent[v:ControlPTZView].Camera" />
</MultiBinding>
</Button.CommandParameter>
</Button>

</Grid>
</UserControl>
99 changes: 99 additions & 0 deletions Views/ControlPTZView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,110 @@
using System;
using Avalonia.Controls;
using DynamicData.Binding;
using System.Diagnostics;
using Avalonia;
using System.Windows.Input;
using System.Reactive.Linq;

namespace TestingEO.Views;

public partial class ControlPTZView : UserControl
{
/// <summary>
/// Identifies the <seealso cref="Command"/> avalonia property.
/// </summary>
public static readonly StyledProperty<ICommand?> CommandProperty =
AvaloniaProperty.Register<ControlPTZView, ICommand?>(nameof(Command));

/// <summary>
/// Gets or sets the command this action should invoke. This is a avalonia property.
/// </summary>
public ICommand? Command
{
get => GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}

public static readonly DirectProperty<ControlPTZView, String> TextProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, String>(nameof(Text), o => o.Text, (o, v) => o.Text = v);
private String _text;
public String Text
{
get => _text;
set => SetAndRaise(TextProperty, ref _text, value);
}

public static readonly DirectProperty<ControlPTZView, String> CmdNProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, String>(nameof(CmdN), o => o.CmdN, (o, v) => o.CmdN = v);
private String _cmdN;
public String CmdN
{
get => _cmdN;
set => SetAndRaise(CmdNProperty, ref _cmdN, value);
}

public static readonly DirectProperty<ControlPTZView, String> CmdPProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, String>(nameof(CmdP), o => o.CmdP, (o, v) => o.CmdP = v);
private String _cmdP;
public String CmdP
{
get => _cmdP;
set => SetAndRaise(CmdPProperty, ref _cmdP, value);
}

public static readonly DirectProperty<ControlPTZView, String> CmdSProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, String>(nameof(CmdS), o => o.CmdS, (o, v) => o.CmdS = v);
private String _cmdS;
public String CmdS
{
get => _cmdS;
set => SetAndRaise(CmdSProperty, ref _cmdS, value);
}

public static readonly DirectProperty<ControlPTZView, int> CameraProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, int>(nameof(Camera), o => o.Camera, (o, v) => o.Camera = v,
defaultBindingMode: Avalonia.Data.BindingMode.OneWay);
private int _camera;
public int Camera
{
get => _camera;
set => SetAndRaise(CameraProperty, ref _camera, value + 1);
}

public static readonly DirectProperty<ControlPTZView, int> MinProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, int>(nameof(Min), o => o.Min, (o, v) => o.Min = v,
defaultBindingMode: Avalonia.Data.BindingMode.OneWay);
private int _min;
public int Min
{
get => _min;
set => SetAndRaise(MinProperty, ref _min, value);
}

public static readonly DirectProperty<ControlPTZView, int> MaxProperty =
AvaloniaProperty.RegisterDirect<ControlPTZView, int>(nameof(Max), o => o.Max, (o, v) => o.Max = v,
defaultBindingMode: Avalonia.Data.BindingMode.OneWay);
private int _max;
public int Max
{
get => _max;
set => SetAndRaise(MaxProperty, ref _max, value);
}

public ControlPTZView()
{
InitializeComponent();

// monitor the changes
Slider.WhenPropertyChanged(t => t.Value, notifyOnInitialValue: false)
.Throttle(TimeSpan.FromSeconds(0.2))
.ObserveOn(ReactiveUI.RxApp.MainThreadScheduler)
.Subscribe(v => SliderValueChanged(v.Value));
}

private void SliderValueChanged(double value)
{
if (Command is null) return;
Command.Execute($"{CmdS}@CamId={Camera}@{value}");
}
}
Loading

0 comments on commit 45c6361

Please sign in to comment.