forked from amkovkov/GranuSightSoftware2
feat: Camera settings + some refactor
This commit is contained in:
@@ -1,12 +1,35 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
using GSS2.Core.Extentions;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
namespace GSS2.Core.Abstractions;
|
namespace GSS2.Core.Abstractions;
|
||||||
|
|
||||||
public abstract record ServiceConfigurationBase
|
public abstract record ServiceConfigurationBase
|
||||||
{
|
{
|
||||||
public virtual string ToString(string separator, int indent)
|
public virtual string ToString(string separator, int indent)
|
||||||
{
|
{
|
||||||
List<string?> strs = new List<string?>();
|
var strs = new List<string?>();
|
||||||
foreach (var prop in GetType().GetProperties())
|
foreach (var prop in GetType().GetProperties())
|
||||||
strs.Add($"{prop.Name}: {prop.GetValue(this)}");
|
{
|
||||||
|
if (prop.GetMethod?.IsStatic != false)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var value = prop.GetValue(this);
|
||||||
|
|
||||||
|
string? strValue = value switch
|
||||||
|
{
|
||||||
|
string str => str,
|
||||||
|
IConfigurationSection configurationSection => $"\n{(configurationSection.AsDictionary() as IDictionary).ToString(separator, indent * 2)}",
|
||||||
|
IDictionary dictionary => $"\n{dictionary.ToString(separator, indent * 2)}",
|
||||||
|
IEnumerable enumerable => $"\n{enumerable.ToString(separator, indent * 2)}",
|
||||||
|
object obj => obj?.ToString(),
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
strs.Add($"{prop.Name}: {strValue}");
|
||||||
|
}
|
||||||
return string.Join(separator, strs.Select(s => $"{new string(' ', indent)}{s}"));
|
return string.Join(separator, strs.Select(s => $"{new string(' ', indent)}{s}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace GSS2.Core.Extentions;
|
||||||
|
|
||||||
|
public static class ConfiurationSectionExtensions
|
||||||
|
{
|
||||||
|
public static Dictionary<string, string> AsDictionary(this IConfigurationSection values)
|
||||||
|
{
|
||||||
|
var dict = new Dictionary<string, string>();
|
||||||
|
foreach (var (key, value) in values.AsEnumerable())
|
||||||
|
if (value is not null)
|
||||||
|
dict.Add(key.Substring(values.Path.Length + 1), value);
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,7 +1,30 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
namespace GSS2.Core.Extentions;
|
namespace GSS2.Core.Extentions;
|
||||||
|
|
||||||
public static class EnumerableExtensions
|
public static class EnumerableExtensions
|
||||||
{
|
{
|
||||||
public static IEnumerable<(int i, T value)> Enumerate<T>(this IEnumerable<T> values) => values.Select((value, i) => (i, value));
|
public static IEnumerable<(int i, T value)> Enumerate<T>(this IEnumerable<T> values) => values.Select((value, i) => (i, value));
|
||||||
|
|
||||||
|
public static string ToString<TKey, TValue>(this IDictionary<TKey, TValue> values, string separator, int indent) => string.Join(separator, values.Select(v => $"{new string(' ', indent)} {v.Key}: {v.Value}"));
|
||||||
|
public static string ToString(this IDictionary values, string separator, int indent)
|
||||||
|
{
|
||||||
|
var strs = new List<string?>();
|
||||||
|
var enumerator = values.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
strs.Add($"{enumerator.Key}: {enumerator.Value}");
|
||||||
|
return string.Join(separator, strs.Select(s => $"{new string(' ', indent)}{s}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToString<TValue>(this IEnumerable<TValue> values, string separator, int indent) => (values as IEnumerable).ToString(separator, indent);
|
||||||
|
public static string ToString(this IEnumerable values, string separator, int indent)
|
||||||
|
{
|
||||||
|
var strs = new List<string?>();
|
||||||
|
var enumerator = values.GetEnumerator();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
strs.Add(enumerator.Current.ToString());
|
||||||
|
return string.Join(separator, strs.Select(s => $"{new string(' ', indent)}{s}"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<PackageReference Include="OpenCvSharp4.runtime.linux-arm" Version="4.11.0.20250506" />
|
<PackageReference Include="OpenCvSharp4.runtime.linux-arm" Version="4.11.0.20250506" />
|
||||||
<PackageReference Include="System.Device.Gpio" Version="4.0.1" />
|
<PackageReference Include="System.Device.Gpio" Version="4.0.1" />
|
||||||
<PackageReference Include="Iot.Device.Bindings" Version="4.0.1" />
|
<PackageReference Include="Iot.Device.Bindings" Version="4.0.1" />
|
||||||
<PackageReference Include="LibCameraSharp" Version="0.5.2-20260129-7" />
|
<PackageReference Include="LibCameraSharp" Version="0.5.2-20260216-4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ using System.Text;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
using LibCameraSharp;
|
using LibCameraSharp;
|
||||||
|
|
||||||
using GSS2.Core.Extentions;
|
using GSS2.Core.Extentions;
|
||||||
using GSS2.Core.Abstractions;
|
using GSS2.Core.Abstractions;
|
||||||
|
using GSS2.Core.Helpers;
|
||||||
|
|
||||||
using Stream = LibCameraSharp.Stream;
|
using Stream = LibCameraSharp.Stream;
|
||||||
|
|
||||||
@@ -55,17 +57,19 @@ public class CameraService : IDisposable
|
|||||||
ViewFinderFps = 30,
|
ViewFinderFps = 30,
|
||||||
ImageCaptureFrameBufferCount = 2,
|
ImageCaptureFrameBufferCount = 2,
|
||||||
ImageCaptureWidth = 4056,
|
ImageCaptureWidth = 4056,
|
||||||
ImageCaptureHeight = 3080
|
ImageCaptureHeight = 3080,
|
||||||
|
CameraHardSettings = null
|
||||||
};
|
};
|
||||||
|
|
||||||
public string CameraId { get; set; } = "";
|
public string CameraId { get; init; } = "";
|
||||||
public int ViewFinderFrameBufferCount { get; set; }
|
public int ViewFinderFrameBufferCount { get; init; }
|
||||||
public int ViewFinderWidth { get; set; }
|
public int ViewFinderWidth { get; init; }
|
||||||
public int ViewFinderHeight { get; set; }
|
public int ViewFinderHeight { get; init; }
|
||||||
public int ViewFinderFps { get; set; }
|
public int ViewFinderFps { get; init; }
|
||||||
public int ImageCaptureFrameBufferCount { get; set; }
|
public int ImageCaptureFrameBufferCount { get; init; }
|
||||||
public int ImageCaptureWidth { get; set; }
|
public int ImageCaptureWidth { get; init; }
|
||||||
public int ImageCaptureHeight { get; set; }
|
public int ImageCaptureHeight { get; init; }
|
||||||
|
public IConfigurationSection? CameraHardSettings { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _disposedValue;
|
private bool _disposedValue;
|
||||||
@@ -86,6 +90,7 @@ public class CameraService : IDisposable
|
|||||||
private System.Timers.ElapsedEventHandler? _viewFinderTimerElapsed = null;
|
private System.Timers.ElapsedEventHandler? _viewFinderTimerElapsed = null;
|
||||||
private EventHandler<Camera.RequestCompletedEventArgs>? _viewFinderRequestComplitedHandler = null;
|
private EventHandler<Camera.RequestCompletedEventArgs>? _viewFinderRequestComplitedHandler = null;
|
||||||
private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
|
private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource();
|
||||||
|
private readonly Dictionary<string, object> _cameraHardSettings = new Dictionary<string, object>();
|
||||||
|
|
||||||
public bool IsImageCapturing { get; private set; } = false;
|
public bool IsImageCapturing { get; private set; } = false;
|
||||||
public List<FrameBuffer> ViewFinderBufferQueue { get; private set; } = new List<FrameBuffer>();
|
public List<FrameBuffer> ViewFinderBufferQueue { get; private set; } = new List<FrameBuffer>();
|
||||||
@@ -106,6 +111,27 @@ public class CameraService : IDisposable
|
|||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_logger.LogInformation("Using following configuration: \n{}", _configuration.ToString("\n", 4));
|
_logger.LogInformation("Using following configuration: \n{}", _configuration.ToString("\n", 4));
|
||||||
|
|
||||||
|
|
||||||
|
if (_configuration.CameraHardSettings is not null)
|
||||||
|
{
|
||||||
|
foreach (var child in _configuration.CameraHardSettings.GetChildren())
|
||||||
|
{
|
||||||
|
object? value = child.Get<object>();
|
||||||
|
if (value is string)
|
||||||
|
{
|
||||||
|
_cameraHardSettings.Add(child.Key, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value = child.Get<object[]>();
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
_cameraHardSettings.Add(child.Key, value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_logger.LogWarning("Cannot serialize camera hard settings value: {}: {}", child.Key, child.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (autoInitialize)
|
if (autoInitialize)
|
||||||
Initialize(CancellationToken.None);
|
Initialize(CancellationToken.None);
|
||||||
}
|
}
|
||||||
@@ -279,6 +305,8 @@ public class CameraService : IDisposable
|
|||||||
throw new Exception($"Error while camera start {result}");
|
throw new Exception($"Error while camera start {result}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Camera settings: \n{}", GetSettings(4));
|
||||||
|
|
||||||
_state |= State.CameraStarted;
|
_state |= State.CameraStarted;
|
||||||
}
|
}
|
||||||
private void ViewFinderFrameBuffersAllocate(CancellationToken cancellationToken)
|
private void ViewFinderFrameBuffersAllocate(CancellationToken cancellationToken)
|
||||||
@@ -476,6 +504,63 @@ public class CameraService : IDisposable
|
|||||||
|
|
||||||
_state |= State.ImageCaptureFrameBuffersMapped;
|
_state |= State.ImageCaptureFrameBuffersMapped;
|
||||||
}
|
}
|
||||||
|
private string GetSettings(int indent = 0)
|
||||||
|
{
|
||||||
|
if (_camera is null)
|
||||||
|
throw new InvalidOperationException("Camera not initialized");
|
||||||
|
|
||||||
|
var controls = _camera.Controls.ToList();
|
||||||
|
controls.Sort((c1, c2) => (int)(c1.Key.Id - c2.Key.Id));
|
||||||
|
var strs = new List<List<string>>()
|
||||||
|
{
|
||||||
|
{["Id", "Name", "Type", "Direction", "IsArray", "Size", "Range", "Default", "Enumerators"]}
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (id, info) in controls)
|
||||||
|
{
|
||||||
|
strs.Add([
|
||||||
|
id.Id.ToString(),
|
||||||
|
id.Name.ToString(),
|
||||||
|
id.Type.ToString(),
|
||||||
|
id.Direction.ToString(),
|
||||||
|
id.IsArray.ToString(),
|
||||||
|
id.Size.ToString(),
|
||||||
|
$"{info.Min.ToString(true)}...{info.Max.ToString(true)}",
|
||||||
|
info.Def.ToString(true),
|
||||||
|
string.Join(", ", id.Enumerators.FirstOrDefault())
|
||||||
|
]);
|
||||||
|
foreach (var enumerator in id.Enumerators.Skip(1))
|
||||||
|
strs.Add([
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
string.Join(", ", enumerator)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
var maxLengths = new List<int>();
|
||||||
|
foreach (var str in strs)
|
||||||
|
foreach (var (i, s) in str.Enumerate())
|
||||||
|
{
|
||||||
|
if (i >= maxLengths.Count())
|
||||||
|
maxLengths.Add(0);
|
||||||
|
maxLengths[i] = Math.Max(maxLengths[i], s.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var (i, str) in strs.Enumerate())
|
||||||
|
{
|
||||||
|
foreach (var (j, s) in str.Enumerate())
|
||||||
|
sb.Append(new string(' ', indent) + string.Format($"{{0,-{maxLengths[j] + 1}}}", s));
|
||||||
|
if (i != strs.Count() - 1)
|
||||||
|
sb.AppendLine();
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<OpenCvSharp.Mat?> CaptureImage(CancellationToken cancellationToken, int framesCount = 1)
|
public async Task<OpenCvSharp.Mat?> CaptureImage(CancellationToken cancellationToken, int framesCount = 1)
|
||||||
{
|
{
|
||||||
@@ -510,7 +595,7 @@ public class CameraService : IDisposable
|
|||||||
while (planesData.Count() < framesCount)
|
while (planesData.Count() < framesCount)
|
||||||
{
|
{
|
||||||
effectiveCancellationToken.ThrowIfCancellationRequested();
|
effectiveCancellationToken.ThrowIfCancellationRequested();
|
||||||
_logger.LogDebug("Requested {} frame(s), now collected {}", framesCount, planesData.Count());
|
_logger.LogDebug("Requested {} frame(s), now collected: {}", framesCount, planesData.Count());
|
||||||
|
|
||||||
var oldPlanesDataCount = planesData.Count();
|
var oldPlanesDataCount = planesData.Count();
|
||||||
|
|
||||||
@@ -524,6 +609,8 @@ public class CameraService : IDisposable
|
|||||||
foreach (var buffer in buffers)
|
foreach (var buffer in buffers)
|
||||||
{
|
{
|
||||||
var request = _camera.CreateRequest(1);
|
var request = _camera.CreateRequest(1);
|
||||||
|
ApplySettings(request, _cameraHardSettings);
|
||||||
|
|
||||||
var result = request.AddBuffer(_imageCaptureStream, buffer);
|
var result = request.AddBuffer(_imageCaptureStream, buffer);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
throw new Exception("Error while request create");
|
throw new Exception("Error while request create");
|
||||||
@@ -669,6 +756,40 @@ public class CameraService : IDisposable
|
|||||||
IsImageCapturing = false;
|
IsImageCapturing = false;
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
private void ApplySettings(Request request, Dictionary<string, object> settings)
|
||||||
|
{
|
||||||
|
if (_camera is null)
|
||||||
|
throw new InvalidOperationException("Camera not initialized");
|
||||||
|
|
||||||
|
foreach (var (id, info) in _camera.Controls)
|
||||||
|
{
|
||||||
|
if (!settings.ContainsKey(id.Name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var value = settings[id.Name];
|
||||||
|
ControlValue? controlValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!id.IsArray)
|
||||||
|
controlValue = ControlValueHelper.ConvertToControlValue(value, id, info);
|
||||||
|
else
|
||||||
|
controlValue = ControlValueHelper.ConvertToControlArray(value, id, info);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Error while converting value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, value.GetType(), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controlValue is null)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Cannot convert to control value (control name:{}, control type:{}, value type:{}, value:{})", id.Name, id.Type, value.GetType(), value);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Controls.Set(id.Id, controlValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void StartViewFinder()
|
public void StartViewFinder()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,19 +28,19 @@ public class IlluminatorService : IDisposable
|
|||||||
MaxUv365PwmDuty = 1.0
|
MaxUv365PwmDuty = 1.0
|
||||||
};
|
};
|
||||||
|
|
||||||
public int WhitePwmPin { get; set; }
|
public int WhitePwmPin { get; init; }
|
||||||
public int Uv254PwmPin { get; set; }
|
public int Uv254PwmPin { get; init; }
|
||||||
public int Uv365PwmPin { get; set; }
|
public int Uv365PwmPin { get; init; }
|
||||||
public int WhiteRelayPin { get; set; }
|
public int WhiteRelayPin { get; init; }
|
||||||
public int Uv254RelayPin { get; set; }
|
public int Uv254RelayPin { get; init; }
|
||||||
public int Uv365RelayPin { get; set; }
|
public int Uv365RelayPin { get; init; }
|
||||||
public int FanPin { get; set; }
|
public int FanPin { get; init; }
|
||||||
|
|
||||||
public int PwmFrequency { get; set; }
|
public int PwmFrequency { get; init; }
|
||||||
|
|
||||||
public double MaxWhitePwmDuty { get; set; }
|
public double MaxWhitePwmDuty { get; init; }
|
||||||
public double MaxUv254PwmDuty { get; set; }
|
public double MaxUv254PwmDuty { get; init; }
|
||||||
public double MaxUv365PwmDuty { get; set; }
|
public double MaxUv365PwmDuty { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<int, int> PinPwmChips = new()
|
private static readonly Dictionary<int, int> PinPwmChips = new()
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class LightsService
|
|||||||
ServerAddress = "localhost:50051"
|
ServerAddress = "localhost:50051"
|
||||||
};
|
};
|
||||||
|
|
||||||
public string ServerAddress { get; set; } = string.Empty;
|
public string ServerAddress { get; init; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly ILogger<LightsService> _logger;
|
private readonly ILogger<LightsService> _logger;
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ public class TemperatureHumidityService : IDisposable
|
|||||||
Address = 0x45
|
Address = 0x45
|
||||||
};
|
};
|
||||||
|
|
||||||
public int Bus { get; set; }
|
public int Bus { get; init; }
|
||||||
public int Address { get; set; }
|
public int Address { get; init; }
|
||||||
}
|
}
|
||||||
private bool _disposedValue;
|
private bool _disposedValue;
|
||||||
private readonly ILogger<TemperatureHumidityService> _logger;
|
private readonly ILogger<TemperatureHumidityService> _logger;
|
||||||
|
|||||||
@@ -0,0 +1,250 @@
|
|||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
using LibCameraSharp;
|
||||||
|
|
||||||
|
namespace GSS2.Core.Helpers;
|
||||||
|
|
||||||
|
public static class ControlValueHelper
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<Type, Func<object, ControlId, object?>> _valueParsers = new()
|
||||||
|
{
|
||||||
|
{ typeof(bool ), (obj, _) => ParseValue<bool >(obj, bool .TryParse) },
|
||||||
|
{ typeof(byte ), (obj, _) => ParseValue<byte >(obj, byte .TryParse) },
|
||||||
|
{ typeof(float ), (obj, _) => ParseValue<float >(obj, float .TryParse) },
|
||||||
|
{ typeof(long ), (obj, _) => ParseValue<long >(obj, long .TryParse) },
|
||||||
|
{ typeof(ushort ), (obj, _) => ParseValue<ushort>(obj, ushort.TryParse) },
|
||||||
|
{ typeof(uint ), (obj, _) => ParseValue<uint >(obj, uint .TryParse) },
|
||||||
|
{ typeof(int ), ParseValueInt },
|
||||||
|
// int - особый случай потому что он может использовать ControlId.Enumerators для прообразования стокового "enum" в значение
|
||||||
|
{ typeof(Rectangle), ParseValueRectangle },
|
||||||
|
{ typeof(Size ), ParseValueSize },
|
||||||
|
{ typeof(Point ), ParseValuePoint }
|
||||||
|
};
|
||||||
|
private delegate bool ParseValueDelegate<T>(string s, out T value);
|
||||||
|
private static object? ParseValue<T>(object obj, ParseValueDelegate<T> parser) where T : struct
|
||||||
|
{
|
||||||
|
if (obj is T value)
|
||||||
|
return value;
|
||||||
|
if (obj is string str && parser(str, out var parsed))
|
||||||
|
return parsed;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static object? ParseValueInt(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
if (obj is int value)
|
||||||
|
return value;
|
||||||
|
if (obj is string str)
|
||||||
|
{
|
||||||
|
if (int.TryParse(str, out value))
|
||||||
|
return value;
|
||||||
|
var match = id.Enumerators.FirstOrDefault(e => e.Value == str);
|
||||||
|
if (!match.Equals(default(KeyValuePair<int, string>)))
|
||||||
|
return match.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static object? ParseValueRectangle(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
if (obj is Rectangle value)
|
||||||
|
return value;
|
||||||
|
if (obj is IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
var list = enumerable.Cast<string>();
|
||||||
|
if (list.Count() != 4)
|
||||||
|
return null;
|
||||||
|
if (!int.TryParse(list.Skip(0).First(), out var x))
|
||||||
|
return null;
|
||||||
|
if (!int.TryParse(list.Skip(1).First(), out var y))
|
||||||
|
return null;
|
||||||
|
if (!uint.TryParse(list.Skip(2).First(), out var width))
|
||||||
|
return null;
|
||||||
|
if (!uint.TryParse(list.Skip(3).First(), out var height))
|
||||||
|
return null;
|
||||||
|
return new Rectangle(x, y, width, height);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static object? ParseValueSize(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
if (obj is Size value)
|
||||||
|
return value;
|
||||||
|
if (obj is IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
var list = enumerable.Cast<string>();
|
||||||
|
if (list.Count() != 2)
|
||||||
|
return null;
|
||||||
|
if (!uint.TryParse(list.Skip(0).First(), out var width))
|
||||||
|
return null;
|
||||||
|
if (!uint.TryParse(list.Skip(1).First(), out var height))
|
||||||
|
return null;
|
||||||
|
return new Size(width, height);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static object? ParseValuePoint(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
if (obj is Point value)
|
||||||
|
return value;
|
||||||
|
if (obj is IEnumerable enumerable)
|
||||||
|
{
|
||||||
|
var list = enumerable.Cast<string>();
|
||||||
|
if (list.Count() != 4)
|
||||||
|
return null;
|
||||||
|
if (!int.TryParse(list.Skip(0).First(), out var x))
|
||||||
|
return null;
|
||||||
|
if (!int.TryParse(list.Skip(1).First(), out var y))
|
||||||
|
return null;
|
||||||
|
return new Point(x, y);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
private static T? ConvertValueStruct<T>(object obj, ControlId id) where T : struct
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (!_valueParsers.ContainsKey(type))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parsed = _valueParsers[type](obj, id);
|
||||||
|
var result = (T?)parsed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private static T? ConvertValueClass<T>(object obj, ControlId id) where T : class
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (!_valueParsers.ContainsKey(type))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parsed = _valueParsers[type](obj, id);
|
||||||
|
var result = (T?)parsed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private static readonly Dictionary<ControlTypeEnum, Func<object, ControlId, ControlValue?>> _controlValueConverters = new()
|
||||||
|
{
|
||||||
|
{ ControlTypeEnum.Bool , (obj, id) => { var v = ConvertValueStruct<bool >(obj, id); return v is null ? null : new ControlValueBool (v.Value); } },
|
||||||
|
{ ControlTypeEnum.Byte , (obj, id) => { var v = ConvertValueStruct<byte >(obj, id); return v is null ? null : new ControlValueByte (v.Value); } },
|
||||||
|
{ ControlTypeEnum.Float , (obj, id) => { var v = ConvertValueStruct<float >(obj, id); return v is null ? null : new ControlValueFloat (v.Value); } },
|
||||||
|
{ ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertValueStruct<int >(obj, id); return v is null ? null : new ControlValueInteger32 (v.Value); } },
|
||||||
|
{ ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertValueStruct<long >(obj, id); return v is null ? null : new ControlValueInteger64 (v.Value); } },
|
||||||
|
{ ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertValueStruct<ushort >(obj, id); return v is null ? null : new ControlValueUnsigned16(v.Value); } },
|
||||||
|
{ ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertValueStruct<uint >(obj, id); return v is null ? null : new ControlValueUnsigned32(v.Value); } },
|
||||||
|
{ ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertValueClass <Rectangle>(obj, id); return v is null ? null : new ControlValueRectangle (v); } },
|
||||||
|
{ ControlTypeEnum.Size , (obj, id) => { var v = ConvertValueClass <Size >(obj, id); return v is null ? null : new ControlValueSize (v); } },
|
||||||
|
{ ControlTypeEnum.Point , (obj, id) => { var v = ConvertValueClass <Point >(obj, id); return v is null ? null : new ControlValuePoint (v); } }
|
||||||
|
};
|
||||||
|
private static readonly Dictionary<ControlTypeEnum, Action<ControlValue, ControlId, ControlInfo>> _controlValueClapers = new()
|
||||||
|
{
|
||||||
|
{ ControlTypeEnum.Bool , (value, id, info) => { } },
|
||||||
|
{ ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByte typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||||
|
{ ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloat typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||||
|
{ ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32 typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||||
|
{ ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64 typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||||
|
{ ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16 typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } },
|
||||||
|
{ ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32 typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(Math.Clamp(typed.Get(), typedMin.Get(), typedMax.Get())); } }
|
||||||
|
};
|
||||||
|
public static ControlValue? ConvertToControlValue(object obj, ControlId id, ControlInfo info)
|
||||||
|
{
|
||||||
|
if (!_controlValueConverters.TryGetValue(id.Type, out var converter))
|
||||||
|
return null;
|
||||||
|
var controlValue = converter(obj, id);
|
||||||
|
if (controlValue is null)
|
||||||
|
return null;
|
||||||
|
if (!_controlValueClapers.TryGetValue(id.Type, out var clamper))
|
||||||
|
return controlValue;
|
||||||
|
clamper(controlValue, id, info);
|
||||||
|
return controlValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, Func<object, ControlId, object?>> _arrayParsers = new()
|
||||||
|
{
|
||||||
|
{ typeof(bool ), ParseArray<bool> },
|
||||||
|
{ typeof(byte ), ParseArray<byte> },
|
||||||
|
{ typeof(float ), ParseArray<float> },
|
||||||
|
{ typeof(long ), ParseArray<long> },
|
||||||
|
{ typeof(ushort ), ParseArray<ushort> },
|
||||||
|
{ typeof(uint ), ParseArray<uint> },
|
||||||
|
{ typeof(int ), ParseArray<int> },
|
||||||
|
{ typeof(Rectangle), ParseArray<Rectangle>},
|
||||||
|
{ typeof(Size ), ParseArray<Size> },
|
||||||
|
{ typeof(Point ), ParseArray<Point> }
|
||||||
|
};
|
||||||
|
private static object? ParseArray<T>(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
if (obj is not IEnumerable enumerable)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var list = enumerable.Cast<object>().ToList();
|
||||||
|
|
||||||
|
if ((ulong)list.Count != id.Size)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var array = new T[list.Count];
|
||||||
|
|
||||||
|
for (int i = 0; i < list.Count; i++)
|
||||||
|
{
|
||||||
|
var parsed = _valueParsers[typeof(T)](list[i], id);
|
||||||
|
if (parsed is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
array[i] = (T)parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
private static IEnumerable<T>? ConvertArray<T>(object obj, ControlId id)
|
||||||
|
{
|
||||||
|
var type = typeof(T);
|
||||||
|
|
||||||
|
if (!_arrayParsers.ContainsKey(type))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parsed = _arrayParsers[type](obj, id);
|
||||||
|
var result = (T[]?)parsed;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
private static readonly Dictionary<ControlTypeEnum, Func<object, ControlId, ControlValue?>> _controlArrayConverters = new()
|
||||||
|
{
|
||||||
|
{ ControlTypeEnum.Bool , (obj, id) => { var v = ConvertArray<bool >(obj, id); return v is null ? null : new ControlValueBoolSpan (v); } },
|
||||||
|
{ ControlTypeEnum.Byte , (obj, id) => { var v = ConvertArray<byte >(obj, id); return v is null ? null : new ControlValueByteSpan (v); } },
|
||||||
|
{ ControlTypeEnum.Float , (obj, id) => { var v = ConvertArray<float >(obj, id); return v is null ? null : new ControlValueFloatSpan (v); } },
|
||||||
|
{ ControlTypeEnum.Integer32 , (obj, id) => { var v = ConvertArray<int >(obj, id); return v is null ? null : new ControlValueInteger32Span (v); } },
|
||||||
|
{ ControlTypeEnum.Integer64 , (obj, id) => { var v = ConvertArray<long >(obj, id); return v is null ? null : new ControlValueInteger64Span (v); } },
|
||||||
|
{ ControlTypeEnum.Unsigned16, (obj, id) => { var v = ConvertArray<ushort >(obj, id); return v is null ? null : new ControlValueUnsigned16Span(v); } },
|
||||||
|
{ ControlTypeEnum.Unsigned32, (obj, id) => { var v = ConvertArray<uint >(obj, id); return v is null ? null : new ControlValueUnsigned32Span(v); } },
|
||||||
|
{ ControlTypeEnum.Rectangle , (obj, id) => { var v = ConvertArray<Rectangle>(obj, id); return v is null ? null : new ControlValueRectangleSpan (v); } },
|
||||||
|
{ ControlTypeEnum.Size , (obj, id) => { var v = ConvertArray<Size >(obj, id); return v is null ? null : new ControlValueSizeSpan (v); } },
|
||||||
|
{ ControlTypeEnum.Point , (obj, id) => { var v = ConvertArray<Point >(obj, id); return v is null ? null : new ControlValuePointSpan (v); } }
|
||||||
|
};
|
||||||
|
private static readonly Dictionary<ControlTypeEnum, Action<ControlValue, ControlId, ControlInfo>> _controlArrayClapers = new()
|
||||||
|
{
|
||||||
|
{ ControlTypeEnum.Bool , (value, id, info) => { } },
|
||||||
|
{ ControlTypeEnum.Byte , (value, id, info) => { if (value is ControlValueByteSpan typed && info.Min is ControlValueByte typedMin && info.Max is ControlValueByte typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Float , (value, id, info) => { if (value is ControlValueFloatSpan typed && info.Min is ControlValueFloat typedMin && info.Max is ControlValueFloat typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Integer32 , (value, id, info) => { if (value is ControlValueInteger32Span typed && info.Min is ControlValueInteger32 typedMin && info.Max is ControlValueInteger32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Integer64 , (value, id, info) => { if (value is ControlValueInteger64Span typed && info.Min is ControlValueInteger64 typedMin && info.Max is ControlValueInteger64 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Unsigned16, (value, id, info) => { if (value is ControlValueUnsigned16Span typed && info.Min is ControlValueUnsigned16 typedMin && info.Max is ControlValueUnsigned16 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Unsigned32, (value, id, info) => { if (value is ControlValueUnsigned32Span typed && info.Min is ControlValueUnsigned32 typedMin && info.Max is ControlValueUnsigned32 typedMax) typed.Set(typed.Get().Select(v => Math.Clamp(v, typedMin.Get(), typedMax.Get()))); } },
|
||||||
|
{ ControlTypeEnum.Rectangle , (value, id, info) => { if (value is ControlValueRectangleSpan typed && info.Min is ControlValueRectangle typedMin && info.Max is ControlValueRectangle typedMax) typed.Set(typed.Get().Select(v => new Rectangle(Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ),
|
||||||
|
Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y ),
|
||||||
|
Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ),
|
||||||
|
Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } },
|
||||||
|
{ ControlTypeEnum.Size , (value, id, info) => { if (value is ControlValueSizeSpan typed && info.Min is ControlValueSize typedMin && info.Max is ControlValueSize typedMax) typed.Set(typed.Get().Select(v => new Size( Math.Clamp(v.Width, typedMin.Get().Width, typedMax.Get().Width ),
|
||||||
|
Math.Clamp(v.Height, typedMin.Get().Height, typedMax.Get().Height)))); } },
|
||||||
|
{ ControlTypeEnum.Point , (value, id, info) => { if (value is ControlValuePointSpan typed && info.Min is ControlValuePoint typedMin && info.Max is ControlValuePoint typedMax) typed.Set(typed.Get().Select(v => new Point( Math.Clamp(v.X, typedMin.Get().X, typedMax.Get().X ),
|
||||||
|
Math.Clamp(v.Y, typedMin.Get().Y, typedMax.Get().Y )))); } },
|
||||||
|
};
|
||||||
|
public static ControlValue? ConvertToControlArray(object obj, ControlId id, ControlInfo info)
|
||||||
|
{
|
||||||
|
if (!_controlArrayConverters.ContainsKey(id.Type))
|
||||||
|
return null;
|
||||||
|
var controlValue = _controlArrayConverters[id.Type](obj, id);
|
||||||
|
if (controlValue is null)
|
||||||
|
return null;
|
||||||
|
if (!_controlArrayClapers.TryGetValue(id.Type, out var clamper))
|
||||||
|
return controlValue;
|
||||||
|
clamper(controlValue, id, info);
|
||||||
|
return controlValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ public sealed class LibCameraLogSink : IDisposable
|
|||||||
private readonly ILogger _loggerV4L2;
|
private readonly ILogger _loggerV4L2;
|
||||||
private readonly ILogger _loggerRPISTREAM;
|
private readonly ILogger _loggerRPISTREAM;
|
||||||
private readonly ILogger _loggerWRAPPER;
|
private readonly ILogger _loggerWRAPPER;
|
||||||
|
private readonly ILogger _loggerRPiAgc;
|
||||||
private bool _disposedValue;
|
private bool _disposedValue;
|
||||||
|
|
||||||
public LibCameraLogSink(ILoggerFactory loggerFactory)
|
public LibCameraLogSink(ILoggerFactory loggerFactory)
|
||||||
@@ -25,6 +26,7 @@ public sealed class LibCameraLogSink : IDisposable
|
|||||||
_loggerV4L2 = loggerFactory.CreateLogger("LibCamera-V4L2");
|
_loggerV4L2 = loggerFactory.CreateLogger("LibCamera-V4L2");
|
||||||
_loggerRPISTREAM = loggerFactory.CreateLogger("LibCamera-RPISTREAM");
|
_loggerRPISTREAM = loggerFactory.CreateLogger("LibCamera-RPISTREAM");
|
||||||
_loggerWRAPPER = loggerFactory.CreateLogger("LibCamera-WRAPPER");
|
_loggerWRAPPER = loggerFactory.CreateLogger("LibCamera-WRAPPER");
|
||||||
|
_loggerRPiAgc = loggerFactory.CreateLogger("LibCamera-RPiAgc");
|
||||||
|
|
||||||
Log.Initialize();
|
Log.Initialize();
|
||||||
Log.SetLevel("Camera", Log.LogLevelEnum.DEBUG);
|
Log.SetLevel("Camera", Log.LogLevelEnum.DEBUG);
|
||||||
@@ -34,6 +36,7 @@ public sealed class LibCameraLogSink : IDisposable
|
|||||||
Log.SetLevel("V4L2", Log.LogLevelEnum.DEBUG);
|
Log.SetLevel("V4L2", Log.LogLevelEnum.DEBUG);
|
||||||
Log.SetLevel("RPISTREAM", Log.LogLevelEnum.DEBUG);
|
Log.SetLevel("RPISTREAM", Log.LogLevelEnum.DEBUG);
|
||||||
Log.SetLevel("WRAPPER", Log.LogLevelEnum.DEBUG);
|
Log.SetLevel("WRAPPER", Log.LogLevelEnum.DEBUG);
|
||||||
|
Log.SetLevel("RPiAgc", Log.LogLevelEnum.DEBUG);
|
||||||
Log.LogReceived += OnLogReceived;
|
Log.LogReceived += OnLogReceived;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +58,7 @@ public sealed class LibCameraLogSink : IDisposable
|
|||||||
"V4L2" => _loggerV4L2,
|
"V4L2" => _loggerV4L2,
|
||||||
"RPISTREAM" => _loggerRPISTREAM,
|
"RPISTREAM" => _loggerRPISTREAM,
|
||||||
"WRAPPER" => _loggerWRAPPER,
|
"WRAPPER" => _loggerWRAPPER,
|
||||||
|
"RPiAgc" => _loggerRPiAgc,
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,13 @@ public partial class MainViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly ILogger<MainViewModel> _logger;
|
private readonly ILogger<MainViewModel> _logger;
|
||||||
private readonly CameraService _cameraService;
|
private readonly CameraService _cameraService;
|
||||||
|
private readonly IlluminatorService _illuminatorService;
|
||||||
[ObservableProperty] private CameraViewModel _cameraViewModel;
|
[ObservableProperty] private CameraViewModel _cameraViewModel;
|
||||||
[ObservableProperty] private ResourcesViewModel _resourcesViewModel;
|
[ObservableProperty] private ResourcesViewModel _resourcesViewModel;
|
||||||
[ObservableProperty] private OpenCvSharp.Mat? _image;
|
[ObservableProperty] private OpenCvSharp.Mat? _image1;
|
||||||
|
[ObservableProperty] private OpenCvSharp.Mat? _image2;
|
||||||
|
[ObservableProperty] private OpenCvSharp.Mat? _image3;
|
||||||
|
[ObservableProperty] private OpenCvSharp.Mat? _image4;
|
||||||
[ObservableProperty] private IRelayCommand _buttonCaptureClick;
|
[ObservableProperty] private IRelayCommand _buttonCaptureClick;
|
||||||
[ObservableProperty] private IRelayCommand _buttonUpClick;
|
[ObservableProperty] private IRelayCommand _buttonUpClick;
|
||||||
[ObservableProperty] private IRelayCommand _buttonDownClick;
|
[ObservableProperty] private IRelayCommand _buttonDownClick;
|
||||||
@@ -20,15 +24,29 @@ public partial class MainViewModel : ViewModelBase
|
|||||||
[ObservableProperty] private bool _canScrollForward;
|
[ObservableProperty] private bool _canScrollForward;
|
||||||
[ObservableProperty] private bool _canScrollBackward;
|
[ObservableProperty] private bool _canScrollBackward;
|
||||||
|
|
||||||
public MainViewModel(ILogger<MainViewModel> logger, CameraViewModel cameraViewModel, ResourcesViewModel resourcesViewModel, CameraService cameraService)
|
public MainViewModel(ILogger<MainViewModel> logger, CameraViewModel cameraViewModel, ResourcesViewModel resourcesViewModel, CameraService cameraService, IlluminatorService illuminatorService)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_cameraService = cameraService;
|
_cameraService = cameraService;
|
||||||
|
_illuminatorService = illuminatorService;
|
||||||
CameraViewModel = cameraViewModel;
|
CameraViewModel = cameraViewModel;
|
||||||
ResourcesViewModel = resourcesViewModel;
|
ResourcesViewModel = resourcesViewModel;
|
||||||
ButtonCaptureClick = new RelayCommand(() =>
|
ButtonCaptureClick = new RelayCommand(() =>
|
||||||
{
|
{
|
||||||
Task.Run(() => cameraService.CaptureImage(CancellationToken.None, 10).ContinueWith(image => Image = image.Result));
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
_illuminatorService.TurnOn();
|
||||||
|
_illuminatorService.SetIntensity(1, 0, 0);
|
||||||
|
await _cameraService.CaptureImage(CancellationToken.None, 3).ContinueWith(image => Image1 = image.Result);
|
||||||
|
_illuminatorService.SetIntensity(0, 1, 1);
|
||||||
|
await _cameraService.CaptureImage(CancellationToken.None, 3).ContinueWith(image => Image2 = image.Result);
|
||||||
|
_illuminatorService.SetIntensity(0, 1, 0);
|
||||||
|
await _cameraService.CaptureImage(CancellationToken.None, 3).ContinueWith(image => Image3 = image.Result);
|
||||||
|
_illuminatorService.SetIntensity(0, 0, 1);
|
||||||
|
await _cameraService.CaptureImage(CancellationToken.None, 3).ContinueWith(image => Image4 = image.Result);
|
||||||
|
_illuminatorService.SetIntensity(0, 0, 0);
|
||||||
|
_illuminatorService.TurnOff();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
ButtonUpClick = new RelayCommand(() => CurrentIndex--);
|
ButtonUpClick = new RelayCommand(() => CurrentIndex--);
|
||||||
ButtonDownClick = new RelayCommand(() => CurrentIndex++);
|
ButtonDownClick = new RelayCommand(() => CurrentIndex++);
|
||||||
|
|||||||
@@ -10,12 +10,17 @@
|
|||||||
<Button Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsEnabled="{Binding CanScrollBackward}" Command="{Binding ButtonUpClick}"/>
|
<Button Grid.Row="0" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsEnabled="{Binding CanScrollBackward}" Command="{Binding ButtonUpClick}"/>
|
||||||
<Button Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsEnabled="{Binding CanScrollForward}" Command="{Binding ButtonDownClick}"/>
|
<Button Grid.Row="1" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsEnabled="{Binding CanScrollForward}" Command="{Binding ButtonDownClick}"/>
|
||||||
<core:SectionPanel Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" ClipToBounds="True" CanScrollForward="{Binding CanScrollForward}" CanScrollBackward="{Binding CanScrollBackward}" CurrentIndex="{Binding CurrentIndex}">
|
<core:SectionPanel Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" ClipToBounds="True" CanScrollForward="{Binding CanScrollForward}" CanScrollBackward="{Binding CanScrollBackward}" CurrentIndex="{Binding CurrentIndex}">
|
||||||
<ContentControl VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding ResourcesViewModel}" />
|
|
||||||
<ContentControl VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding CameraViewModel}" />
|
|
||||||
<Grid ColumnDefinitions="100,*">
|
<Grid ColumnDefinitions="100,*">
|
||||||
<Button Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Command="{Binding ButtonCaptureClick}"/>
|
<Button Grid.Column="0" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Command="{Binding ButtonCaptureClick}"/>
|
||||||
<core:OpenCvImage Grid.Column="1" Image="{Binding Image}" Background="Red" />
|
<Grid Grid.Column="1" ColumnDefinitions="*,*" RowDefinitions="*,*" >
|
||||||
|
<core:OpenCvImage Grid.Column="0" Grid.Row="0" Image="{Binding Image1}" />
|
||||||
|
<core:OpenCvImage Grid.Column="1" Grid.Row="0" Image="{Binding Image2}" />
|
||||||
|
<core:OpenCvImage Grid.Column="0" Grid.Row="1" Image="{Binding Image3}" />
|
||||||
|
<core:OpenCvImage Grid.Column="1" Grid.Row="1" Image="{Binding Image4}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<ContentControl VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding ResourcesViewModel}" />
|
||||||
|
<!-- <ContentControl VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Content="{Binding CameraViewModel}" /> -->
|
||||||
</core:SectionPanel>
|
</core:SectionPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -32,19 +32,55 @@
|
|||||||
"Uv365RelayPin": 20,
|
"Uv365RelayPin": 20,
|
||||||
"FanPin": 21,
|
"FanPin": 21,
|
||||||
"PwmFrequency": 200,
|
"PwmFrequency": 200,
|
||||||
"MaxWhitePwmDuty": 1.0,
|
"MaxWhitePwmDuty": 0.03,
|
||||||
"MaxUv254PwmDuty": 1.0,
|
"MaxUv254PwmDuty": 1.0,
|
||||||
"MaxUv365PwmDuty": 1.0
|
"MaxUv365PwmDuty": 1.0
|
||||||
},
|
},
|
||||||
"Camera": {
|
"Camera": {
|
||||||
"CameraId": "",
|
"CameraId": "/base/axi/pcie@1000120000/rp1/i2c@70000/imx477@1a",
|
||||||
"ViewFinderFrameBufferCount": 5,
|
"ViewFinderFrameBufferCount": 5,
|
||||||
"ViewFinderWidth": 4056,
|
"ViewFinderWidth": 4056,
|
||||||
"ViewFinderHeight": 3040,
|
"ViewFinderHeight": 3040,
|
||||||
"ViewFinderFps": 30,
|
"ViewFinderFps": 30,
|
||||||
"ImageCaptureFrameBufferCount": 2,
|
"ImageCaptureFrameBufferCount": 5,
|
||||||
"ImageCaptureWidth": 4056,
|
"ImageCaptureWidth": 4056,
|
||||||
"ImageCaptureHeight": 3040
|
"ImageCaptureHeight": 3040,
|
||||||
|
"CameraHardSettings": {
|
||||||
|
"AeEnable": false,
|
||||||
|
"ExposureValue": 8.0,
|
||||||
|
"ExposureTime": 1000000,
|
||||||
|
"ExposureTimeMode": "ExposureTimeModeManual",
|
||||||
|
"AnalogueGain": 1.0,
|
||||||
|
"AnalogueGainMode": "AnalogueGainModeManual",
|
||||||
|
"AeFlickerMode": "FlickerOff",
|
||||||
|
"AeFlickerPeriod": 5000,
|
||||||
|
"Brightness": 0.0,
|
||||||
|
"Contrast": 1.0,
|
||||||
|
"AwbEnable": true,
|
||||||
|
"ColourGains": [
|
||||||
|
3.3,
|
||||||
|
1.5
|
||||||
|
],
|
||||||
|
"ColourTemperature": 5500,
|
||||||
|
"Saturation": 1.0,
|
||||||
|
"Sharpness": 1.0,
|
||||||
|
"ScalerCrop": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4056,
|
||||||
|
3040
|
||||||
|
],
|
||||||
|
"FrameDurationLimits": [
|
||||||
|
100000,
|
||||||
|
694434742
|
||||||
|
],
|
||||||
|
"HdrMode": "HdrModeOff",
|
||||||
|
"NoiseReductionMode": "NoiseReductionModeOff",
|
||||||
|
"StatsOutputEnable": true,
|
||||||
|
"SyncMode": "SyncModeOff",
|
||||||
|
"SyncFrames": 1,
|
||||||
|
"CnnEnableInputTensor": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"TemperatureHumidity": {
|
"TemperatureHumidity": {
|
||||||
"Bus": 6,
|
"Bus": 6,
|
||||||
|
|||||||
@@ -40,9 +40,12 @@ public partial class ResourcesViewModel : ViewModelBase, IDisposable
|
|||||||
|
|
||||||
public ResourcesViewModel(ComputeResourcesService computeResourcesService, ILogger<ResourcesViewModel> logger)
|
public ResourcesViewModel(ComputeResourcesService computeResourcesService, ILogger<ResourcesViewModel> logger)
|
||||||
{
|
{
|
||||||
_computeResourcesService = computeResourcesService;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
_logger.LogInformation("Initialization");
|
||||||
|
|
||||||
|
_computeResourcesService = computeResourcesService;
|
||||||
|
|
||||||
ResourceChartViewModel CreateChartViewModel(string name, double minLimit, double maxLimit, double minStep)
|
ResourceChartViewModel CreateChartViewModel(string name, double minLimit, double maxLimit, double minStep)
|
||||||
{
|
{
|
||||||
return new ResourceChartViewModel(
|
return new ResourceChartViewModel(
|
||||||
@@ -130,12 +133,12 @@ public partial class ResourcesViewModel : ViewModelBase, IDisposable
|
|||||||
_updateTimer.Elapsed += UpdateTimerElapsed;
|
_updateTimer.Elapsed += UpdateTimerElapsed;
|
||||||
_updateTimer.AutoReset = true;
|
_updateTimer.AutoReset = true;
|
||||||
_updateTimer.Start();
|
_updateTimer.Start();
|
||||||
_logger.LogInformation("{} initialized", nameof(ResourcesViewModel));
|
|
||||||
|
_logger.LogInformation("Initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnBoundsChanged(Rect value)
|
partial void OnBoundsChanged(Rect value)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("BoundsChanged {}", value);
|
|
||||||
if (value.Width < 300)
|
if (value.Width < 300)
|
||||||
{
|
{
|
||||||
IsChartsColumnVisible = false;
|
IsChartsColumnVisible = false;
|
||||||
@@ -153,16 +156,6 @@ public partial class ResourcesViewModel : ViewModelBase, IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
partial void OnIsChartsColumnVisibleChanged(bool value)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("IsChartsColumnVisibleChanged {}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
partial void OnIsBarsColumnVisibleChanged(bool value)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("IsBarsColumnVisibleChanged {}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea) => Update();
|
private void UpdateTimerElapsed(object? sender, System.Timers.ElapsedEventArgs ea) => Update();
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user