hawkeye camera support(not tested)
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\framework\Inspectron.HawkEye\Inspectron.HawkEye.csproj" />
|
||||||
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
using Inspectron.HawkEye.Protocol;
|
||||||
|
using OpenCvSharp;
|
||||||
|
using Serilog;
|
||||||
|
using System.Net;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
|
using CameraSettings = Inspectron.HawkEye.Protocol.CameraSettings;
|
||||||
|
|
||||||
|
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye
|
||||||
|
{
|
||||||
|
public class HawkeyeCameraImageSource : IImageSource, IVisionBuilderModule
|
||||||
|
{
|
||||||
|
private readonly HawkeyeSettings _settings;
|
||||||
|
private ImageClientTCP _client;
|
||||||
|
private CameraSettings _cameraSettings;
|
||||||
|
|
||||||
|
|
||||||
|
public HawkeyeCameraImageSource(HawkeyeSettings settings)
|
||||||
|
{
|
||||||
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
|
||||||
|
_client = new ImageClientTCP(new IPEndPoint(IPAddress.Parse("192.168.3.15"), 27001),
|
||||||
|
IPAddress.Parse(_settings.Adapter));
|
||||||
|
_client.ImageReceived += _client_ImageReceived;
|
||||||
|
_client.SettingsReceived += _client_SettingsReceived;
|
||||||
|
_client.Connect();
|
||||||
|
Log.Information("Connected to Hawkeye camera at {Adapter}", _settings.Adapter, 27001);
|
||||||
|
|
||||||
|
}
|
||||||
|
private void _client_SettingsReceived(CameraSettings obj)
|
||||||
|
{
|
||||||
|
ApplySettings(obj);
|
||||||
|
}
|
||||||
|
public void ApplySettings(CameraSettings obj)
|
||||||
|
{
|
||||||
|
_cameraSettings = UICameraSettings.LoadSettingsLocal(_settings.SettingsFile).ToCameraSettings();
|
||||||
|
_client.ApplySettings(_cameraSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Mat> GetImage(CancellationToken token)
|
||||||
|
{
|
||||||
|
|
||||||
|
_lastImage = null;
|
||||||
|
if (_imageAquisitionTaskSource != null && !_imageAquisitionTaskSource.Task.IsCanceled)
|
||||||
|
{
|
||||||
|
_imageAquisitionTaskSource.SetCanceled(CancellationToken.None);
|
||||||
|
_imageAquisitionTaskSource = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
_imageAquisitionTaskSource= new TaskCompletionSource<Mat>();
|
||||||
|
_client.Trigger();
|
||||||
|
return _imageAquisitionTaskSource.Task;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskCompletionSource<Mat>? _imageAquisitionTaskSource;
|
||||||
|
private Mat _lastImage;
|
||||||
|
byte[] _flipBuffer = new byte[2000 * 2000];
|
||||||
|
private void _client_ImageReceived(byte[] obj)
|
||||||
|
{
|
||||||
|
Log.Debug("Got image on {adapter}", _settings.Adapter);
|
||||||
|
FlipLines(obj, _flipBuffer);
|
||||||
|
obj = _flipBuffer;
|
||||||
|
var pinnedArray = GCHandle.Alloc(obj, GCHandleType.Pinned);
|
||||||
|
var pointer = pinnedArray.AddrOfPinnedObject();
|
||||||
|
|
||||||
|
Mat image = new Mat(_cameraSettings.ImageSettings.Lines, _cameraSettings.ImageSettings.SensorWidth,
|
||||||
|
MatType.CV_8UC1, pointer);
|
||||||
|
|
||||||
|
var xCrop = _cameraSettings.ImageSettings.SensorWidth - _cameraSettings.ImageWidth - _cameraSettings.OffsetX;
|
||||||
|
|
||||||
|
// crop the image with opencv
|
||||||
|
_lastImage = image[0, _cameraSettings.ImageSettings.Lines, xCrop,
|
||||||
|
xCrop + _cameraSettings.ImageWidth].Clone();
|
||||||
|
|
||||||
|
if (_cameraSettings.BayerFilter)
|
||||||
|
{
|
||||||
|
_lastImage = BayerFilter(_lastImage);
|
||||||
|
}
|
||||||
|
|
||||||
|
_imageAquisitionTaskSource!.SetResult(_lastImage);
|
||||||
|
|
||||||
|
pinnedArray.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mat BayerFilter(Mat image)
|
||||||
|
{
|
||||||
|
// Assumes input is a single-channel Bayer pattern image (CV_8UC1)
|
||||||
|
// Output is a 3-channel BGR image (CV_8UC3)
|
||||||
|
if (image == null || image.Empty())
|
||||||
|
throw new ArgumentException("Input image is null or empty.", nameof(image));
|
||||||
|
|
||||||
|
Mat bgrImage = new Mat();
|
||||||
|
// Use OpenCV's demosaicing function for Bayer BG pattern
|
||||||
|
Cv2.CvtColor(image, bgrImage, ColorConversionCodes.BayerBG2BGR);
|
||||||
|
|
||||||
|
return bgrImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void FlipLines(byte[] src, byte[] dst)
|
||||||
|
{
|
||||||
|
int srcStride = _cameraSettings.ImageSettings.SensorWidth;
|
||||||
|
int dstStride = _cameraSettings.ImageSettings.SensorWidth;
|
||||||
|
int pixelSize = 1;
|
||||||
|
int copySize = srcStride * pixelSize;
|
||||||
|
var lines = _cameraSettings.ImageSettings.Lines;
|
||||||
|
for (int line = 0; line < lines; line += 4)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int srcPos = (line + i) * srcStride;
|
||||||
|
int dstPos = (line + (4 - i)) * dstStride;
|
||||||
|
for (int j = 0; j < copySize; j++)
|
||||||
|
{
|
||||||
|
dst[dstPos + j] = src[srcPos + j];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitializeModule()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/HawkeyeSettings.cs
Normal file
25
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/HawkeyeSettings.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using Inspectron.Settings;
|
||||||
|
using Inspectron.Settings.Attributes;
|
||||||
|
using VisionBuilder.UI.Common;
|
||||||
|
|
||||||
|
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||||
|
|
||||||
|
public class HawkeyeSettings(string CameraName): ISettings
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public string Adapter { get; set; }="192.168.1.100";
|
||||||
|
|
||||||
|
[File("*.jcnf")]
|
||||||
|
public string SettingsFile { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public void RegisterSettings(InspectronSettings settings)
|
||||||
|
{
|
||||||
|
settings.RegisterSimple(this, () => Adapter, $"{CameraName}/Sources/Hawkeye", nameof(Adapter));
|
||||||
|
settings.RegisterSimple(this, () => SettingsFile, $"{CameraName}/Sources/Hawkeye", nameof(SettingsFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
119
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/UICameraSettings.cs
Normal file
119
Hawkeye.VisionBuilder.UI.Sources.Hawkeye/UICameraSettings.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using Inspectron.HawkEye.Protocol;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||||
|
|
||||||
|
public class UICameraSettings
|
||||||
|
{
|
||||||
|
public UICameraSettings()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public UICameraSettings(CameraSettings cameraSettings)
|
||||||
|
{
|
||||||
|
Shutter = cameraSettings.ImageSettings.Shutter;
|
||||||
|
Gain = cameraSettings.ImageSettings.Gain;
|
||||||
|
OffsetX = cameraSettings.OffsetX;
|
||||||
|
SensorWidth = cameraSettings.ImageSettings.SensorWidth;
|
||||||
|
Width = cameraSettings.ImageWidth;
|
||||||
|
Lines = cameraSettings.ImageSettings.Lines;
|
||||||
|
Divider = cameraSettings.ImageSettings.Divider;
|
||||||
|
Light1 = cameraSettings.LightPwm1;
|
||||||
|
Light2 = cameraSettings.LightPwm2;
|
||||||
|
TriggerEnabled = cameraSettings.ImageSettings.UseExternalTrigger == 1;
|
||||||
|
Name = cameraSettings.Name;
|
||||||
|
CaptureBuffer = cameraSettings.ImageSettings.CaptureBuffer;
|
||||||
|
RescaleWidth = cameraSettings.RescaleWidth;
|
||||||
|
MinorCutoff = cameraSettings.MinorCutoff;
|
||||||
|
BayerFilter = cameraSettings.BayerFilter;
|
||||||
|
LaserTrigger = cameraSettings.LaserTrigger;
|
||||||
|
LaserTriggerDelay = cameraSettings.LaserTriggerDelay;
|
||||||
|
FlipLines = cameraSettings.FlipLines;
|
||||||
|
TriggerLights = cameraSettings.TriggerLights;
|
||||||
|
MirrorX = cameraSettings.MirrorX;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public CameraSettings ToCameraSettings()
|
||||||
|
{
|
||||||
|
return new CameraSettings()
|
||||||
|
{
|
||||||
|
ImageSettings = new ImageSettings()
|
||||||
|
{
|
||||||
|
Lines = Lines,
|
||||||
|
CaptureBuffer = CaptureBuffer,
|
||||||
|
Divider = Divider,
|
||||||
|
UseExternalTrigger = TriggerEnabled ? 1 : 0,
|
||||||
|
Gain = Gain,
|
||||||
|
Shutter = Shutter,
|
||||||
|
SensorWidth = SensorWidth
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
LightPwm1 = Light1,
|
||||||
|
LightPwm2 = Light2,
|
||||||
|
OffsetX = OffsetX,
|
||||||
|
ImageWidth = Width,
|
||||||
|
RescaleWidth = RescaleWidth,
|
||||||
|
MinorCutoff = MinorCutoff,
|
||||||
|
BayerFilter = BayerFilter,
|
||||||
|
LaserTrigger = LaserTrigger,
|
||||||
|
LaserTriggerDelay = LaserTriggerDelay,
|
||||||
|
FlipLines = FlipLines,
|
||||||
|
TriggerLights = TriggerLights,
|
||||||
|
MirrorX = MirrorX
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UICameraSettings LoadSettingsLocal(string path)
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<UICameraSettings>(File.ReadAllText(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int LaserTriggerDelay { get; set; }
|
||||||
|
|
||||||
|
public bool LaserTrigger { get; set; }
|
||||||
|
|
||||||
|
public bool TriggerEnabled { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public int CaptureBuffer { get; set; }
|
||||||
|
|
||||||
|
public bool FlipLines { get; set; }
|
||||||
|
|
||||||
|
public int Shutter { get; set; }
|
||||||
|
|
||||||
|
public int Gain { get; set; }
|
||||||
|
|
||||||
|
public int OffsetX { get; set; }
|
||||||
|
|
||||||
|
public int SensorWidth { get; set; }
|
||||||
|
|
||||||
|
public int Width { get; set; }
|
||||||
|
|
||||||
|
public int RescaleWidth { get; set; }
|
||||||
|
|
||||||
|
public int Lines { get; set; }
|
||||||
|
|
||||||
|
public int Divider { get; set; }
|
||||||
|
|
||||||
|
public int Light1 { get; set; }
|
||||||
|
|
||||||
|
public int Light2 { get; set; }
|
||||||
|
|
||||||
|
public bool TriggerLights { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public int MinorCutoff { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public bool BayerFilter { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public bool MirrorX { get; set; }
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ namespace Inspectron.Camera.UEye
|
|||||||
int cameraIdx;
|
int cameraIdx;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public IDSImageSource(IDSImageSourceSettings settings)
|
public IDSImageSource(IDSImageSourceSettings settings)
|
||||||
{
|
{
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
@@ -68,8 +67,7 @@ namespace Inspectron.Camera.UEye
|
|||||||
|
|
||||||
Byte[] u8img;
|
Byte[] u8img;
|
||||||
_camera.Memory.CopyToArray(idx, out u8img);
|
_camera.Memory.CopyToArray(idx, out u8img);
|
||||||
//Console.WriteLine($"Image size: {size.Width}x{size.Height}, channels: {channels}({mode}), data size: {u8img.Length}, pitch: {pitch}");
|
|
||||||
//Console.ReadLine();
|
|
||||||
Mat mat = new Mat(size.Height,pitch, MatType.CV_8UC(channels));
|
Mat mat = new Mat(size.Height,pitch, MatType.CV_8UC(channels));
|
||||||
mat.SetArray(u8img);
|
mat.SetArray(u8img);
|
||||||
return mat;
|
return mat;
|
||||||
@@ -82,11 +80,11 @@ namespace Inspectron.Camera.UEye
|
|||||||
|
|
||||||
uEye.Defines.Status statusRet = 0;
|
uEye.Defines.Status statusRet = 0;
|
||||||
|
|
||||||
// Open _camera
|
// Open camera
|
||||||
statusRet = _camera.Init(cameraIdx);
|
statusRet = _camera.Init(cameraIdx);
|
||||||
if (statusRet != uEye.Defines.Status.Success)
|
if (statusRet != uEye.Defines.Status.Success)
|
||||||
{
|
{
|
||||||
throw new Exception("_camera initializing failed: "+statusRet);
|
throw new Exception("Camera initialization failed: "+statusRet);
|
||||||
}
|
}
|
||||||
uEye.Types.SensorInfo info = new uEye.Types.SensorInfo();
|
uEye.Types.SensorInfo info = new uEye.Types.SensorInfo();
|
||||||
_camera.Information.GetSensorInfo(out info);
|
_camera.Information.GetSensorInfo(out info);
|
||||||
@@ -101,7 +99,6 @@ namespace Inspectron.Camera.UEye
|
|||||||
if (statusRet != uEye.Defines.Status.Success)
|
if (statusRet != uEye.Defines.Status.Success)
|
||||||
{
|
{
|
||||||
throw new Exception("Allocate Memory failed");
|
throw new Exception("Allocate Memory failed");
|
||||||
|
|
||||||
}
|
}
|
||||||
_camera.EventFrame += onFrameEvent;
|
_camera.EventFrame += onFrameEvent;
|
||||||
try
|
try
|
||||||
|
|||||||
14
Hawkeye.VisionBuilder.Workflow/.claude/settings.local.json
Normal file
14
Hawkeye.VisionBuilder.Workflow/.claude/settings.local.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(find:*)",
|
||||||
|
"Bash(for file in UnionOperation.cs HatsOperation.cs OpeningOperation.cs DilationOperation.cs ClosingOperation.cs)",
|
||||||
|
"Bash(do echo \"=== $file ===\")",
|
||||||
|
"Bash(tail:*)",
|
||||||
|
"Bash(done)",
|
||||||
|
"Bash(dotnet build:*)",
|
||||||
|
"Bash(ls:*)"
|
||||||
|
],
|
||||||
|
"deny": []
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -90,8 +90,9 @@ namespace Hawkeye.VisionBuilder.Workflow
|
|||||||
{
|
{
|
||||||
return Regex.Replace(typeName.Replace("Operation", ""), "(\\B[A-Z]+?(?=[A-Z][^A-Z])|\\B[A-Z]+?(?=[^A-Z]))", (useSpaces?" ":"")+"$1");
|
return Regex.Replace(typeName.Replace("Operation", ""), "(\\B[A-Z]+?(?=[A-Z][^A-Z])|\\B[A-Z]+?(?=[^A-Z]))", (useSpaces?" ":"")+"$1");
|
||||||
}
|
}
|
||||||
|
[NotForTool]
|
||||||
public bool Result { get; set; } = true;
|
public bool Result { get; set; } = true;
|
||||||
|
[NotForTool]
|
||||||
public string ResultString
|
public string ResultString
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -104,7 +105,12 @@ namespace Hawkeye.VisionBuilder.Workflow
|
|||||||
[NotForTool]
|
[NotForTool]
|
||||||
public string Label { get; set; }
|
public string Label { get; set; }
|
||||||
|
|
||||||
|
[NotForTool]
|
||||||
|
public bool Enabled { get; set; } = true;
|
||||||
|
|
||||||
|
[NotForTool]
|
||||||
public TimeSpan ExecutionTime { get; set; }
|
public TimeSpan ExecutionTime { get; set; }
|
||||||
|
[NotForTool]
|
||||||
public string ExecutionTimeString
|
public string ExecutionTimeString
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -132,7 +138,7 @@ namespace Hawkeye.VisionBuilder.Workflow
|
|||||||
public Dictionary<string, object> GetParameters()
|
public Dictionary<string, object> GetParameters()
|
||||||
{
|
{
|
||||||
|
|
||||||
var props = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
var props = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||||
.Where(x=>!x.HasAttribute(typeof(NotForToolAttribute)));
|
.Where(x=>!x.HasAttribute(typeof(NotForToolAttribute)));
|
||||||
return props.ToDictionary(x => x.Name, x => x.GetValue(this)!);
|
return props.ToDictionary(x => x.Name, x => x.GetValue(this)!);
|
||||||
}
|
}
|
||||||
@@ -142,7 +148,7 @@ namespace Hawkeye.VisionBuilder.Workflow
|
|||||||
foreach (KeyValuePair<string, object> pair in parameters)
|
foreach (KeyValuePair<string, object> pair in parameters)
|
||||||
{
|
{
|
||||||
this.GetType().GetProperty(pair.Key,
|
this.GetType().GetProperty(pair.Key,
|
||||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
BindingFlags.Public | BindingFlags.Instance )
|
||||||
?.SetValue(this,pair.Value);
|
?.SetValue(this,pair.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,6 +281,23 @@ namespace Hawkeye.VisionBuilder.Workflow
|
|||||||
Label = br.ReadString();
|
Label = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
dict[nameof(Id)] = Id.ToString();
|
||||||
|
dict[nameof(Label)] = Label;
|
||||||
|
dict[nameof(Enabled)] = Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
if (dict.ContainsKey(nameof(Id)))
|
||||||
|
Id = new Guid(dict[nameof(Id)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(Label)))
|
||||||
|
Label = dict[nameof(Label)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(Enabled)))
|
||||||
|
Enabled = Convert.ToBoolean(dict[nameof(Enabled)]);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetError(string eMessage)
|
public void SetError(string eMessage)
|
||||||
{
|
{
|
||||||
Status=eMessage;
|
Status=eMessage;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ public class BaseOperationDecorator
|
|||||||
_baseOperation = baseOperation;
|
_baseOperation = baseOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool Result => Operation.Result;
|
public bool Result => Operation.Result;
|
||||||
public string ResultString => Operation.ToString();
|
public string ResultString => Operation.ToString();
|
||||||
public string TypeName => Operation.TypeName;
|
public string TypeName => Operation.TypeName;
|
||||||
@@ -21,4 +22,10 @@ public class BaseOperationDecorator
|
|||||||
public string ExecutionTimeString => Operation.ExecutionTimeString;
|
public string ExecutionTimeString => Operation.ExecutionTimeString;
|
||||||
|
|
||||||
public BaseOperation Operation => _baseOperation;
|
public BaseOperation Operation => _baseOperation;
|
||||||
|
|
||||||
|
public bool IsEnabled
|
||||||
|
{
|
||||||
|
get => _baseOperation.Enabled;
|
||||||
|
set => _baseOperation.Enabled = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -122,4 +122,17 @@ public class AnomalyAI: BaseOperation
|
|||||||
_modelFilePath.Format = br.ReadString();
|
_modelFilePath.Format = br.ReadString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -181,4 +181,54 @@ public class ArrayModelMatchingOperation:BaseOperation
|
|||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
ReloadModelInfo();
|
ReloadModelInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
dict[nameof(Margin)] = Margin;
|
||||||
|
dict[nameof(Amount)] = Amount;
|
||||||
|
dict[nameof(ModelName)] = ModelName;
|
||||||
|
|
||||||
|
// Save ArrayHorizontalElement SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_BlockSizeX"] = SearchArea.BlockSize.X;
|
||||||
|
dict["SearchArea_BlockSizeY"] = SearchArea.BlockSize.Y;
|
||||||
|
dict["SearchArea_BlockCount"] = SearchArea.BlockCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(Margin)))
|
||||||
|
Margin = Convert.ToInt32(dict[nameof(Margin)]);
|
||||||
|
if (dict.ContainsKey(nameof(Amount)))
|
||||||
|
Amount = Convert.ToInt32(dict[nameof(Amount)]);
|
||||||
|
if (dict.ContainsKey(nameof(ModelName)))
|
||||||
|
ModelName = dict[nameof(ModelName)].ToString();
|
||||||
|
|
||||||
|
// Load ArrayHorizontalElement SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable") || dict.ContainsKey("SearchArea_LocationX") || dict.ContainsKey("SearchArea_BlockSizeX"))
|
||||||
|
{
|
||||||
|
SearchArea = new ArrayHorizontalElement();
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_BlockSizeX") && dict.ContainsKey("SearchArea_BlockSizeY"))
|
||||||
|
SearchArea.BlockSize = new Vector2(Convert.ToSingle(dict["SearchArea_BlockSizeX"]), Convert.ToSingle(dict["SearchArea_BlockSizeY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_BlockCount"))
|
||||||
|
SearchArea.BlockCount = Convert.ToInt32(dict["SearchArea_BlockCount"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReloadModelInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -120,5 +120,18 @@ public class BackgroundSeparationModelOperation:BaseOperation,IHaveOrigin
|
|||||||
ModelName = br.ReadString();
|
ModelName = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelName)] = ModelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelName)))
|
||||||
|
ModelName = dict[nameof(ModelName)].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
public OriginElement Origin { get; set; }
|
public OriginElement Origin { get; set; }
|
||||||
}
|
}
|
||||||
@@ -30,4 +30,20 @@ public class Color128BinaryOperation:ColorAIOperation
|
|||||||
FilterClasses = br.ReadString();
|
FilterClasses = br.ReadString();
|
||||||
ModelFilePath.Path = br.ReadString();
|
ModelFilePath.Path = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -110,4 +110,20 @@ public class Color128HalfOperation:BaseOperation
|
|||||||
FilterClasses = br.ReadString();
|
FilterClasses = br.ReadString();
|
||||||
ModelFilePath.Path = br.ReadString();
|
ModelFilePath.Path = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,4 +33,20 @@ public class Color128SimpleOperation: ColorAIOperation
|
|||||||
FilterClasses = br.ReadString();
|
FilterClasses = br.ReadString();
|
||||||
ModelFilePath.Path = br.ReadString();
|
ModelFilePath.Path = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,27 @@ public class ColorModelOperation : ColorAIOperation
|
|||||||
var absPath = Path.GetFullPath(Path.Combine(dir, ModelFilePath.Path));
|
var absPath = Path.GetFullPath(Path.Combine(dir, ModelFilePath.Path));
|
||||||
ModelFilePath.Path = absPath;
|
ModelFilePath.Path = absPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
{
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
// get current directory
|
||||||
|
var dir = Directory.GetCurrentDirectory();
|
||||||
|
// get absolute path
|
||||||
|
var absPath = Path.GetFullPath(Path.Combine(dir, ModelFilePath.Path));
|
||||||
|
ModelFilePath.Path = absPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -156,4 +156,23 @@ public class ModelAIOperation: BaseOperation
|
|||||||
FilterClasses = br.ReadString();
|
FilterClasses = br.ReadString();
|
||||||
IsCategorical = br.ReadBoolean();
|
IsCategorical = br.ReadBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(IsCategorical)] = IsCategorical;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(IsCategorical)))
|
||||||
|
IsCategorical = Convert.ToBoolean(dict[nameof(IsCategorical)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -145,4 +145,17 @@ public class MultichannelAI: BaseOperation
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -147,4 +147,23 @@ public class RawModelAIOperation: BaseOperation
|
|||||||
FilterClasses = br.ReadString();
|
FilterClasses = br.ReadString();
|
||||||
IsCategorical = br.ReadBoolean();
|
IsCategorical = br.ReadBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
dict[nameof(FilterClasses)] = FilterClasses;
|
||||||
|
dict[nameof(IsCategorical)] = IsCategorical;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(FilterClasses)))
|
||||||
|
FilterClasses = dict[nameof(FilterClasses)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(IsCategorical)))
|
||||||
|
IsCategorical = Convert.ToBoolean(dict[nameof(IsCategorical)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -139,4 +139,17 @@ public class YoloDetectionOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
ModelFilePath.Path = br.ReadString();
|
ModelFilePath.Path = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ModelFilePath)] = ModelFilePath.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ModelFilePath)))
|
||||||
|
ModelFilePath.Path = dict[nameof(ModelFilePath)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -112,4 +112,38 @@ public class YoloPickDetected:BaseOperation
|
|||||||
MinArea = br.ReadInt32();
|
MinArea = br.ReadInt32();
|
||||||
MaxArea = br.ReadInt32();
|
MaxArea = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(SlotName)] = SlotName;
|
||||||
|
dict[nameof(TypeId)] = TypeId;
|
||||||
|
dict[nameof(MinWidth)] = MinWidth;
|
||||||
|
dict[nameof(MaxWidth)] = MaxWidth;
|
||||||
|
dict[nameof(MinHeight)] = MinHeight;
|
||||||
|
dict[nameof(MaxHeight)] = MaxHeight;
|
||||||
|
dict[nameof(MinArea)] = MinArea;
|
||||||
|
dict[nameof(MaxArea)] = MaxArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(SlotName)))
|
||||||
|
SlotName = dict[nameof(SlotName)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(TypeId)))
|
||||||
|
TypeId = Convert.ToInt32(dict[nameof(TypeId)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinWidth)))
|
||||||
|
MinWidth = Convert.ToInt32(dict[nameof(MinWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxWidth)))
|
||||||
|
MaxWidth = Convert.ToInt32(dict[nameof(MaxWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinHeight)))
|
||||||
|
MinHeight = Convert.ToInt32(dict[nameof(MinHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxHeight)))
|
||||||
|
MaxHeight = Convert.ToInt32(dict[nameof(MaxHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinArea)))
|
||||||
|
MinArea = Convert.ToInt32(dict[nameof(MinArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxArea)))
|
||||||
|
MaxArea = Convert.ToInt32(dict[nameof(MaxArea)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,4 +35,20 @@ public class CannyOperation:BaseOperation
|
|||||||
Threshold1 = br.ReadInt32();
|
Threshold1 = br.ReadInt32();
|
||||||
Threshold2 = br.ReadInt32();
|
Threshold2 = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Threshold1)] = Threshold1;
|
||||||
|
dict[nameof(Threshold2)] = Threshold2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Threshold1)))
|
||||||
|
Threshold1 = Convert.ToInt32(dict[nameof(Threshold1)]);
|
||||||
|
if (dict.ContainsKey(nameof(Threshold2)))
|
||||||
|
Threshold2 = Convert.ToInt32(dict[nameof(Threshold2)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -52,4 +52,20 @@ public class DetectionPaddingOperation:BaseOperation
|
|||||||
HorizontalPadding = br.ReadInt32();
|
HorizontalPadding = br.ReadInt32();
|
||||||
VerticalPadding = br.ReadInt32();
|
VerticalPadding = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(HorizontalPadding)] = HorizontalPadding;
|
||||||
|
dict[nameof(VerticalPadding)] = VerticalPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(HorizontalPadding)))
|
||||||
|
HorizontalPadding = Convert.ToInt32(dict[nameof(HorizontalPadding)]);
|
||||||
|
if (dict.ContainsKey(nameof(VerticalPadding)))
|
||||||
|
VerticalPadding = Convert.ToInt32(dict[nameof(VerticalPadding)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -47,4 +47,17 @@ public class GaussianBlurOperation : BaseOperation
|
|||||||
KernelSize.Script = br.ReadString();
|
KernelSize.Script = br.ReadString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(KernelSize)] = KernelSize.Script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(KernelSize)))
|
||||||
|
KernelSize.Script = dict[nameof(KernelSize)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,29 @@ public class PorabollisticHoughOperation:BaseOperation
|
|||||||
MinLineLength = br.ReadInt32();
|
MinLineLength = br.ReadInt32();
|
||||||
MaxLineGap = br.ReadInt32();
|
MaxLineGap = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(DistanceResolution)] = DistanceResolution;
|
||||||
|
dict[nameof(AngleResolution)] = AngleResolution;
|
||||||
|
dict[nameof(Threshold)] = Threshold;
|
||||||
|
dict[nameof(MinLineLength)] = MinLineLength;
|
||||||
|
dict[nameof(MaxLineGap)] = MaxLineGap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(DistanceResolution)))
|
||||||
|
DistanceResolution = Convert.ToDouble(dict[nameof(DistanceResolution)]);
|
||||||
|
if (dict.ContainsKey(nameof(AngleResolution)))
|
||||||
|
AngleResolution = Convert.ToDouble(dict[nameof(AngleResolution)]);
|
||||||
|
if (dict.ContainsKey(nameof(Threshold)))
|
||||||
|
Threshold = Convert.ToInt32(dict[nameof(Threshold)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinLineLength)))
|
||||||
|
MinLineLength = Convert.ToInt32(dict[nameof(MinLineLength)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxLineGap)))
|
||||||
|
MaxLineGap = Convert.ToInt32(dict[nameof(MaxLineGap)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -44,4 +44,20 @@ public class ThresholdMinMaxOperation : BaseOperation
|
|||||||
ThresholdMin.Script = br.ReadString();
|
ThresholdMin.Script = br.ReadString();
|
||||||
ThresholdMax.Script = br.ReadString();
|
ThresholdMax.Script = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ThresholdMin)] = ThresholdMin.Script;
|
||||||
|
dict[nameof(ThresholdMax)] = ThresholdMax.Script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ThresholdMin)))
|
||||||
|
ThresholdMin.Script = dict[nameof(ThresholdMin)].ToString();
|
||||||
|
if (dict.ContainsKey(nameof(ThresholdMax)))
|
||||||
|
ThresholdMax.Script = dict[nameof(ThresholdMax)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,4 +41,17 @@ public class ThresholdOperation:BaseOperation
|
|||||||
ThresholdMin.Script = br.ReadString();
|
ThresholdMin.Script = br.ReadString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ThresholdMin)] = ThresholdMin.Script;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ThresholdMin)))
|
||||||
|
ThresholdMin.Script = dict[nameof(ThresholdMin)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -132,6 +132,23 @@ public class ColorProfileOperation:BaseOperation
|
|||||||
_initialized = false;
|
_initialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ColorProfilePath)] = ColorProfilePath.Path;
|
||||||
|
dict[nameof(Category)] = Category.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ColorProfilePath)))
|
||||||
|
ColorProfilePath.Path = EnsureRelativePath(dict[nameof(ColorProfilePath)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(Category)))
|
||||||
|
Category.Value = dict[nameof(Category)].ToString();
|
||||||
|
_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
string EnsureRelativePath(string path)
|
string EnsureRelativePath(string path)
|
||||||
{
|
{
|
||||||
if (path.StartsWith(".."))
|
if (path.StartsWith(".."))
|
||||||
|
|||||||
@@ -94,4 +94,35 @@ public class GaborFilterOperation : BaseOperation
|
|||||||
ThetaToAngle = br.ReadDouble();
|
ThetaToAngle = br.ReadDouble();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(KSize)] = KSize;
|
||||||
|
dict[nameof(Sigma)] = Sigma;
|
||||||
|
dict[nameof(NumFilters)] = NumFilters;
|
||||||
|
dict[nameof(Lambda)] = Lambda;
|
||||||
|
dict[nameof(Gamma)] = Gamma;
|
||||||
|
dict[nameof(ThetaFromAngle)] = ThetaFromAngle;
|
||||||
|
dict[nameof(ThetaToAngle)] = ThetaToAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(KSize)))
|
||||||
|
KSize = Convert.ToInt32(dict[nameof(KSize)]);
|
||||||
|
if (dict.ContainsKey(nameof(Sigma)))
|
||||||
|
Sigma = Convert.ToDouble(dict[nameof(Sigma)]);
|
||||||
|
if (dict.ContainsKey(nameof(NumFilters)))
|
||||||
|
NumFilters = Convert.ToInt32(dict[nameof(NumFilters)]);
|
||||||
|
if (dict.ContainsKey(nameof(Lambda)))
|
||||||
|
Lambda = Convert.ToDouble(dict[nameof(Lambda)]);
|
||||||
|
if (dict.ContainsKey(nameof(Gamma)))
|
||||||
|
Gamma = Convert.ToDouble(dict[nameof(Gamma)]);
|
||||||
|
if (dict.ContainsKey(nameof(ThetaFromAngle)))
|
||||||
|
ThetaFromAngle = Convert.ToDouble(dict[nameof(ThetaFromAngle)]);
|
||||||
|
if (dict.ContainsKey(nameof(ThetaToAngle)))
|
||||||
|
ThetaToAngle = Convert.ToDouble(dict[nameof(ThetaToAngle)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -57,4 +57,27 @@ public class LUTFilterOperation : BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
LutData.Deserialize(br);
|
LutData.Deserialize(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
// Save LutData as serialized representation
|
||||||
|
using var ms = new MemoryStream();
|
||||||
|
using var bw = new BinaryWriter(ms);
|
||||||
|
LutData.Serialize(bw);
|
||||||
|
dict[nameof(LutData)] = Convert.ToBase64String(ms.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(LutData)))
|
||||||
|
{
|
||||||
|
var base64Data = dict[nameof(LutData)].ToString();
|
||||||
|
var bytes = Convert.FromBase64String(base64Data);
|
||||||
|
using var ms = new MemoryStream(bytes);
|
||||||
|
using var br = new BinaryReader(ms);
|
||||||
|
LutData.Deserialize(br);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -38,4 +38,17 @@ public class NoiseFilterOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
NoiseSize = br.ReadInt32();
|
NoiseSize = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(NoiseSize)] = NoiseSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(NoiseSize)))
|
||||||
|
NoiseSize = Convert.ToInt32(dict[nameof(NoiseSize)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -51,5 +51,33 @@ public class RangeFilterOperation: BaseOperation
|
|||||||
C3Max = br.ReadInt32();
|
C3Max = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(C1Min)] = C1Min;
|
||||||
|
dict[nameof(C1Max)] = C1Max;
|
||||||
|
dict[nameof(C2Min)] = C2Min;
|
||||||
|
dict[nameof(C2Max)] = C2Max;
|
||||||
|
dict[nameof(C3Min)] = C3Min;
|
||||||
|
dict[nameof(C3Max)] = C3Max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(C1Min)))
|
||||||
|
C1Min = Convert.ToInt32(dict[nameof(C1Min)]);
|
||||||
|
if (dict.ContainsKey(nameof(C1Max)))
|
||||||
|
C1Max = Convert.ToInt32(dict[nameof(C1Max)]);
|
||||||
|
if (dict.ContainsKey(nameof(C2Min)))
|
||||||
|
C2Min = Convert.ToInt32(dict[nameof(C2Min)]);
|
||||||
|
if (dict.ContainsKey(nameof(C2Max)))
|
||||||
|
C2Max = Convert.ToInt32(dict[nameof(C2Max)]);
|
||||||
|
if (dict.ContainsKey(nameof(C3Min)))
|
||||||
|
C3Min = Convert.ToInt32(dict[nameof(C3Min)]);
|
||||||
|
if (dict.ContainsKey(nameof(C3Max)))
|
||||||
|
C3Max = Convert.ToInt32(dict[nameof(C3Max)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,4 +68,22 @@ public class GetImageOperation:BaseOperation,IHaveImage
|
|||||||
Id = new Guid(br.ReadString());
|
Id = new Guid(br.ReadString());
|
||||||
Label = br.ReadString();
|
Label = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the operation to the dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dict"></param>
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Load the operation from the dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dict"></param>
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -35,4 +35,17 @@ public class GetChannelOperation: BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
Channel = br.ReadInt32();
|
Channel = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Channel)] = Channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Channel)))
|
||||||
|
Channel = Convert.ToInt32(dict[nameof(Channel)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,4 +41,20 @@ public class ImageSizeProportionOperation:BaseOperation
|
|||||||
Width = br.ReadDouble();
|
Width = br.ReadDouble();
|
||||||
Height = br.ReadDouble();
|
Height = br.ReadDouble();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Width)] = Width;
|
||||||
|
dict[nameof(Height)] = Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Width)))
|
||||||
|
Width = Convert.ToDouble(dict[nameof(Width)]);
|
||||||
|
if (dict.ContainsKey(nameof(Height)))
|
||||||
|
Height = Convert.ToDouble(dict[nameof(Height)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -39,4 +39,17 @@ public class ImageFromMemoryOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
SlotName = br.ReadString();
|
SlotName = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(SlotName)] = SlotName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(SlotName)))
|
||||||
|
SlotName = dict[nameof(SlotName)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,4 +33,17 @@ public class ImageToMemoryOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
SlotName = br.ReadString();
|
SlotName = br.ReadString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(SlotName)] = SlotName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(SlotName)))
|
||||||
|
SlotName = dict[nameof(SlotName)].ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -40,4 +40,17 @@ public class ClosingOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
Closing = br.ReadInt32();
|
Closing = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Closing)] = Closing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Closing)))
|
||||||
|
Closing = Convert.ToInt32(dict[nameof(Closing)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -80,4 +80,43 @@ public class CutOperation:BaseOperation
|
|||||||
|
|
||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ReferencePoint)] = ReferencePoint.ToString();
|
||||||
|
|
||||||
|
// Save SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_SizeX"] = SearchArea.Size.X;
|
||||||
|
dict["SearchArea_SizeY"] = SearchArea.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ReferencePoint)))
|
||||||
|
ReferencePoint = new Guid(dict[nameof(ReferencePoint)].ToString());
|
||||||
|
|
||||||
|
// Load SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable") || dict.ContainsKey("SearchArea_IsGood") || dict.ContainsKey("SearchArea_LocationX"))
|
||||||
|
{
|
||||||
|
SearchArea = new RectangleElement();
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_SizeX") && dict.ContainsKey("SearchArea_SizeY"))
|
||||||
|
SearchArea.Size = new Vector2(Convert.ToSingle(dict["SearchArea_SizeX"]), Convert.ToSingle(dict["SearchArea_SizeY"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -41,4 +41,17 @@ public class DilationOperation:BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
Dilation = br.ReadInt32();
|
Dilation = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Dilation)] = Dilation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Dilation)))
|
||||||
|
Dilation = Convert.ToInt32(dict[nameof(Dilation)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -40,4 +40,17 @@ public class HatsOperation : BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
HatSize = br.ReadInt32();
|
HatSize = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(HatSize)] = HatSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(HatSize)))
|
||||||
|
HatSize = Convert.ToInt32(dict[nameof(HatSize)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,4 +40,17 @@ public class OpeningOperation : BaseOperation
|
|||||||
base.Load(br);
|
base.Load(br);
|
||||||
Opening = br.ReadInt32();
|
Opening = br.ReadInt32();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Opening)] = Opening;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Opening)))
|
||||||
|
Opening = Convert.ToInt32(dict[nameof(Opening)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,4 +45,14 @@ public class SubtractOperation : BaseOperation
|
|||||||
{
|
{
|
||||||
base.Load(br);
|
base.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,14 @@ public class TakeBiggestOperation : BaseOperation
|
|||||||
{
|
{
|
||||||
base.Load(br);
|
base.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,4 +48,14 @@ public class UnionOperation : BaseOperation
|
|||||||
{
|
{
|
||||||
base.Load(br);
|
base.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,4 +155,54 @@ public class FindBlobOperation : BaseOperation, IHaveOrigin, IHaveSearchArea
|
|||||||
|
|
||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(MinArea)] = MinArea;
|
||||||
|
dict[nameof(MaxArea)] = MaxArea;
|
||||||
|
dict[nameof(MinRadius)] = MinRadius;
|
||||||
|
dict[nameof(MaxRadius)] = MaxRadius;
|
||||||
|
dict[nameof(BadIfFound)] = BadIfFound;
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
|
||||||
|
// Save SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_SizeX"] = SearchArea.Size.X;
|
||||||
|
dict["SearchArea_SizeY"] = SearchArea.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(MinArea)))
|
||||||
|
MinArea = Convert.ToInt32(dict[nameof(MinArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxArea)))
|
||||||
|
MaxArea = Convert.ToInt32(dict[nameof(MaxArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinRadius)))
|
||||||
|
MinRadius = Convert.ToInt32(dict[nameof(MinRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxRadius)))
|
||||||
|
MaxRadius = Convert.ToInt32(dict[nameof(MaxRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(BadIfFound)))
|
||||||
|
BadIfFound = Convert.ToBoolean(dict[nameof(BadIfFound)]);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
|
||||||
|
// Load SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_SizeX") && dict.ContainsKey("SearchArea_SizeY"))
|
||||||
|
SearchArea.Size = new Vector2(Convert.ToSingle(dict["SearchArea_SizeX"]), Convert.ToSingle(dict["SearchArea_SizeY"]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -230,4 +230,78 @@ public class FindManyBlobsExtOperation : BaseOperation
|
|||||||
ReferenceId = new Guid(br.ReadString());
|
ReferenceId = new Guid(br.ReadString());
|
||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(MinArea)] = MinArea;
|
||||||
|
dict[nameof(MaxArea)] = MaxArea;
|
||||||
|
dict[nameof(MinRadius)] = MinRadius;
|
||||||
|
dict[nameof(MaxRadius)] = MaxRadius;
|
||||||
|
dict[nameof(CheckInternalRadius)] = CheckInternalRadius;
|
||||||
|
dict[nameof(MinInternalRadius)] = MinInternalRadius;
|
||||||
|
dict[nameof(MaxInternalRadius)] = MaxInternalRadius;
|
||||||
|
dict[nameof(CheckWidth)] = CheckWidth;
|
||||||
|
dict[nameof(MinWidth)] = MinWidth;
|
||||||
|
dict[nameof(MaxWidth)] = MaxWidth;
|
||||||
|
dict[nameof(CheckHeight)] = CheckHeight;
|
||||||
|
dict[nameof(MinHeight)] = MinHeight;
|
||||||
|
dict[nameof(MaxHeight)] = MaxHeight;
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
|
||||||
|
// Save SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_SizeX"] = SearchArea.Size.X;
|
||||||
|
dict["SearchArea_SizeY"] = SearchArea.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(MinArea)))
|
||||||
|
MinArea = Convert.ToInt32(dict[nameof(MinArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxArea)))
|
||||||
|
MaxArea = Convert.ToInt32(dict[nameof(MaxArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinRadius)))
|
||||||
|
MinRadius = Convert.ToInt32(dict[nameof(MinRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxRadius)))
|
||||||
|
MaxRadius = Convert.ToInt32(dict[nameof(MaxRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(CheckInternalRadius)))
|
||||||
|
CheckInternalRadius = Convert.ToBoolean(dict[nameof(CheckInternalRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinInternalRadius)))
|
||||||
|
MinInternalRadius = Convert.ToInt32(dict[nameof(MinInternalRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxInternalRadius)))
|
||||||
|
MaxInternalRadius = Convert.ToInt32(dict[nameof(MaxInternalRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(CheckWidth)))
|
||||||
|
CheckWidth = Convert.ToBoolean(dict[nameof(CheckWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinWidth)))
|
||||||
|
MinWidth = Convert.ToInt32(dict[nameof(MinWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxWidth)))
|
||||||
|
MaxWidth = Convert.ToInt32(dict[nameof(MaxWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(CheckHeight)))
|
||||||
|
CheckHeight = Convert.ToBoolean(dict[nameof(CheckHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinHeight)))
|
||||||
|
MinHeight = Convert.ToInt32(dict[nameof(MinHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxHeight)))
|
||||||
|
MaxHeight = Convert.ToInt32(dict[nameof(MaxHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
|
||||||
|
// Load SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_SizeX") && dict.ContainsKey("SearchArea_SizeY"))
|
||||||
|
SearchArea.Size = new Vector2(Convert.ToSingle(dict["SearchArea_SizeX"]), Convert.ToSingle(dict["SearchArea_SizeY"]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,4 +172,51 @@ public class FindManyBlobsOperation:BaseOperation
|
|||||||
|
|
||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(MinArea)] = MinArea;
|
||||||
|
dict[nameof(MaxArea)] = MaxArea;
|
||||||
|
dict[nameof(MinRadius)] = MinRadius;
|
||||||
|
dict[nameof(MaxRadius)] = MaxRadius;
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
|
||||||
|
// Save SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_SizeX"] = SearchArea.Size.X;
|
||||||
|
dict["SearchArea_SizeY"] = SearchArea.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(MinArea)))
|
||||||
|
MinArea = Convert.ToInt32(dict[nameof(MinArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxArea)))
|
||||||
|
MaxArea = Convert.ToInt32(dict[nameof(MaxArea)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinRadius)))
|
||||||
|
MinRadius = Convert.ToInt32(dict[nameof(MinRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxRadius)))
|
||||||
|
MaxRadius = Convert.ToInt32(dict[nameof(MaxRadius)]);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
|
||||||
|
// Load SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_SizeX") && dict.ContainsKey("SearchArea_SizeY"))
|
||||||
|
SearchArea.Size = new Vector2(Convert.ToSingle(dict["SearchArea_SizeX"]), Convert.ToSingle(dict["SearchArea_SizeY"]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -109,4 +109,54 @@ public class FindRectangleOperation:BaseOperation
|
|||||||
BadIfFound = br.ReadBoolean();
|
BadIfFound = br.ReadBoolean();
|
||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(MinWidth)] = MinWidth;
|
||||||
|
dict[nameof(MaxWidth)] = MaxWidth;
|
||||||
|
dict[nameof(MinHeight)] = MinHeight;
|
||||||
|
dict[nameof(MaxHeight)] = MaxHeight;
|
||||||
|
dict[nameof(BadIfFound)] = BadIfFound;
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
|
||||||
|
// Save SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_SizeX"] = SearchArea.Size.X;
|
||||||
|
dict["SearchArea_SizeY"] = SearchArea.Size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(MinWidth)))
|
||||||
|
MinWidth = Convert.ToInt32(dict[nameof(MinWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxWidth)))
|
||||||
|
MaxWidth = Convert.ToInt32(dict[nameof(MaxWidth)]);
|
||||||
|
if (dict.ContainsKey(nameof(MinHeight)))
|
||||||
|
MinHeight = Convert.ToInt32(dict[nameof(MinHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(MaxHeight)))
|
||||||
|
MaxHeight = Convert.ToInt32(dict[nameof(MaxHeight)]);
|
||||||
|
if (dict.ContainsKey(nameof(BadIfFound)))
|
||||||
|
BadIfFound = Convert.ToBoolean(dict[nameof(BadIfFound)]);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
|
||||||
|
// Load SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_SizeX") && dict.ContainsKey("SearchArea_SizeY"))
|
||||||
|
SearchArea.Size = new Vector2(Convert.ToSingle(dict["SearchArea_SizeX"]), Convert.ToSingle(dict["SearchArea_SizeY"]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -103,4 +103,23 @@ public class TwoBlobsAlign:BaseOperation,IHaveOrigin
|
|||||||
DesiredAngle = br.ReadInt32();
|
DesiredAngle = br.ReadInt32();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(Reference1)] = Reference1.ToString();
|
||||||
|
dict[nameof(Reference2)] = Reference2.ToString();
|
||||||
|
dict[nameof(DesiredAngle)] = DesiredAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(Reference1)))
|
||||||
|
Reference1 = new Guid(dict[nameof(Reference1)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(Reference2)))
|
||||||
|
Reference2 = new Guid(dict[nameof(Reference2)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(DesiredAngle)))
|
||||||
|
DesiredAngle = Convert.ToInt32(dict[nameof(DesiredAngle)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -100,5 +100,21 @@ public class EdgeIntersectionOperation : BaseOperation, IHaveOrigin
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ReferenceEdge1)] = ReferenceEdge1.ToString();
|
||||||
|
dict[nameof(ReferenceEdge2)] = ReferenceEdge2.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceEdge1)))
|
||||||
|
ReferenceEdge1 = new Guid(dict[nameof(ReferenceEdge1)].ToString());
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceEdge2)))
|
||||||
|
ReferenceEdge2 = new Guid(dict[nameof(ReferenceEdge2)].ToString());
|
||||||
|
}
|
||||||
|
|
||||||
public OriginElement Origin { get; set; } = OriginElement.Default;
|
public OriginElement Origin { get; set; } = OriginElement.Default;
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,48 @@ public class FindEdgeOperation : BaseOperation, IHaveEdge, IHaveOrigin
|
|||||||
SearchArea.Load(br);
|
SearchArea.Load(br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Save(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Save(dict);
|
||||||
|
dict[nameof(ReferenceId)] = ReferenceId.ToString();
|
||||||
|
|
||||||
|
// Save RotatedRectangleElement SearchArea properties manually
|
||||||
|
dict["SearchArea_Editable"] = SearchArea.Editable;
|
||||||
|
dict["SearchArea_IsGood"] = SearchArea.IsGood;
|
||||||
|
dict["SearchArea_LocationX"] = SearchArea.Location.X;
|
||||||
|
dict["SearchArea_LocationY"] = SearchArea.Location.Y;
|
||||||
|
dict["SearchArea_OffsetX"] = SearchArea.Offset.X;
|
||||||
|
dict["SearchArea_OffsetY"] = SearchArea.Offset.Y;
|
||||||
|
dict["SearchArea_HeightX"] = SearchArea.Height.X;
|
||||||
|
dict["SearchArea_HeightY"] = SearchArea.Height.Y;
|
||||||
|
dict["SearchArea_HalfWidth"] = SearchArea.HalfWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Load(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
base.Load(dict);
|
||||||
|
if (dict.ContainsKey(nameof(ReferenceId)))
|
||||||
|
ReferenceId = new Guid(dict[nameof(ReferenceId)].ToString());
|
||||||
|
|
||||||
|
// Load RotatedRectangleElement SearchArea properties manually
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable") || dict.ContainsKey("SearchArea_LocationX") || dict.ContainsKey("SearchArea_HeightX"))
|
||||||
|
{
|
||||||
|
SearchArea = new RotatedRectangleElement();
|
||||||
|
if (dict.ContainsKey("SearchArea_Editable"))
|
||||||
|
SearchArea.Editable = Convert.ToBoolean(dict["SearchArea_Editable"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_IsGood"))
|
||||||
|
SearchArea.IsGood = Convert.ToBoolean(dict["SearchArea_IsGood"]);
|
||||||
|
if (dict.ContainsKey("SearchArea_LocationX") && dict.ContainsKey("SearchArea_LocationY"))
|
||||||
|
SearchArea.Location = new Vector2(Convert.ToSingle(dict["SearchArea_LocationX"]), Convert.ToSingle(dict["SearchArea_LocationY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_OffsetX") && dict.ContainsKey("SearchArea_OffsetY"))
|
||||||
|
SearchArea.Offset = new Vector2(Convert.ToSingle(dict["SearchArea_OffsetX"]), Convert.ToSingle(dict["SearchArea_OffsetY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_HeightX") && dict.ContainsKey("SearchArea_HeightY"))
|
||||||
|
SearchArea.Height = new Vector2(Convert.ToSingle(dict["SearchArea_HeightX"]), Convert.ToSingle(dict["SearchArea_HeightY"]));
|
||||||
|
if (dict.ContainsKey("SearchArea_HalfWidth"))
|
||||||
|
SearchArea.HalfWidth = Convert.ToSingle(dict["SearchArea_HalfWidth"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public RotatedRectangleElement SearchArea { get; set; }
|
public RotatedRectangleElement SearchArea { get; set; }
|
||||||
public OriginElement Origin { get; set; }
|
public OriginElement Origin { get; set; }
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.ComponentModel.Design.Serialization;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Reflection.PortableExecutable;
|
using System.Reflection.PortableExecutable;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||||
using Size = OpenCvSharp.Size;
|
using Size = OpenCvSharp.Size;
|
||||||
@@ -32,6 +33,11 @@ public class WorkflowList
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
foreach (BaseOperation operation in Operations)
|
foreach (BaseOperation operation in Operations)
|
||||||
{
|
{
|
||||||
|
if (!operation.Enabled)
|
||||||
|
{
|
||||||
|
operation.Result=true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Context.CancellationToken.IsCancellationRequested) break;
|
if (Context.CancellationToken.IsCancellationRequested) break;
|
||||||
operation.Interpret(Context);
|
operation.Interpret(Context);
|
||||||
}
|
}
|
||||||
@@ -62,6 +68,7 @@ public class WorkflowList
|
|||||||
UpdateConfigVariables();
|
UpdateConfigVariables();
|
||||||
foreach (BaseOperation operation in Operations)
|
foreach (BaseOperation operation in Operations)
|
||||||
{
|
{
|
||||||
|
if(!operation.Enabled)continue;
|
||||||
if(operation == op) break;
|
if(operation == op) break;
|
||||||
operation.Interpret(Context);
|
operation.Interpret(Context);
|
||||||
}
|
}
|
||||||
@@ -275,7 +282,194 @@ public class WorkflowList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, object> ConvertJsonElementsToNatives(Dictionary<string, object> dict)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
foreach (var kvp in dict)
|
||||||
|
{
|
||||||
|
result[kvp.Key] = ConvertJsonElementToNative(kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object ConvertJsonElementToNative(object value)
|
||||||
|
{
|
||||||
|
if (value is JsonElement element)
|
||||||
|
{
|
||||||
|
return element.ValueKind switch
|
||||||
|
{
|
||||||
|
JsonValueKind.String => element.GetString(),
|
||||||
|
JsonValueKind.Number => element.TryGetInt32(out var intVal) ? intVal :
|
||||||
|
element.TryGetDouble(out var doubleVal) ? doubleVal :
|
||||||
|
element.GetDecimal(),
|
||||||
|
JsonValueKind.True => true,
|
||||||
|
JsonValueKind.False => false,
|
||||||
|
JsonValueKind.Null => null,
|
||||||
|
JsonValueKind.Object => ConvertJsonElementsToNatives(
|
||||||
|
JsonSerializer.Deserialize<Dictionary<string, object>>(element.GetRawText())),
|
||||||
|
JsonValueKind.Array => element.EnumerateArray()
|
||||||
|
.Select(x => ConvertJsonElementToNative(x)).ToArray(),
|
||||||
|
_ => value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (value is Dictionary<string, object> nestedDict)
|
||||||
|
{
|
||||||
|
return ConvertJsonElementsToNatives(nestedDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveJSON(string fileName)
|
||||||
|
{
|
||||||
|
var workflowData = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// Save Configuration
|
||||||
|
var configData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["RuntimeCameraType"] = Configuration.RuntimeCameraType.ToString(),
|
||||||
|
["DevelopmentCameraType"] = Configuration.DevelopmentCameraType.ToString(),
|
||||||
|
["Outputs"] = Configuration.Outputs.ToString(),
|
||||||
|
["EmulationPath"] = Configuration.EmulationPath,
|
||||||
|
["PythonPath"] = Configuration.PythonPath,
|
||||||
|
["ResultPin"] = Configuration.ResultPin,
|
||||||
|
["SerialPort"] = Configuration.SerialPort,
|
||||||
|
["Delay"] = Configuration.Delay
|
||||||
|
};
|
||||||
|
workflowData["Configuration"] = configData;
|
||||||
|
|
||||||
|
// Save Operations
|
||||||
|
var operationsData = new List<Dictionary<string, object>>();
|
||||||
|
foreach (BaseOperation operation in Operations)
|
||||||
|
{
|
||||||
|
var operationData = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["Type"] = operation.GetType().AssemblyQualifiedName
|
||||||
|
};
|
||||||
|
|
||||||
|
var operationDict = new Dictionary<string, object>();
|
||||||
|
operation.Save(operationDict);
|
||||||
|
operationData["Data"] = operationDict;
|
||||||
|
|
||||||
|
operationsData.Add(operationData);
|
||||||
|
}
|
||||||
|
workflowData["Operations"] = operationsData;
|
||||||
|
|
||||||
|
// Save Recipe Image
|
||||||
|
if (RecipeImage == null)
|
||||||
|
{
|
||||||
|
var mat = new Mat(128, 128, MatType.CV_8UC3, Scalar.Gray);
|
||||||
|
var image = mat.ToBytes();
|
||||||
|
workflowData["RecipeImage"] = Convert.ToBase64String(image);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var mat = RecipeImage;
|
||||||
|
var image = mat.Resize(new Size(128, 128)).ToBytes();
|
||||||
|
workflowData["RecipeImage"] = Convert.ToBase64String(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
string jsonString = JsonSerializer.Serialize(workflowData, options);
|
||||||
|
File.WriteAllText(fileName, jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadJSON(string fileName, OperationDiscoveryService operationDiscoveryService)
|
||||||
|
{
|
||||||
|
string jsonString = File.ReadAllText(fileName);
|
||||||
|
var workflowData = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonString);
|
||||||
|
|
||||||
|
if (workflowData == null) return;
|
||||||
|
|
||||||
|
// Load Configuration
|
||||||
|
if (workflowData.ContainsKey("Configuration"))
|
||||||
|
{
|
||||||
|
var configElement = (JsonElement)workflowData["Configuration"];
|
||||||
|
var configData = JsonSerializer.Deserialize<Dictionary<string, object>>(configElement.GetRawText());
|
||||||
|
|
||||||
|
if (configData != null)
|
||||||
|
{
|
||||||
|
var convertedConfigData = ConvertJsonElementsToNatives(configData);
|
||||||
|
|
||||||
|
if (convertedConfigData.ContainsKey("RuntimeCameraType"))
|
||||||
|
Configuration.RuntimeCameraType = Enum.Parse<ECameraType>(convertedConfigData["RuntimeCameraType"].ToString());
|
||||||
|
if (convertedConfigData.ContainsKey("DevelopmentCameraType"))
|
||||||
|
Configuration.DevelopmentCameraType = Enum.Parse<ECameraType>(convertedConfigData["DevelopmentCameraType"].ToString());
|
||||||
|
if (convertedConfigData.ContainsKey("Outputs"))
|
||||||
|
Configuration.Outputs = Enum.Parse<EOutputs>(convertedConfigData["Outputs"].ToString());
|
||||||
|
if (convertedConfigData.ContainsKey("EmulationPath"))
|
||||||
|
Configuration.EmulationPath = convertedConfigData["EmulationPath"].ToString();
|
||||||
|
if (convertedConfigData.ContainsKey("PythonPath"))
|
||||||
|
Configuration.PythonPath = convertedConfigData["PythonPath"].ToString();
|
||||||
|
if (convertedConfigData.ContainsKey("ResultPin"))
|
||||||
|
Configuration.ResultPin = (int)convertedConfigData["ResultPin"];
|
||||||
|
if (convertedConfigData.ContainsKey("SerialPort"))
|
||||||
|
Configuration.SerialPort = convertedConfigData["SerialPort"].ToString();
|
||||||
|
if (convertedConfigData.ContainsKey("Delay"))
|
||||||
|
Configuration.Delay = (int)convertedConfigData["Delay"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!File.Exists(Configuration.PythonPath))
|
||||||
|
PythonMissing();
|
||||||
|
|
||||||
|
// Load Operations
|
||||||
|
Operations.Clear();
|
||||||
|
if (workflowData.ContainsKey("Operations"))
|
||||||
|
{
|
||||||
|
var operationsElement = (JsonElement)workflowData["Operations"];
|
||||||
|
var operationsArray = JsonSerializer.Deserialize<List<Dictionary<string, object>>>(operationsElement.GetRawText());
|
||||||
|
|
||||||
|
if (operationsArray != null)
|
||||||
|
{
|
||||||
|
foreach (var operationData in operationsArray)
|
||||||
|
{
|
||||||
|
if (operationData.ContainsKey("Type") && operationData.ContainsKey("Data"))
|
||||||
|
{
|
||||||
|
string operationType = operationData["Type"].ToString();
|
||||||
|
var type = Type.GetType(operationType);
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
BaseOperation operation = operationDiscoveryService.CreateInstance(type);
|
||||||
|
|
||||||
|
var dataElement = (JsonElement)operationData["Data"];
|
||||||
|
var operationDict = JsonSerializer.Deserialize<Dictionary<string, object>>(dataElement.GetRawText());
|
||||||
|
|
||||||
|
if (operationDict != null)
|
||||||
|
{
|
||||||
|
var convertedDict = ConvertJsonElementsToNatives(operationDict);
|
||||||
|
operation.Load(convertedDict);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operations.Add(operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load Recipe Image
|
||||||
|
if (workflowData.ContainsKey("RecipeImage"))
|
||||||
|
{
|
||||||
|
string base64Image = workflowData["RecipeImage"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(base64Image))
|
||||||
|
{
|
||||||
|
byte[] imageData = Convert.FromBase64String(base64Image);
|
||||||
|
RecipeImage = Mat.FromImageData(imageData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorkflowList LoadJSONFromFile(string fileName)
|
||||||
|
{
|
||||||
|
var workflowList = new WorkflowList();
|
||||||
|
workflowList.LoadJSON(fileName, new OperationDiscoveryService(workflowList));
|
||||||
|
return workflowList;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -43,7 +43,7 @@ namespace Hawkeye.VisionBuilder.Features.ImageStatistics
|
|||||||
_goodNode.Nodes.Add(new TreeNode(nodeText) { Tag = filePath });
|
_goodNode.Nodes.Add(new TreeNode(nodeText) { Tag = filePath });
|
||||||
_allFiles.Add(filePath);
|
_allFiles.Add(filePath);
|
||||||
UpdateBranchText(_goodNode, "Good");
|
UpdateBranchText(_goodNode, "Good");
|
||||||
treeView.ExpandAll();
|
//treeView.ExpandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddBadImage(string filePath, string[]? defects = null)
|
public void AddBadImage(string filePath, string[]? defects = null)
|
||||||
@@ -66,7 +66,7 @@ namespace Hawkeye.VisionBuilder.Features.ImageStatistics
|
|||||||
_badNode.Nodes.Add(new TreeNode(nodeText) { Tag = filePath });
|
_badNode.Nodes.Add(new TreeNode(nodeText) { Tag = filePath });
|
||||||
_allFiles.Add(filePath);
|
_allFiles.Add(filePath);
|
||||||
UpdateBranchText(_badNode, "Bad");
|
UpdateBranchText(_badNode, "Bad");
|
||||||
treeView.ExpandAll();
|
//treeView.ExpandAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateBranchText(TreeNode node, string name)
|
private void UpdateBranchText(TreeNode node, string name)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
btnTest = new Button();
|
btnTest = new Button();
|
||||||
button1 = new Button();
|
button1 = new Button();
|
||||||
Indicator = new DataGridViewTextBoxColumn();
|
Indicator = new DataGridViewTextBoxColumn();
|
||||||
Link = new DataGridViewTextBoxColumn();
|
Enabled = new DataGridViewCheckBoxColumn();
|
||||||
UserName = new DataGridViewTextBoxColumn();
|
UserName = new DataGridViewTextBoxColumn();
|
||||||
Result = new DataGridViewTextBoxColumn();
|
Result = new DataGridViewTextBoxColumn();
|
||||||
TypeName = new DataGridViewTextBoxColumn();
|
TypeName = new DataGridViewTextBoxColumn();
|
||||||
@@ -50,14 +50,13 @@
|
|||||||
dataGridView1.AllowUserToAddRows = false;
|
dataGridView1.AllowUserToAddRows = false;
|
||||||
dataGridView1.AllowUserToDeleteRows = false;
|
dataGridView1.AllowUserToDeleteRows = false;
|
||||||
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
dataGridView1.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
dataGridView1.Columns.AddRange(new DataGridViewColumn[] { Indicator, Link, UserName, Result, TypeName, Time });
|
dataGridView1.Columns.AddRange(new DataGridViewColumn[] { Indicator, Enabled, UserName, Result, TypeName, Time });
|
||||||
dataGridView1.Dock = DockStyle.Fill;
|
dataGridView1.Dock = DockStyle.Fill;
|
||||||
dataGridView1.Location = new Point(0, 40);
|
dataGridView1.Location = new Point(0, 40);
|
||||||
dataGridView1.MultiSelect = false;
|
dataGridView1.MultiSelect = false;
|
||||||
dataGridView1.Name = "dataGridView1";
|
dataGridView1.Name = "dataGridView1";
|
||||||
dataGridView1.ReadOnly = true;
|
dataGridView1.ReadOnly = true;
|
||||||
dataGridView1.RowHeadersVisible = false;
|
dataGridView1.RowHeadersVisible = false;
|
||||||
dataGridView1.RowTemplate.Height = 25;
|
|
||||||
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||||
dataGridView1.Size = new Size(540, 562);
|
dataGridView1.Size = new Size(540, 562);
|
||||||
dataGridView1.TabIndex = 0;
|
dataGridView1.TabIndex = 0;
|
||||||
@@ -126,12 +125,12 @@
|
|||||||
Indicator.ReadOnly = true;
|
Indicator.ReadOnly = true;
|
||||||
Indicator.Width = 32;
|
Indicator.Width = 32;
|
||||||
//
|
//
|
||||||
// Link
|
// Enabled
|
||||||
//
|
//
|
||||||
Link.HeaderText = "";
|
Enabled.HeaderText = "";
|
||||||
Link.Name = "Link";
|
Enabled.Name = "Enabled";
|
||||||
Link.ReadOnly = true;
|
Enabled.ReadOnly = true;
|
||||||
Link.Width = 32;
|
Enabled.Width = 32;
|
||||||
//
|
//
|
||||||
// UserName
|
// UserName
|
||||||
//
|
//
|
||||||
@@ -182,7 +181,7 @@
|
|||||||
private Label lblProcessingTime;
|
private Label lblProcessingTime;
|
||||||
private Label label1;
|
private Label label1;
|
||||||
private DataGridViewTextBoxColumn Indicator;
|
private DataGridViewTextBoxColumn Indicator;
|
||||||
private DataGridViewTextBoxColumn Link;
|
private DataGridViewCheckBoxColumn Enabled;
|
||||||
private DataGridViewTextBoxColumn UserName;
|
private DataGridViewTextBoxColumn UserName;
|
||||||
private DataGridViewTextBoxColumn Result;
|
private DataGridViewTextBoxColumn Result;
|
||||||
private DataGridViewTextBoxColumn TypeName;
|
private DataGridViewTextBoxColumn TypeName;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
|||||||
|
|
||||||
dataGridView1.DataSource = _bindingSource;
|
dataGridView1.DataSource = _bindingSource;
|
||||||
dataGridView1.Columns[0].DataPropertyName = nameof(BaseOperationDecorator.Result);
|
dataGridView1.Columns[0].DataPropertyName = nameof(BaseOperationDecorator.Result);
|
||||||
|
dataGridView1.Columns[1].DataPropertyName = nameof(BaseOperationDecorator.IsEnabled);
|
||||||
dataGridView1.Columns[2].DataPropertyName = nameof(BaseOperationDecorator.Label);
|
dataGridView1.Columns[2].DataPropertyName = nameof(BaseOperationDecorator.Label);
|
||||||
dataGridView1.Columns[2].ReadOnly = false;
|
dataGridView1.Columns[2].ReadOnly = false;
|
||||||
dataGridView1.ReadOnly = false;
|
dataGridView1.ReadOnly = false;
|
||||||
@@ -88,29 +89,6 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
|||||||
|
|
||||||
private void DataGridView1_Paint(object? sender, PaintEventArgs e)
|
private void DataGridView1_Paint(object? sender, PaintEventArgs e)
|
||||||
{
|
{
|
||||||
var g = e.Graphics;
|
|
||||||
var c1 = dataGridView1.GetCellDisplayRectangle(1, 0, true);
|
|
||||||
var c2 = dataGridView1.GetCellDisplayRectangle(1, 1, true);
|
|
||||||
|
|
||||||
Pen start = new Pen(Color.Blue, 2);
|
|
||||||
|
|
||||||
Pen body = new Pen(Color.Blue, 2);
|
|
||||||
|
|
||||||
Pen end = new Pen(Color.Blue, 2);
|
|
||||||
|
|
||||||
end.EndCap = LineCap.Custom;
|
|
||||||
|
|
||||||
AdjustableArrowCap bigArrow = new AdjustableArrowCap(5, 5);
|
|
||||||
end.CustomEndCap = bigArrow;
|
|
||||||
|
|
||||||
g.DrawLine(start, c1.Right, (c1.Bottom - c1.Top) / 2 + c1.Top, (c1.Right - c1.Left) / 2 + c1.Left,
|
|
||||||
(c1.Bottom - c1.Top) / 2 + c1.Top);
|
|
||||||
|
|
||||||
g.DrawLine(body, (c1.Right - c1.Left) / 2 + c1.Left, (c1.Bottom - c1.Top) / 2 + c1.Top,
|
|
||||||
(c2.Right - c2.Left) / 2 + c1.Left, (c2.Bottom - c2.Top) / 2 + c2.Top);
|
|
||||||
|
|
||||||
g.DrawLine(end, (c2.Right - c2.Left) / 2 + c2.Left, (c2.Bottom - c2.Top) / 2 + c2.Top, c2.Right,
|
|
||||||
(c2.Bottom - c2.Top) / 2 + c2.Top);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -119,15 +97,21 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
|||||||
private void DataGridView1_CellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
|
private void DataGridView1_CellPainting(object? sender, DataGridViewCellPaintingEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (e.ColumnIndex == 0 && e.RowIndex > -1 && e.Value != null)
|
if (e.RowIndex > -1)
|
||||||
|
{
|
||||||
|
var c1 = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
|
||||||
|
var row = dataGridView1.Rows[e.RowIndex];
|
||||||
|
var rowValue = row.DataBoundItem as BaseOperationDecorator;
|
||||||
|
e.PaintBackground(c1, true);
|
||||||
|
if (e.ColumnIndex == 0 && e.Value != null)
|
||||||
{
|
{
|
||||||
var cx = e.CellBounds.Width / 2 + e.CellBounds.Left;
|
var cx = e.CellBounds.Width / 2 + e.CellBounds.Left;
|
||||||
var cy = e.CellBounds.Height / 2 + e.CellBounds.Top;
|
var cy = e.CellBounds.Height / 2 + e.CellBounds.Top;
|
||||||
var radius = e.CellBounds.Height / 2 - 3;
|
var radius = e.CellBounds.Height / 2 - 3;
|
||||||
var c1 = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
|
|
||||||
e.PaintBackground(c1, true);
|
|
||||||
|
|
||||||
if ((bool)e.Value)
|
|
||||||
|
|
||||||
|
if ((bool) e.Value)
|
||||||
{
|
{
|
||||||
e.Graphics.FillEllipse(Brushes.Green, cx - radius, cy - radius, radius * 2, radius * 2);
|
e.Graphics.FillEllipse(Brushes.Green, cx - radius, cy - radius, radius * 2, radius * 2);
|
||||||
}
|
}
|
||||||
@@ -135,9 +119,11 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
|||||||
{
|
{
|
||||||
e.Graphics.FillEllipse(Brushes.Red, cx - radius, cy - radius, radius * 2, radius * 2);
|
e.Graphics.FillEllipse(Brushes.Red, cx - radius, cy - radius, radius * 2, radius * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,64 @@
|
|||||||
<root>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
@@ -60,7 +120,7 @@
|
|||||||
<metadata name="Indicator.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="Indicator.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>True</value>
|
<value>True</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
<metadata name="Link.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="Enabled.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
<value>True</value>
|
<value>True</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
<metadata name="UserName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="UserName.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
|||||||
@@ -182,13 +182,26 @@ namespace Hawkeye.VisionBuilder
|
|||||||
private void loadRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
private void loadRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
OpenFileDialog dialog = new OpenFileDialog();
|
OpenFileDialog dialog = new OpenFileDialog();
|
||||||
dialog.Filter = "*.hrcp|*.hrcp";
|
dialog.Filter = "Suported types|*.jhrcp;*.hrcp";
|
||||||
PrepareDialog(dialog);
|
PrepareDialog(dialog);
|
||||||
|
|
||||||
if (dialog.ShowDialog() == DialogResult.OK)
|
if (dialog.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
var path = dialog.FileName;
|
var path = dialog.FileName;
|
||||||
LoadRecipe(path);
|
var ext = Path.GetExtension(path);
|
||||||
|
if (ext == ".jhrcp")
|
||||||
|
{
|
||||||
|
LoadRecipeJSON(path);
|
||||||
|
}
|
||||||
|
else if (ext == ".hrcp")
|
||||||
|
{
|
||||||
|
LoadRecipeBinary(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MessageBox.Show("Unsupported file format. Please use .hrcp or .jhrcp files.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -210,7 +223,7 @@ namespace Hawkeye.VisionBuilder
|
|||||||
private string _loadedPath = null;
|
private string _loadedPath = null;
|
||||||
|
|
||||||
|
|
||||||
public void LoadRecipe(string path)
|
public void LoadRecipeBinary(string path)
|
||||||
{
|
{
|
||||||
_loadedPath = path;
|
_loadedPath = path;
|
||||||
var bytes = File.ReadAllBytes(path);
|
var bytes = File.ReadAllBytes(path);
|
||||||
@@ -218,13 +231,23 @@ namespace Hawkeye.VisionBuilder
|
|||||||
BinaryReader reader = new BinaryReader(stream);
|
BinaryReader reader = new BinaryReader(stream);
|
||||||
|
|
||||||
|
|
||||||
_workflowList.PythonMissing += _workflowList_PythonMissing;
|
|
||||||
_workflowList.Load(reader, _discoveryService);
|
_workflowList.Load(reader, _discoveryService);
|
||||||
ReinitCameraPanel(_workflowList);
|
ReinitCameraPanel(_workflowList);
|
||||||
OrganizeWindows(null, null);
|
OrganizeWindows(null, null);
|
||||||
_visionStepsPanel.RefreshWorkflow();
|
_visionStepsPanel.RefreshWorkflow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void LoadRecipeJSON(string path)
|
||||||
|
{
|
||||||
|
_loadedPath = path;
|
||||||
|
_workflowList.LoadJSON(path,_discoveryService);
|
||||||
|
ReinitCameraPanel(_workflowList);
|
||||||
|
OrganizeWindows(null, null);
|
||||||
|
_visionStepsPanel.RefreshWorkflow();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private void _workflowList_PythonMissing()
|
private void _workflowList_PythonMissing()
|
||||||
{
|
{
|
||||||
SetupConfig();
|
SetupConfig();
|
||||||
@@ -234,9 +257,23 @@ namespace Hawkeye.VisionBuilder
|
|||||||
private void saveRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
private void saveRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
SaveFileDialog dialog = new SaveFileDialog();
|
SaveFileDialog dialog = new SaveFileDialog();
|
||||||
dialog.Filter = "*.hrcp|*.hrcp";
|
dialog.Filter = "*.jhrcp|*.jhrcp|*.hrcp|*.hrcp";
|
||||||
PrepareDialog(dialog);
|
PrepareDialog(dialog);
|
||||||
if (dialog.ShowDialog() == DialogResult.OK)
|
if (dialog.ShowDialog() == DialogResult.OK)
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(dialog.FileName);
|
||||||
|
if (ext == ".jhrcp")
|
||||||
|
{
|
||||||
|
_workflowList.SaveJSON(dialog.FileName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SaveBinary(dialog.FileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveBinary(string path)
|
||||||
{
|
{
|
||||||
MemoryStream ms = new MemoryStream();
|
MemoryStream ms = new MemoryStream();
|
||||||
BinaryWriter bw = new BinaryWriter(ms);
|
BinaryWriter bw = new BinaryWriter(ms);
|
||||||
@@ -244,8 +281,7 @@ namespace Hawkeye.VisionBuilder
|
|||||||
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage.ImageData;
|
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage.ImageData;
|
||||||
_workflowList.Save(bw);
|
_workflowList.Save(bw);
|
||||||
ms.Close();
|
ms.Close();
|
||||||
File.WriteAllBytes(dialog.FileName, ms.ToArray());
|
File.WriteAllBytes(path, ms.ToArray());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toolBtnConfiguration_Click(object sender, EventArgs e)
|
private void toolBtnConfiguration_Click(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Hawkeye.VisionBuilder.UI.Sources.Emulation;
|
using Hawkeye.VisionBuilder.UI.Sources.Emulation;
|
||||||
|
using Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||||
using Inspectron.Camera.UEye;
|
using Inspectron.Camera.UEye;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Extensions.ChildKernel;
|
using Ninject.Extensions.ChildKernel;
|
||||||
@@ -26,6 +27,7 @@ public static class ModuleExtensions
|
|||||||
kernel.Bind<IImageSource, IVisionBuilderModule>().To<EmulationCameraImageSource>().InSingletonScope();
|
kernel.Bind<IImageSource, IVisionBuilderModule>().To<EmulationCameraImageSource>().InSingletonScope();
|
||||||
break;
|
break;
|
||||||
case CameraSettings.EImageSource.Hawkeye:
|
case CameraSettings.EImageSource.Hawkeye:
|
||||||
|
kernel.Bind<IImageSource, IVisionBuilderModule>().To<HawkeyeCameraImageSource>().InSingletonScope();
|
||||||
break;
|
break;
|
||||||
case CameraSettings.EImageSource.IDS:
|
case CameraSettings.EImageSource.IDS:
|
||||||
kernel.Bind<IImageSource, IVisionBuilderModule>().To<IDSImageSource>().InSingletonScope();
|
kernel.Bind<IImageSource, IVisionBuilderModule>().To<IDSImageSource>().InSingletonScope();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
|
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Emulation\Hawkeye.VisionBuilder.UI.Sources.Emulation.csproj" />
|
||||||
|
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.Hawkeye\Hawkeye.VisionBuilder.UI.Sources.Hawkeye.csproj" />
|
||||||
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.IDS\Hawkeye.VisionBuilder.UI.Sources.IDS.csproj" />
|
<ProjectReference Include="..\Hawkeye.VisionBuilder.UI.Sources.IDS\Hawkeye.VisionBuilder.UI.Sources.IDS.csproj" />
|
||||||
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
<ProjectReference Include="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -75,6 +75,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "B24SiemensEmulator", "Plugi
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "Plugins\TestPlugin\TestPlugin.csproj", "{F7D39916-489A-3583-09A0-175AE82D08B7}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "Plugins\TestPlugin\TestPlugin.csproj", "{F7D39916-489A-3583-09A0-175AE82D08B7}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hawkeye.VisionBuilder.UI.Sources.Hawkeye", "Hawkeye.VisionBuilder.UI.Sources.Hawkeye\Hawkeye.VisionBuilder.UI.Sources.Hawkeye.csproj", "{C55BE2DA-C60B-491C-8668-9517C7F6FF2F}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inspectron.HawkEye", "framework\Inspectron.HawkEye\Inspectron.HawkEye.csproj", "{44D1BA17-FB52-40A2-9D99-E49DA56C10C2}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Starters", "Starters", "{F3414823-B70E-435E-B4EA-80ABF4371449}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -197,12 +203,22 @@ Global
|
|||||||
{F7D39916-489A-3583-09A0-175AE82D08B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{F7D39916-489A-3583-09A0-175AE82D08B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{F7D39916-489A-3583-09A0-175AE82D08B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{F7D39916-489A-3583-09A0-175AE82D08B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{F7D39916-489A-3583-09A0-175AE82D08B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
{F7D39916-489A-3583-09A0-175AE82D08B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{B62D435C-3572-4B5B-A381-992345D343B0} = {F3414823-B70E-435E-B4EA-80ABF4371449}
|
||||||
{D5FD2E9D-DA4F-1343-47E0-FBC473A149BC} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
{D5FD2E9D-DA4F-1343-47E0-FBC473A149BC} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
|
{7697A266-A721-4D74-9C5C-0D4F2F6BBF68} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}
|
||||||
{E286CE4C-B68A-94B6-F477-0DBF42358009} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
{E286CE4C-B68A-94B6-F477-0DBF42358009} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
{E8868ABD-E4D0-1B7E-494E-06FB1F7D1AF5} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
{E8868ABD-E4D0-1B7E-494E-06FB1F7D1AF5} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
{8B7FAFEE-4066-483C-9C9C-10D2D596206D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
{8B7FAFEE-4066-483C-9C9C-10D2D596206D} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
|
||||||
@@ -224,6 +240,8 @@ Global
|
|||||||
{DB20082B-60E8-D623-3F3A-04612A929CD6} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
{DB20082B-60E8-D623-3F3A-04612A929CD6} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||||
{52D6A9AE-D0F1-4C52-B688-E0219169E179} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
{52D6A9AE-D0F1-4C52-B688-E0219169E179} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||||
{F7D39916-489A-3583-09A0-175AE82D08B7} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}
|
{F7D39916-489A-3583-09A0-175AE82D08B7} = {F2406FBB-DFD3-4CBE-9644-A9FFC2FCBB71}
|
||||||
|
{C55BE2DA-C60B-491C-8668-9517C7F6FF2F} = {D05689E3-04C6-4E3B-ACA7-3F4507CED4CC}
|
||||||
|
{44D1BA17-FB52-40A2-9D99-E49DA56C10C2} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677}
|
SolutionGuid = {3CE42AE5-D79F-4E97-A246-AA8FD228B677}
|
||||||
|
|||||||
159
framework/Inspectron.HawkEye/Camera/I2CLinux.cs
Normal file
159
framework/Inspectron.HawkEye/Camera/I2CLinux.cs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Mono.Unix.Native;
|
||||||
|
|
||||||
|
namespace Inspectron.Devices.Raspberry
|
||||||
|
{
|
||||||
|
public unsafe class I2CLinux
|
||||||
|
{
|
||||||
|
string device;
|
||||||
|
int fd = -1;
|
||||||
|
|
||||||
|
public I2CLinux(int index)
|
||||||
|
{
|
||||||
|
device = "/dev/i2c-" + index;
|
||||||
|
Open();
|
||||||
|
//Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Open()
|
||||||
|
{
|
||||||
|
fd = Syscall.open(device, OpenFlags.O_RDWR);
|
||||||
|
if (fd < 0)
|
||||||
|
throw new IOException(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IoCtl(byte devAddr)
|
||||||
|
{
|
||||||
|
int ret = LunixNatives.ioctl(fd, LunixNatives.I2C_SLAVE, devAddr);
|
||||||
|
if (ret < 0)
|
||||||
|
throw new IOException(device + ": ioctl");
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte readBytes(byte devAddr, byte regAddr, byte length, byte[] data, int offset, ushort timeout = 0)
|
||||||
|
{
|
||||||
|
if (length > 127)
|
||||||
|
throw new IOException(device + ": length > 127");
|
||||||
|
|
||||||
|
//Open();
|
||||||
|
|
||||||
|
IoCtl(devAddr);
|
||||||
|
|
||||||
|
//fixed(byte* p = ®Addr)
|
||||||
|
{
|
||||||
|
int ret = (int)Syscall.write(fd, ®Addr, 1);
|
||||||
|
if (ret != 1)
|
||||||
|
throw new IOException(device + ": write");
|
||||||
|
}
|
||||||
|
|
||||||
|
int count;
|
||||||
|
fixed (byte* p = &data[offset])
|
||||||
|
{
|
||||||
|
count = (int)Syscall.read(fd, p, (ulong)length);
|
||||||
|
if (count < 0)
|
||||||
|
throw new IOException(device + ": read");
|
||||||
|
else if (count != length)
|
||||||
|
throw new IOException(device + ": read short: length = " + length + " > " + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Close();
|
||||||
|
|
||||||
|
return (byte)count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte readBytes(byte devAddr, byte regAddr, byte length, byte[] data, ushort timeout = 0)
|
||||||
|
{
|
||||||
|
return readBytes(devAddr, regAddr, length, data, 0, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write multiple bytes to an 8-bit device register.
|
||||||
|
* @param devAddr I2C slave device address
|
||||||
|
* @param regAddr First register address to write to
|
||||||
|
* @param length Number of bytes to write
|
||||||
|
* @param data Buffer to copy new data from
|
||||||
|
* @return Status of operation (true = success)
|
||||||
|
*/
|
||||||
|
public void writeBytes(byte devAddr, byte regAddr, byte length, byte[] data)
|
||||||
|
{
|
||||||
|
if (length > 127)
|
||||||
|
throw new IOException(device + ": length > 127");
|
||||||
|
|
||||||
|
//Open();
|
||||||
|
IoCtl(devAddr);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[128];
|
||||||
|
buffer[0] = regAddr;
|
||||||
|
Array.Copy(data, 0, buffer, 1, length);
|
||||||
|
|
||||||
|
int count;
|
||||||
|
fixed (byte* p = buffer)
|
||||||
|
{
|
||||||
|
count = (int)Syscall.write(fd, p, (ulong)(length + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new IOException(device + ": write = " + count);
|
||||||
|
}
|
||||||
|
else if (count != length + 1)
|
||||||
|
{
|
||||||
|
throw new IOException(device + ": write short = " + count);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Write multiple words to a 16-bit device register.
|
||||||
|
* @param devAddr I2C slave device address
|
||||||
|
* @param regAddr First register address to write to
|
||||||
|
* @param length Number of words to write
|
||||||
|
* @param data Buffer to copy new data from
|
||||||
|
* @return Status of operation (true = success)
|
||||||
|
*/
|
||||||
|
public void writeWords(byte devAddr, byte regAddr, byte length, ushort[] data)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
byte[] buf = new byte[128];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
// Should do potential byteswap and call writeBytes() really, but that
|
||||||
|
// messes with the callers buffer
|
||||||
|
|
||||||
|
if (length > 63)
|
||||||
|
{
|
||||||
|
throw new IOException(device + ": length > 63");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open();
|
||||||
|
IoCtl(devAddr);
|
||||||
|
|
||||||
|
buf[0] = regAddr;
|
||||||
|
for (i = 0; i < (int)length; i++)
|
||||||
|
{
|
||||||
|
buf[i * 2 + 1] = (byte)(data[i] >> 8);
|
||||||
|
buf[i * 2 + 2] = (byte)data[i];
|
||||||
|
}
|
||||||
|
fixed (byte* p = buf)
|
||||||
|
{
|
||||||
|
count = (int)Syscall.write(fd, p, (ulong)(length * 2 + 1));
|
||||||
|
}
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new IOException(device + ": write");
|
||||||
|
}
|
||||||
|
else if (count != length * 2 + 1)
|
||||||
|
{
|
||||||
|
throw new IOException(device + ": write short");
|
||||||
|
}
|
||||||
|
//Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
int ret = Syscall.close(fd);
|
||||||
|
if (ret != 0)
|
||||||
|
throw new IOException(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
112
framework/Inspectron.HawkEye/Camera/InspectronCamera.cs
Normal file
112
framework/Inspectron.HawkEye/Camera/InspectronCamera.cs
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using Inspectron.Devices.Raspberry;
|
||||||
|
using Inspectron.HawkEye.Protocol;
|
||||||
|
using Inspectron.HawkEye.Protocol.Interfaces;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Camera
|
||||||
|
{
|
||||||
|
public class InspectronCamera:ICameraControl,IImageSource,ILightControl
|
||||||
|
{
|
||||||
|
|
||||||
|
[DllImport("libVCLibProxy.so", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
static extern IntPtr init(Int32 captBuf);
|
||||||
|
[DllImport("libVCLibProxy.so", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
static extern int trigger(IntPtr cpt, byte[] addr, int lines, int captBuf,ref int cancelFlag);
|
||||||
|
[DllImport("libVCLibProxy.so", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
static extern void set_parameters(IntPtr cpt, ref ImageSettings imageSettings);
|
||||||
|
|
||||||
|
[DllImport("libVCLibProxy.so", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int test();
|
||||||
|
byte[] _buffer = new byte[2048 * 250 * 4];
|
||||||
|
|
||||||
|
public InspectronCamera()
|
||||||
|
{
|
||||||
|
|
||||||
|
_i2c = new I2CLinux(0);
|
||||||
|
_i2c.Open();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private bool _isRunningContiniuos = false;
|
||||||
|
private IntPtr _cp= IntPtr.Zero;
|
||||||
|
private ImageSettings _imageSettings=new ImageSettings()
|
||||||
|
{
|
||||||
|
CaptureBuffer = 25,
|
||||||
|
Gain = 200,
|
||||||
|
Lines = 1000,
|
||||||
|
Shutter = 200,
|
||||||
|
SensorWidth = 1440
|
||||||
|
};
|
||||||
|
|
||||||
|
private I2CLinux _i2c;
|
||||||
|
|
||||||
|
private int _lastBuffer = -1;
|
||||||
|
public void SetParameters(CameraSettings cameraSettings)
|
||||||
|
|
||||||
|
{
|
||||||
|
_cameraSettings = cameraSettings;
|
||||||
|
ImageSettings imageSettings = cameraSettings.ImageSettings;
|
||||||
|
if (_lastBuffer ==-1)
|
||||||
|
{
|
||||||
|
_lastBuffer = imageSettings.CaptureBuffer;
|
||||||
|
_cp = init(_lastBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_imageSettings = imageSettings;
|
||||||
|
_buffer=new byte[imageSettings.SensorWidth*(imageSettings.Lines)];
|
||||||
|
var cpImageSettings = imageSettings;
|
||||||
|
cpImageSettings.UseExternalTrigger =imageSettings.UseExternalTrigger;
|
||||||
|
|
||||||
|
if(_lastBuffer==imageSettings.CaptureBuffer)
|
||||||
|
set_parameters(_cp, ref cpImageSettings);
|
||||||
|
else
|
||||||
|
Console.WriteLine("Warning! Buffer size changed. Needs restart");
|
||||||
|
_i2c.writeBytes(4, 2, 4, BitConverter.GetBytes(imageSettings.Divider));
|
||||||
|
Thread.Sleep(100);
|
||||||
|
_i2c.writeBytes(4,3,1, new byte[] { (byte)imageSettings.UseExternalTrigger });
|
||||||
|
Thread.Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int _cancelFlag = 0;
|
||||||
|
private CameraSettings _cameraSettings;
|
||||||
|
|
||||||
|
public byte[] GetImage()
|
||||||
|
{
|
||||||
|
_cancelFlag = 0;
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
_i2c.writeBytes(4, 4, 1,new byte[]{ (byte)(_cameraSettings.LaserTrigger?1:0)} );
|
||||||
|
//Thread.Sleep(20);
|
||||||
|
_i2c.writeBytes(4, 5, 4, BitConverter.GetBytes(_cameraSettings.LaserTriggerDelay));
|
||||||
|
//Thread.Sleep(20);
|
||||||
|
|
||||||
|
trigger(_cp, _buffer, _imageSettings.Lines, _imageSettings.CaptureBuffer,ref _cancelFlag);
|
||||||
|
sw.Stop();
|
||||||
|
Console.WriteLine("Trigger time: "+sw.ElapsedMilliseconds);
|
||||||
|
if(_cancelFlag==1)return new byte[0];
|
||||||
|
return _buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelTrigger()
|
||||||
|
{
|
||||||
|
_cancelFlag = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLight(int pwm1, int pwm2)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"setting lights to {pwm1}/{pwm2}");
|
||||||
|
pwm1 = (int)(pwm1 / 100.0 * 255);
|
||||||
|
pwm2 = (int)(pwm2 / 100.0 * 255);
|
||||||
|
_i2c.writeBytes(4, 1, 2, new byte[] { (byte)pwm1,(byte)pwm2 });
|
||||||
|
|
||||||
|
Thread.Sleep(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
framework/Inspectron.HawkEye/Camera/LunixNatives.cs
Normal file
21
framework/Inspectron.HawkEye/Camera/LunixNatives.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Inspectron.Devices.Raspberry
|
||||||
|
{
|
||||||
|
public static class LunixNatives
|
||||||
|
{
|
||||||
|
public const int O_RDWR = 2;
|
||||||
|
|
||||||
|
[DllImport("libc.so.6")]
|
||||||
|
extern public static int open(string file, int mode);
|
||||||
|
|
||||||
|
[DllImport("libc.so.6")]
|
||||||
|
extern public static int close(int fd);
|
||||||
|
|
||||||
|
[DllImport("libc.so.6")]
|
||||||
|
extern public static int ioctl(int fd, int request, byte x);
|
||||||
|
|
||||||
|
public const int I2C_SLAVE = 0x0703;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
104
framework/Inspectron.HawkEye/DefragmentedPacket.cs
Normal file
104
framework/Inspectron.HawkEye/DefragmentedPacket.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public class DefragmentedPacket
|
||||||
|
{
|
||||||
|
private readonly uint _packetSize;
|
||||||
|
private byte[] _receivedParts = null;
|
||||||
|
//ConcurrentDictionary<uint,byte[]> _packetParts = new ConcurrentDictionary<uint, byte[]>();
|
||||||
|
private byte[][] _packetParts;
|
||||||
|
public DefragmentedPacket(uint packetSize)
|
||||||
|
{
|
||||||
|
_packetSize = packetSize;
|
||||||
|
_packetParts=new byte[30000][];
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _uniquePackets=0;
|
||||||
|
public void Defragment(byte[] data)
|
||||||
|
{
|
||||||
|
var packetStart = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
if (BitConverter.ToUInt32(data, i) == 114455)
|
||||||
|
{
|
||||||
|
packetStart = i;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packetStart == -1) return;
|
||||||
|
|
||||||
|
MemoryStream ms = new MemoryStream(data,packetStart, data.Length - packetStart);
|
||||||
|
BinaryReader br = new BinaryReader(ms);
|
||||||
|
br.ReadUInt32();//packetStart
|
||||||
|
var packetType = br.ReadUInt32();
|
||||||
|
var sequenceId = br.ReadUInt32();
|
||||||
|
var packetNumber = br.ReadUInt32();
|
||||||
|
|
||||||
|
var totalPackets = br.ReadUInt32();
|
||||||
|
|
||||||
|
if(_receivedParts==null)_receivedParts=new byte[totalPackets];
|
||||||
|
if (_receivedParts[packetNumber] == 1) return;
|
||||||
|
_receivedParts[packetNumber] = 1;
|
||||||
|
Interlocked.Increment(ref _uniquePackets);
|
||||||
|
var dataLen = br.ReadInt32();
|
||||||
|
|
||||||
|
var dataBytes = br.ReadBytes(dataLen);
|
||||||
|
|
||||||
|
|
||||||
|
_packetParts[packetNumber] = dataBytes;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Reconstruct()
|
||||||
|
{
|
||||||
|
var parts = _receivedParts.Length;
|
||||||
|
byte[] res = new byte[parts*_packetSize];
|
||||||
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
int resSize = 0;
|
||||||
|
for (int i = 0; i < parts; i++)
|
||||||
|
{
|
||||||
|
//if (_packetParts.ContainsKey((uint) i))
|
||||||
|
if (_packetParts[i]!=null)
|
||||||
|
{
|
||||||
|
var packetData = _packetParts[(uint) i];
|
||||||
|
|
||||||
|
Array.Copy(packetData, 0,res, resSize, packetData.Length);
|
||||||
|
resSize += packetData.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resSize += (int)_packetSize-20/*headerSize*/;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
|
||||||
|
Array.Resize(ref res,resSize);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsComplete
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_receivedParts == null) return false;
|
||||||
|
return _uniquePackets == _receivedParts.Length;
|
||||||
|
//return _receivedParts.All(x => x == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
105
framework/Inspectron.HawkEye/FragmentedPacket.cs
Normal file
105
framework/Inspectron.HawkEye/FragmentedPacket.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public class FragmentedPacket
|
||||||
|
{
|
||||||
|
private readonly uint _packetSize;
|
||||||
|
private readonly EPacketType _packetType;
|
||||||
|
private readonly uint _sequnceId;
|
||||||
|
|
||||||
|
public FragmentedPacket(uint packetSize,EPacketType packetType,uint sequnceId)
|
||||||
|
{
|
||||||
|
_packetSize = packetSize;
|
||||||
|
_packetType = packetType;
|
||||||
|
_sequnceId = sequnceId;
|
||||||
|
}
|
||||||
|
public IEnumerable<byte[]> Fragment(byte[] packetData)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint dataPtr = 0;
|
||||||
|
|
||||||
|
int packetNumber = 0;
|
||||||
|
uint headerSize = 20;
|
||||||
|
uint payloadSize = (_packetSize - headerSize);
|
||||||
|
var totalPackets = (uint)Math.Ceiling(((double)packetData.Length / payloadSize));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MemoryStream ms = new MemoryStream();
|
||||||
|
BinaryWriter bw = new BinaryWriter(ms);
|
||||||
|
bw.Write((uint) 114455); //packetStart //4
|
||||||
|
bw.Write((uint) _packetType); //8
|
||||||
|
bw.Write((uint) _sequnceId); //12
|
||||||
|
bw.Write((uint) packetNumber); //16
|
||||||
|
//total packets?
|
||||||
|
|
||||||
|
|
||||||
|
bw.Write(totalPackets); //20
|
||||||
|
|
||||||
|
byte[] data = new byte[payloadSize];
|
||||||
|
uint dataSize = Math.Min((uint)(packetData.Length-dataPtr), payloadSize);
|
||||||
|
Array.Copy(packetData, dataPtr, data, 0, dataSize);
|
||||||
|
dataPtr += dataSize;
|
||||||
|
bw.Write(dataSize);
|
||||||
|
bw.Write(data);
|
||||||
|
|
||||||
|
packetNumber += 1;
|
||||||
|
yield return ms.ToArray();
|
||||||
|
|
||||||
|
} while (packetNumber < totalPackets);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public IEnumerable<byte[]> FragmentTo(byte[] packetData,UDPSocket socket)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uint dataPtr = 0;
|
||||||
|
|
||||||
|
int packetNumber = 0;
|
||||||
|
uint headerSize = 20;
|
||||||
|
uint payloadSize = (_packetSize - headerSize);
|
||||||
|
var totalPackets = (uint)Math.Ceiling(((double)packetData.Length / payloadSize));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MemoryStream ms = new MemoryStream();
|
||||||
|
BinaryWriter bw = new BinaryWriter(ms);
|
||||||
|
bw.Write((uint)114455); //packetStart //4
|
||||||
|
bw.Write((uint)_packetType); //8
|
||||||
|
bw.Write((uint)_sequnceId); //12
|
||||||
|
bw.Write((uint)packetNumber); //16
|
||||||
|
//total packets?
|
||||||
|
|
||||||
|
|
||||||
|
bw.Write(totalPackets); //20
|
||||||
|
|
||||||
|
byte[] data = new byte[payloadSize];
|
||||||
|
uint dataSize = Math.Min((uint)(packetData.Length - dataPtr), payloadSize);
|
||||||
|
Array.Copy(packetData, dataPtr, data, 0, dataSize);
|
||||||
|
dataPtr += dataSize;
|
||||||
|
bw.Write(dataSize);
|
||||||
|
bw.Write(data);
|
||||||
|
|
||||||
|
packetNumber += 1;
|
||||||
|
yield return ms.ToArray();
|
||||||
|
|
||||||
|
} while (packetNumber < totalPackets);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
framework/Inspectron.HawkEye/Inspectron.HawkEye.csproj
Normal file
21
framework/Inspectron.HawkEye/Inspectron.HawkEye.csproj
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
|
<PackageReference Include="NLog" Version="4.7.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
9
framework/Inspectron.HawkEye/Packets/EPacketType.cs
Normal file
9
framework/Inspectron.HawkEye/Packets/EPacketType.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public enum EPacketType
|
||||||
|
{
|
||||||
|
Test,
|
||||||
|
ImageData,
|
||||||
|
ImageRequest
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public class ImageRequestPacket
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
10
framework/Inspectron.HawkEye/Packets/Packet.cs
Normal file
10
framework/Inspectron.HawkEye/Packets/Packet.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public class Packet
|
||||||
|
{
|
||||||
|
public EPacketType PacketType { get; set; }
|
||||||
|
public byte[] Payload { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
framework/Inspectron.HawkEye/Packets/PacketImage.cs
Normal file
12
framework/Inspectron.HawkEye/Packets/PacketImage.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Inspectron.HawkEye
|
||||||
|
{
|
||||||
|
public class PacketImage
|
||||||
|
{
|
||||||
|
public uint TriggerId { get; set; }
|
||||||
|
public uint PacketId { get; set; }
|
||||||
|
public uint TotalPackets { get; set; }
|
||||||
|
|
||||||
|
public uint StartIndex { get; set; }
|
||||||
|
public byte[] Data { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
22
framework/Inspectron.HawkEye/Protocol/CameraSettings.cs
Normal file
22
framework/Inspectron.HawkEye/Protocol/CameraSettings.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public class CameraSettings
|
||||||
|
{
|
||||||
|
public ImageSettings ImageSettings { get; set; }
|
||||||
|
public int LightPwm1 { get; set; }
|
||||||
|
public int LightPwm2 { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int OffsetX { get; set; }
|
||||||
|
public int ImageWidth { get; set; }
|
||||||
|
public int RescaleWidth { get; set; }
|
||||||
|
public int MinorCutoff { get; set; }
|
||||||
|
public bool BayerFilter { get; set; }
|
||||||
|
public bool LaserTrigger { get; set; }
|
||||||
|
public int LaserTriggerDelay { get; set; }
|
||||||
|
public bool FlipLines { get; set; }
|
||||||
|
public bool MirrorX { get; set; }
|
||||||
|
public bool TriggerLights { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol.Discovery
|
||||||
|
{
|
||||||
|
public class CameraInfo
|
||||||
|
{
|
||||||
|
public string Mac { get; set; }
|
||||||
|
public IPAddress Address { get; set; }
|
||||||
|
public IPAddress AdapterAddress { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol.Discovery
|
||||||
|
{
|
||||||
|
public class DiscoveryClient
|
||||||
|
{
|
||||||
|
public ReadOnlyCollection<CameraInfo> Discovered => new ReadOnlyCollection<CameraInfo>(_discovered);
|
||||||
|
private readonly int _port;
|
||||||
|
private readonly List<CameraInfo> _discovered = new List<CameraInfo>();
|
||||||
|
|
||||||
|
private readonly object _discoveryLock = new object();
|
||||||
|
|
||||||
|
public DiscoveryClient(int port)
|
||||||
|
{
|
||||||
|
_port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<CameraInfo> CameraFound = delegate { };
|
||||||
|
|
||||||
|
public void Discover()
|
||||||
|
{
|
||||||
|
var allInterfaces = NetworkInterface
|
||||||
|
.GetAllNetworkInterfaces()
|
||||||
|
.Where(nic => nic.OperationalStatus == OperationalStatus.Up);
|
||||||
|
|
||||||
|
|
||||||
|
Parallel.ForEach(allInterfaces, DiscoverOnInterface);
|
||||||
|
//foreach (NetworkInterface i in allInterfaces)
|
||||||
|
//{
|
||||||
|
// DiscoverOnInterface(i);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DiscoverOnInterface(NetworkInterface iface)
|
||||||
|
{
|
||||||
|
var address = iface.GetIPProperties().UnicastAddresses
|
||||||
|
.First(x => x.Address.AddressFamily == AddressFamily.InterNetwork).Address;
|
||||||
|
UdpClient client;
|
||||||
|
lock (_discoveryLock)
|
||||||
|
{
|
||||||
|
client = new UdpClient(new IPEndPoint(address, 0));
|
||||||
|
var requestData = Encoding.ASCII.GetBytes("discovery");
|
||||||
|
client.Client.ReceiveTimeout = 2000;
|
||||||
|
|
||||||
|
var s = client.Client;
|
||||||
|
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
|
||||||
|
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
|
||||||
|
client.EnableBroadcast = true;
|
||||||
|
client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
|
||||||
|
client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
|
||||||
|
client.Send(requestData, requestData.Length, new IPEndPoint(IPAddress.Broadcast, _port));
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverEp = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
|
||||||
|
byte[] serverResponseData;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
serverResponseData = client.Receive(ref serverEp);
|
||||||
|
var serverResponse = Encoding.ASCII.GetString(serverResponseData);
|
||||||
|
Console.WriteLine("Recived {0} from {1}", serverResponse, serverEp.Address);
|
||||||
|
var found = new CameraInfo {Mac = serverResponse, Address = serverEp.Address,AdapterAddress = address};
|
||||||
|
lock (_discoveryLock)
|
||||||
|
{
|
||||||
|
if (_discovered.Any(x => x.Mac == found.Mac)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraFound(found);
|
||||||
|
_discovered.Add(found);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol.Discovery
|
||||||
|
{
|
||||||
|
public class DiscoveryServer
|
||||||
|
{
|
||||||
|
private UdpClient _server;
|
||||||
|
private byte[] _name;
|
||||||
|
|
||||||
|
public DiscoveryServer(int port,string name=null)
|
||||||
|
{
|
||||||
|
_server = new UdpClient(port);
|
||||||
|
|
||||||
|
if(name==null)
|
||||||
|
{ _name = Encoding.UTF8.GetBytes( NetworkInterface
|
||||||
|
.GetAllNetworkInterfaces()
|
||||||
|
.Where(nic => nic.OperationalStatus == OperationalStatus.Up && nic.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
||||||
|
.Select(nic => nic.GetPhysicalAddress().ToString())
|
||||||
|
.FirstOrDefault());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_name = Encoding.UTF8.GetBytes(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
Thread th = new Thread(DiscoveryLoop);
|
||||||
|
th.Start();
|
||||||
|
}
|
||||||
|
private void DiscoveryLoop()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var clientEp = new IPEndPoint(IPAddress.Any, 0);
|
||||||
|
var clientRequestData = _server.Receive(ref clientEp);
|
||||||
|
var clientRequest = Encoding.ASCII.GetString(clientRequestData);
|
||||||
|
|
||||||
|
Console.WriteLine("Recived {0} from {1}, sending response", clientRequest, clientEp.Address.ToString());
|
||||||
|
_server.Send(_name, _name.Length, clientEp);
|
||||||
|
_server.Send(_name, _name.Length, clientEp);
|
||||||
|
_server.Send(_name, _name.Length, clientEp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
framework/Inspectron.HawkEye/Protocol/ECommand.cs
Normal file
13
framework/Inspectron.HawkEye/Protocol/ECommand.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public enum ECommand
|
||||||
|
{
|
||||||
|
Connect,
|
||||||
|
Trigger, StartContinuous, StopContinuous,
|
||||||
|
Settings,SaveSettings,
|
||||||
|
OK,
|
||||||
|
SaveCalibration,
|
||||||
|
GetCalibration,
|
||||||
|
NotOK
|
||||||
|
}
|
||||||
|
}
|
||||||
9
framework/Inspectron.HawkEye/Protocol/EData.cs
Normal file
9
framework/Inspectron.HawkEye/Protocol/EData.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public enum EData
|
||||||
|
{
|
||||||
|
Image,
|
||||||
|
|
||||||
|
OK
|
||||||
|
}
|
||||||
|
}
|
||||||
114
framework/Inspectron.HawkEye/Protocol/ImageClient.cs
Normal file
114
framework/Inspectron.HawkEye/Protocol/ImageClient.cs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using Inspectron.HawkEye.UDPB;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public class ImageClient : IDisposable
|
||||||
|
{
|
||||||
|
private static readonly object connectionLock = new object();
|
||||||
|
private readonly IPEndPoint _endpoint;
|
||||||
|
private readonly IPAddress _adapter;
|
||||||
|
|
||||||
|
private readonly UDPBSocket _imageSocket = new UDPBSocket();
|
||||||
|
private readonly UDPBSocket _commandSocket = new UDPBSocket();
|
||||||
|
private bool _isConnected = true;
|
||||||
|
|
||||||
|
public ImageClient(IPEndPoint endpoint,IPAddress adapter)
|
||||||
|
{
|
||||||
|
_endpoint = endpoint;
|
||||||
|
_adapter = adapter;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_isConnected = false;
|
||||||
|
_commandSocket.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
lock (connectionLock)
|
||||||
|
{
|
||||||
|
_commandSocket.Connect(_endpoint,_adapter);
|
||||||
|
var port = UDPBSocket.FindFreePort(_adapter);
|
||||||
|
var bytesPort = BitConverter.GetBytes(port);
|
||||||
|
_commandSocket.SendData(new[]
|
||||||
|
{(byte) ECommand.Connect, bytesPort[0], bytesPort[1], bytesPort[2], bytesPort[3]});
|
||||||
|
var settingsData0 = _commandSocket.Receive();
|
||||||
|
var b = new byte[settingsData0.Length - 1];
|
||||||
|
Array.Copy(settingsData0, 1, b, 0, b.Length);
|
||||||
|
var settingsString = Encoding.UTF8.GetString(b);
|
||||||
|
SettingsReceived(JsonConvert.DeserializeObject<CameraSettings>(settingsString));
|
||||||
|
_imageSocket.Listen(_adapter,port);
|
||||||
|
var th = new Thread(ReceiveLoop);
|
||||||
|
th.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<byte[]> ImageReceived = delegate { };
|
||||||
|
public event Action<CameraSettings> SettingsReceived = delegate { };
|
||||||
|
|
||||||
|
|
||||||
|
public void Trigger()
|
||||||
|
{
|
||||||
|
_commandSocket.SendData(new[] {(byte) ECommand.Trigger});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplySettings(CameraSettings cameraSettings)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cameraSettings));
|
||||||
|
var databytes = new byte[1000];
|
||||||
|
databytes[0] = (byte) ECommand.Settings;
|
||||||
|
Array.Copy(data, 0, databytes, 1, data.Length);
|
||||||
|
|
||||||
|
_commandSocket.SendData(databytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartContinuous()
|
||||||
|
{
|
||||||
|
_commandSocket.SendData(new[] {(byte) ECommand.StartContinuous});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopContinuous()
|
||||||
|
{
|
||||||
|
_commandSocket.SendData(new[] {(byte) ECommand.StopContinuous});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettingsOnCamera()
|
||||||
|
{
|
||||||
|
_commandSocket.SendData(new[] {(byte) ECommand.SaveSettings});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void ReceiveLoop()
|
||||||
|
{
|
||||||
|
while (_isConnected)
|
||||||
|
{
|
||||||
|
var data = _imageSocket.Receive();
|
||||||
|
|
||||||
|
Process(data);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process(byte[] data)
|
||||||
|
{
|
||||||
|
switch ((EData) data[0])
|
||||||
|
{
|
||||||
|
case EData.Image:
|
||||||
|
var b = new byte[data.Length - 1];
|
||||||
|
Array.Copy(data, 1, b, 0, b.Length);
|
||||||
|
ImageReceived(data);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
192
framework/Inspectron.HawkEye/Protocol/ImageClientTCP.cs
Normal file
192
framework/Inspectron.HawkEye/Protocol/ImageClientTCP.cs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using Inspectron.HawkEye.UDPB;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public class ImageClientTCP : IDisposable
|
||||||
|
{
|
||||||
|
private static readonly object connectionLock = new object();
|
||||||
|
private readonly IPEndPoint _endpoint;
|
||||||
|
private readonly IPAddress _adapter;
|
||||||
|
|
||||||
|
private TcpListener _imageSocket;
|
||||||
|
private TcpClient _commandSocket;
|
||||||
|
private bool _isConnected = true;
|
||||||
|
|
||||||
|
public ImageClientTCP(IPEndPoint endpoint,IPAddress adapter)
|
||||||
|
{
|
||||||
|
_endpoint = endpoint;
|
||||||
|
_adapter = adapter;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_isConnected = false;
|
||||||
|
_commandSocket.Dispose();
|
||||||
|
}
|
||||||
|
byte[] _commandBuffer = new byte[1500];
|
||||||
|
private TcpClient _lastClient;
|
||||||
|
public bool SupportsCalibration { get; set; }
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
lock (connectionLock)
|
||||||
|
{
|
||||||
|
_commandSocket=new TcpClient(new IPEndPoint(_adapter, 0));
|
||||||
|
Console.WriteLine($"Bind on {_adapter?.ToString()}");
|
||||||
|
|
||||||
|
_commandSocket.Connect(_endpoint);
|
||||||
|
var port = UDPBSocket.FindFreePort(_adapter);
|
||||||
|
Console.WriteLine("Connected");
|
||||||
|
|
||||||
|
_imageSocket = new TcpListener(_adapter, port);
|
||||||
|
_imageSocket.Start();
|
||||||
|
|
||||||
|
Console.WriteLine("TCP started");
|
||||||
|
|
||||||
|
var bytesPort = BitConverter.GetBytes(port);
|
||||||
|
_commandSocket.Client.SendData(new[]
|
||||||
|
{(byte) ECommand.Connect, bytesPort[0], bytesPort[1], bytesPort[2], bytesPort[3]});
|
||||||
|
|
||||||
|
var receivedLen = _commandSocket.Client.Receive(_commandBuffer);
|
||||||
|
Console.WriteLine("received answer length:"+receivedLen);
|
||||||
|
var b = new byte[receivedLen - 1];
|
||||||
|
Array.Copy(_commandBuffer, 1, b, 0, b.Length);
|
||||||
|
var settingsString = Encoding.UTF8.GetString(b);
|
||||||
|
SettingsReceived(JsonConvert.DeserializeObject<CameraSettings>(settingsString));
|
||||||
|
if (SupportsCalibration)
|
||||||
|
{
|
||||||
|
|
||||||
|
_commandSocket.Client.SendData(new[] {(byte) ECommand.GetCalibration});
|
||||||
|
receivedLen = _commandSocket.Client.Receive(_commandBuffer);
|
||||||
|
if (_commandBuffer[0] == (byte) ECommand.OK)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"calibration received {receivedLen} bytes");
|
||||||
|
var c = new byte[receivedLen - 1];
|
||||||
|
Array.Copy(_commandBuffer, 1, c, 0, c.Length);
|
||||||
|
var calibrationString = Encoding.UTF8.GetString(c);
|
||||||
|
CalibrationReceived(JsonConvert.DeserializeObject<List<Dictionary<double,double>>>(calibrationString));
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("no calibration received");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var th = new Thread(ReceiveLoop);
|
||||||
|
th.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<byte[]> ImageReceived = delegate { };
|
||||||
|
public event Action<CameraSettings> SettingsReceived = delegate { };
|
||||||
|
public event Action<List<Dictionary<double,double>>> CalibrationReceived = delegate { };
|
||||||
|
|
||||||
|
|
||||||
|
public void Trigger()
|
||||||
|
{
|
||||||
|
_commandSocket.Client.SendData(new[] {(byte) ECommand.Trigger});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplySettings(CameraSettings cameraSettings)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(cameraSettings));
|
||||||
|
var databytes = new byte[1000];
|
||||||
|
databytes[0] = (byte) ECommand.Settings;
|
||||||
|
Array.Copy(data, 0, databytes, 1, data.Length);
|
||||||
|
|
||||||
|
_commandSocket.Client.SendData(databytes);
|
||||||
|
byte[] ok = new byte[1];
|
||||||
|
_commandSocket.Client.Receive(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartContinuous()
|
||||||
|
{
|
||||||
|
_commandSocket.Client.SendData(new[] {(byte) ECommand.StartContinuous});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopContinuous()
|
||||||
|
{
|
||||||
|
_commandSocket.Client.SendData(new[] {(byte) ECommand.StopContinuous});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveSettingsOnCamera()
|
||||||
|
{
|
||||||
|
_commandSocket.Client.SendData(new[] {(byte) ECommand.SaveSettings});
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] _imageBuffer = new byte[10*1024*1024];
|
||||||
|
private void ReceiveLoop()
|
||||||
|
{
|
||||||
|
while (_isConnected)
|
||||||
|
{
|
||||||
|
_lastClient = _imageSocket.AcceptTcpClient();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int received=0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_lastClient.Client.Receive(_imageBuffer, 0, 1,
|
||||||
|
SocketFlags.None);
|
||||||
|
Process(_imageBuffer);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveCalibration(List<Dictionary<double,double>> calibration)
|
||||||
|
{
|
||||||
|
var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(calibration));
|
||||||
|
var databytes = new byte[1500];
|
||||||
|
databytes[0] = (byte)ECommand.SaveCalibration;
|
||||||
|
Array.Copy(data, 0, databytes, 1, data.Length);
|
||||||
|
|
||||||
|
_commandSocket.Client.SendData(databytes);
|
||||||
|
byte[] ok = new byte[1];
|
||||||
|
_commandSocket.Client.Receive(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Process(byte[] data)
|
||||||
|
{
|
||||||
|
switch ((EData) data[0])
|
||||||
|
{
|
||||||
|
case EData.Image:
|
||||||
|
_lastClient.Client.Receive(_imageBuffer, 1, 4,
|
||||||
|
SocketFlags.None);
|
||||||
|
var imageSize = BitConverter.ToInt32(_imageBuffer,1);
|
||||||
|
var received = 0;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
received += _lastClient.Client.Receive(_imageBuffer, 5+ received, imageSize- received,
|
||||||
|
SocketFlags.None);
|
||||||
|
} while (received < imageSize);
|
||||||
|
|
||||||
|
var b = new byte[imageSize];
|
||||||
|
Array.Copy(data, 5, b, 0, b.Length);
|
||||||
|
ImageReceived(b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
164
framework/Inspectron.HawkEye/Protocol/ImageServer.cs
Normal file
164
framework/Inspectron.HawkEye/Protocol/ImageServer.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Inspectron.HawkEye.Protocol.Interfaces;
|
||||||
|
using Inspectron.HawkEye.UDPB;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public class ImageServer
|
||||||
|
{
|
||||||
|
private readonly IImageSource _imageSource;
|
||||||
|
private readonly ICameraControl _cameraControl;
|
||||||
|
private readonly ILightControl _lightControl;
|
||||||
|
private readonly UDPBSocket _commandSocket = new UDPBSocket();
|
||||||
|
private readonly UDPBSocket _imageSocket = new UDPBSocket();
|
||||||
|
|
||||||
|
private bool _autoTrigger;
|
||||||
|
private bool _applySettings;
|
||||||
|
private CameraSettings _imageSettingsToApply=new CameraSettings(){ImageSettings = new ImageSettings()};
|
||||||
|
|
||||||
|
public ImageServer(IImageSource imageSource, ICameraControl cameraControl, ILightControl lightControl)
|
||||||
|
{
|
||||||
|
_imageSource = imageSource;
|
||||||
|
_cameraControl = cameraControl;
|
||||||
|
_lightControl = lightControl;
|
||||||
|
_imageSocket.LossSimulation = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
_commandSocket.Listen(IPAddress.Any, 27001);
|
||||||
|
var th = new Thread(ReceiveLoop);
|
||||||
|
th.Start();
|
||||||
|
if (File.Exists("settings.json"))
|
||||||
|
{
|
||||||
|
var settings = JsonConvert.DeserializeObject<CameraSettings>(File.ReadAllText("settings.json"));
|
||||||
|
_imageSettingsToApply = settings;
|
||||||
|
ApplyParameters(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyParameters(CameraSettings cameraSettings)
|
||||||
|
{
|
||||||
|
_cameraControl.SetParameters(cameraSettings);
|
||||||
|
|
||||||
|
_lightControl.SetLight(cameraSettings.LightPwm1, cameraSettings.LightPwm2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSettingsLocally()
|
||||||
|
{
|
||||||
|
File.WriteAllText("settings.json", JsonConvert.SerializeObject(_imageSettingsToApply));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReceiveLoop()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
|
||||||
|
var data = _commandSocket.Receive();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ProcessCommand(data);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerLoop()
|
||||||
|
{
|
||||||
|
while (_autoTrigger)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_applySettings)
|
||||||
|
{
|
||||||
|
ApplyParameters(_imageSettingsToApply);
|
||||||
|
_applySettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendImage();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCommand(byte[] data)
|
||||||
|
{
|
||||||
|
Console.WriteLine(((ECommand) data[0]).ToString());
|
||||||
|
switch ((ECommand) data[0])
|
||||||
|
{
|
||||||
|
case ECommand.Connect:
|
||||||
|
{
|
||||||
|
var port = new byte[4];
|
||||||
|
Array.Copy(data, 1, port, 0, 4);
|
||||||
|
_imageSocket.Connect(new IPEndPoint((_commandSocket.LastConnection as IPEndPoint).Address, BitConverter.ToInt32(port,0)),null);
|
||||||
|
var databytes = new byte[1000];
|
||||||
|
var settings = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_imageSettingsToApply));
|
||||||
|
Array.Copy(settings, 0, databytes, 1, settings.Length);
|
||||||
|
databytes[0] = (byte) ECommand.Settings;
|
||||||
|
_commandSocket.SendData(databytes);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.Trigger:
|
||||||
|
{
|
||||||
|
Task.Run(() => { SendImage(); });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.StartContinuous:
|
||||||
|
{
|
||||||
|
_autoTrigger = true;
|
||||||
|
var th = new Thread(TriggerLoop);
|
||||||
|
th.Start();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.StopContinuous:
|
||||||
|
{
|
||||||
|
_autoTrigger = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.Settings:
|
||||||
|
{
|
||||||
|
var databytes = new byte[999];
|
||||||
|
Array.Copy(data, 1, databytes, 0, 999);
|
||||||
|
var settings = JsonConvert.DeserializeObject<CameraSettings>(Encoding.UTF8.GetString(databytes));
|
||||||
|
_imageSettingsToApply = settings;
|
||||||
|
if (_autoTrigger)
|
||||||
|
_applySettings = true;
|
||||||
|
else
|
||||||
|
ApplyParameters(_imageSettingsToApply);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.SaveSettings:
|
||||||
|
{
|
||||||
|
SaveSettingsLocally();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendImage()
|
||||||
|
{
|
||||||
|
var image = _imageSource.GetImage();
|
||||||
|
var b = new byte[image.Length + 1];
|
||||||
|
b[0] = (byte) EData.Image;
|
||||||
|
Array.Copy(image, 0, b, 1, image.Length);
|
||||||
|
|
||||||
|
_imageSocket.SendData(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
269
framework/Inspectron.HawkEye/Protocol/ImageServerTCP.cs
Normal file
269
framework/Inspectron.HawkEye/Protocol/ImageServerTCP.cs
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Inspectron.HawkEye.Protocol.Interfaces;
|
||||||
|
using Inspectron.HawkEye.UDPB;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public class ImageServerTCP
|
||||||
|
{
|
||||||
|
private readonly IImageSource _imageSource;
|
||||||
|
private readonly ICameraControl _cameraControl;
|
||||||
|
private readonly ILightControl _lightControl;
|
||||||
|
private TcpListener _commandSocket;
|
||||||
|
private TcpClient _imageSocket;
|
||||||
|
|
||||||
|
private bool _autoTrigger;
|
||||||
|
private bool _applySettings;
|
||||||
|
|
||||||
|
private CameraSettings _imageSettingsToApply=new CameraSettings(){ImageSettings = new ImageSettings()};
|
||||||
|
private TcpClient _lastClient;
|
||||||
|
|
||||||
|
public ImageServerTCP(IImageSource imageSource, ICameraControl cameraControl, ILightControl lightControl)
|
||||||
|
{
|
||||||
|
_imageSource = imageSource;
|
||||||
|
_cameraControl = cameraControl;
|
||||||
|
_lightControl = lightControl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
_commandSocket = new TcpListener(IPAddress.Any, 27001);
|
||||||
|
_commandSocket.Start();
|
||||||
|
Console.WriteLine("listen tcp");
|
||||||
|
var th = new Thread(ReceiveLoop);
|
||||||
|
th.Start();
|
||||||
|
if (File.Exists("settings.json"))
|
||||||
|
{
|
||||||
|
var settings = JsonConvert.DeserializeObject<CameraSettings>(File.ReadAllText("settings.json"));
|
||||||
|
_imageSettingsToApply = settings;
|
||||||
|
ApplyParameters(settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyParameters(CameraSettings cameraSettings, bool setLight = false)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Setting parameters:"+JsonConvert.SerializeObject(cameraSettings,Formatting.Indented));
|
||||||
|
_cameraControl.SetParameters(cameraSettings);
|
||||||
|
if(setLight&&!cameraSettings.TriggerLights) _lightControl.SetLight(cameraSettings.LightPwm1, cameraSettings.LightPwm2);
|
||||||
|
else _lightControl.SetLight(0, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSettingsLocally()
|
||||||
|
{
|
||||||
|
File.WriteAllText("settings.json", JsonConvert.SerializeObject(_imageSettingsToApply,Formatting.Indented));
|
||||||
|
}
|
||||||
|
byte[] _commandBuffer = new byte[1500];
|
||||||
|
private Task _lastTriggerTask=Task.CompletedTask;
|
||||||
|
|
||||||
|
private void ReceiveLoop()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
|
||||||
|
_lastClient=_commandSocket.AcceptTcpClient();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_lastClient.GetStream().Read(_commandBuffer, 0, 1500);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessCommand(_commandBuffer);
|
||||||
|
} while (_lastClient.Connected);
|
||||||
|
CancelTrigger();
|
||||||
|
_lightControl.SetLight(0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerLoop()
|
||||||
|
{
|
||||||
|
while (_autoTrigger)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_applySettings)
|
||||||
|
{
|
||||||
|
ApplyParameters(_imageSettingsToApply,true);
|
||||||
|
_applySettings = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendImage();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessCommand(byte[] data)
|
||||||
|
{
|
||||||
|
Console.WriteLine(((ECommand) data[0]).ToString());
|
||||||
|
switch ((ECommand) data[0])
|
||||||
|
{
|
||||||
|
case ECommand.Connect:
|
||||||
|
{
|
||||||
|
var port = new byte[4];
|
||||||
|
Array.Copy(data, 1, port, 0, 4);
|
||||||
|
_imageSocket=new TcpClient();
|
||||||
|
_imageSocket.Connect(new IPEndPoint((_lastClient.Client.RemoteEndPoint as IPEndPoint).Address, BitConverter.ToInt32(port,0)));
|
||||||
|
var databytes = new byte[1000];
|
||||||
|
var settings = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_imageSettingsToApply));
|
||||||
|
Array.Copy(settings, 0, databytes, 1, settings.Length);
|
||||||
|
databytes[0] = (byte) ECommand.Settings;
|
||||||
|
_lastClient.Client.SendData(databytes);
|
||||||
|
Console.WriteLine("data sent");
|
||||||
|
|
||||||
|
|
||||||
|
_lightControl.SetLight(_imageSettingsToApply.LightPwm1, _imageSettingsToApply.LightPwm2);
|
||||||
|
_autoTrigger = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.GetCalibration:
|
||||||
|
{
|
||||||
|
var databytes = new byte[1500];
|
||||||
|
if (File.Exists("calibration.calib"))
|
||||||
|
{
|
||||||
|
databytes[0] = (byte)ECommand.OK;
|
||||||
|
Array.Copy(File.ReadAllBytes("calibration.calib"),0,databytes,1,1500-1);
|
||||||
|
_lastClient.Client.SendData(databytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
databytes[0] = (byte)ECommand.NotOK;
|
||||||
|
_lastClient.Client.SendData(databytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.Trigger:
|
||||||
|
{
|
||||||
|
CancelTrigger();
|
||||||
|
_lastTriggerTask=Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendImage();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.WriteLine(e.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.StartContinuous:
|
||||||
|
{
|
||||||
|
_autoTrigger = true;
|
||||||
|
var th = new Thread(TriggerLoop);
|
||||||
|
th.Start();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.StopContinuous:
|
||||||
|
{
|
||||||
|
_autoTrigger = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.Settings:
|
||||||
|
{
|
||||||
|
CancelTrigger();
|
||||||
|
var databytes = new byte[999];
|
||||||
|
Array.Copy(data, 1, databytes, 0, 999);
|
||||||
|
var settings = JsonConvert.DeserializeObject<CameraSettings>(Encoding.UTF8.GetString(databytes));
|
||||||
|
_imageSettingsToApply = settings;
|
||||||
|
if (_autoTrigger)
|
||||||
|
_applySettings = true;
|
||||||
|
else
|
||||||
|
ApplyParameters(_imageSettingsToApply,true);
|
||||||
|
|
||||||
|
_lastClient.Client.Send(new []{(byte)EData.OK});
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.SaveCalibration:
|
||||||
|
{
|
||||||
|
|
||||||
|
var databytes = new byte[1500 - 1];
|
||||||
|
Array.Copy(data, 1, databytes, 0, 1500 - 1);
|
||||||
|
File.WriteAllBytes("calibration.calib",databytes);
|
||||||
|
|
||||||
|
_lastClient.Client.Send(new[] { (byte)EData.OK });
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ECommand.SaveSettings:
|
||||||
|
{
|
||||||
|
SaveSettingsLocally();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CancelTrigger()
|
||||||
|
{
|
||||||
|
_imageSource.CancelTrigger();
|
||||||
|
_lastTriggerTask.Wait();
|
||||||
|
|
||||||
|
}
|
||||||
|
private void SendImage()
|
||||||
|
{
|
||||||
|
if (_imageSettingsToApply.TriggerLights)
|
||||||
|
{
|
||||||
|
_lightControl.SetLight(_imageSettingsToApply.LightPwm1, _imageSettingsToApply.LightPwm2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var image = _imageSource.GetImage();
|
||||||
|
if (image.Length == 0) return;
|
||||||
|
if (_imageSettingsToApply.TriggerLights)
|
||||||
|
{
|
||||||
|
_lightControl.SetLight(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = EncodeImage(image);
|
||||||
|
|
||||||
|
_imageSocket.Client.SendData(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] EncodeImage(byte[] image)
|
||||||
|
{
|
||||||
|
var b = new byte[image.Length + 5];
|
||||||
|
b[0] = (byte) EData.Image;
|
||||||
|
var byteSize = BitConverter.GetBytes(image.Length);
|
||||||
|
Array.Copy(byteSize, 0, b, 1, 4);
|
||||||
|
Array.Copy(image, 0, b, 5, image.Length);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
private static byte[] EncodeChanneledImage(byte[] image,byte channels)
|
||||||
|
{
|
||||||
|
var b = new byte[image.Length + 6];
|
||||||
|
b[0] = (byte)EData.Image;
|
||||||
|
b[1] = channels;
|
||||||
|
var byteSize = BitConverter.GetBytes(image.Length);
|
||||||
|
Array.Copy(byteSize, 0, b, 2, 4);
|
||||||
|
Array.Copy(image, 0, b, 6, image.Length);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
framework/Inspectron.HawkEye/Protocol/ImageSettings.cs
Normal file
17
framework/Inspectron.HawkEye/Protocol/ImageSettings.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 0)]
|
||||||
|
public struct ImageSettings
|
||||||
|
{
|
||||||
|
public int Shutter { get; set; }
|
||||||
|
public int Gain { get; set; }
|
||||||
|
public int SensorWidth { get; set; }
|
||||||
|
public int Lines { get; set; }
|
||||||
|
public int CaptureBuffer { get; set; }
|
||||||
|
public int UseExternalTrigger { get; set; }
|
||||||
|
public int Divider { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Inspectron.HawkEye.Protocol.Interfaces
|
||||||
|
{
|
||||||
|
public interface ICameraControl
|
||||||
|
{
|
||||||
|
|
||||||
|
void SetParameters(CameraSettings imageSettings);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Inspectron.HawkEye.Protocol.Interfaces
|
||||||
|
{
|
||||||
|
public interface IImageSource
|
||||||
|
{
|
||||||
|
byte[] GetImage();
|
||||||
|
void CancelTrigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Inspectron.HawkEye.Protocol.Interfaces
|
||||||
|
{
|
||||||
|
public interface ILightControl
|
||||||
|
{
|
||||||
|
void SetLight(int pwm1, int pwm2);
|
||||||
|
}
|
||||||
|
}
|
||||||
23
framework/Inspectron.HawkEye/Protocol/SocketExtensions.cs
Normal file
23
framework/Inspectron.HawkEye/Protocol/SocketExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Inspectron.HawkEye.RTSP;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.Protocol
|
||||||
|
{
|
||||||
|
public static class SocketExtensions
|
||||||
|
{
|
||||||
|
public static void Listen(this Socket self,IPAddress adapterAddress,int port)
|
||||||
|
{
|
||||||
|
self.Bind(new IPEndPoint(adapterAddress,port));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SendData(this Socket self, byte[] data)
|
||||||
|
{
|
||||||
|
Console.WriteLine("send data");
|
||||||
|
|
||||||
|
|
||||||
|
self.Send(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
136
framework/Inspectron.HawkEye/RTSP/AACPayload.cs
Normal file
136
framework/Inspectron.HawkEye/RTSP/AACPayload.cs
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
// This class handles the AAC-hbd (High Bitrate) Payload
|
||||||
|
// It has methods to process the RTP Payload
|
||||||
|
|
||||||
|
// (c) 2018 Roger Hardiman, RJH Technical Consultancy Ltd
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
RFC 3640
|
||||||
|
3.3.6. High Bit-rate AAC
|
||||||
|
|
||||||
|
This mode is signaled by mode=AAC-hbr.This mode supports the
|
||||||
|
transportation of variable size AAC frames.In one RTP packet,
|
||||||
|
either one or more complete AAC frames are carried, or a single
|
||||||
|
fragment of an AAC frame is carried.In this mode, the AAC frames
|
||||||
|
are allowed to be interleaved and hence receivers MUST support de-
|
||||||
|
interleaving.The maximum size of an AAC frame in this mode is 8191
|
||||||
|
octets.
|
||||||
|
|
||||||
|
In this mode, the RTP payload consists of the AU Header Section,
|
||||||
|
followed by either one AAC frame, several concatenated AAC frames or
|
||||||
|
one fragmented AAC frame.The Auxiliary Section MUST be empty. For
|
||||||
|
each AAC frame contained in the payload, there MUST be an AU-header
|
||||||
|
in the AU Header Section to provide:
|
||||||
|
|
||||||
|
a) the size of each AAC frame in the payload and
|
||||||
|
|
||||||
|
b) index information for computing the sequence(and hence timing) of
|
||||||
|
each AAC frame.
|
||||||
|
|
||||||
|
To code the maximum size of an AAC frame requires 13 bits.
|
||||||
|
Therefore, in this configuration 13 bits are allocated to the AU-
|
||||||
|
size, and 3 bits to the AU-Index(-delta) field.Thus, each AU-header
|
||||||
|
has a size of 2 octets.Each AU-Index field MUST be coded with the
|
||||||
|
value 0. In the AU Header Section, the concatenated AU-headers MUST
|
||||||
|
be preceded by the 16-bit AU-headers-length field, as specified in
|
||||||
|
section 3.2.1.
|
||||||
|
|
||||||
|
In addition to the required MIME format parameters, the following
|
||||||
|
parameters MUST be present: sizeLength, indexLength, and
|
||||||
|
indexDeltaLength.AAC frames always have a fixed duration per Access
|
||||||
|
Unit; when interleaving in this mode, this specific duration MUST be
|
||||||
|
signaled by the MIME format parameter constantDuration.In addition,
|
||||||
|
the parameter maxDisplacement MUST be present when interleaving.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
m= audio 49230 RTP/AVP 96
|
||||||
|
a= rtpmap:96 mpeg4-generic/48000/6
|
||||||
|
a= fmtp:96 streamtype= 5; profile-level-id= 16; mode= AAC-hbr;config= 11B0; sizeLength= 13; indexLength= 3;indexDeltaLength= 3; constantDuration= 1024
|
||||||
|
|
||||||
|
The hexadecimal value of the "config" parameter is the AudioSpecificConfig(), as defined in ISO/IEC 14496-3.
|
||||||
|
AudioSpecificConfig() specifies a 5.1 channel AAC stream with a sampling rate of 48 kHz.For the description of MIME parameters, see
|
||||||
|
section 4.1.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
public class AACPayload
|
||||||
|
{
|
||||||
|
public uint ObjectType = 0;
|
||||||
|
public uint FrequencyIndex = 0;
|
||||||
|
public uint ChannelConfiguration = 0;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public AACPayload(String config_string)
|
||||||
|
{
|
||||||
|
/***
|
||||||
|
5 bits: object type
|
||||||
|
if (object type == 31)
|
||||||
|
6 bits + 32: object type
|
||||||
|
4 bits: frequency index
|
||||||
|
if (frequency index == 15)
|
||||||
|
24 bits: frequency
|
||||||
|
4 bits: channel configuration
|
||||||
|
var bits: AOT Specific Config
|
||||||
|
***/
|
||||||
|
|
||||||
|
// config is a string in hex eg 1490 or 0x1210
|
||||||
|
// Read each ASCII character and add to a bit array
|
||||||
|
BitStream bs = new BitStream();
|
||||||
|
bs.AddHexString(config_string);
|
||||||
|
|
||||||
|
// Read 5 bits
|
||||||
|
ObjectType = bs.Read(5);
|
||||||
|
|
||||||
|
// Read 4 bits
|
||||||
|
FrequencyIndex = bs.Read(4);
|
||||||
|
|
||||||
|
// Read 4 bits
|
||||||
|
ChannelConfiguration = bs.Read(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> Process_AAC_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
// RTP Payload for MPEG4-GENERIC can consist of multple blocks.
|
||||||
|
// Each block has 3 parts
|
||||||
|
// Part 1 - Acesss Unit Header Length + Header
|
||||||
|
// Part 2 - Access Unit Auxiliary Data Length + Data (not used in AAC High Bitrate)
|
||||||
|
// Part 3 - Access Unit Audio Data
|
||||||
|
|
||||||
|
// The rest of the RTP packet is the AMR data
|
||||||
|
List<byte[]> audio_data = new List<byte[]>();
|
||||||
|
|
||||||
|
int ptr = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (ptr + 4 > rtp_payload.Length) break; // 2 bytes for AU Header Length, 2 bytes of AU Header payload
|
||||||
|
|
||||||
|
// Get Size of the AU Header
|
||||||
|
int au_headers_length_bits = (((rtp_payload[ptr] << 8) + (rtp_payload[ptr + 1] << 0))); // 16 bits
|
||||||
|
int au_headers_length = (int)Math.Ceiling((double)au_headers_length_bits / 8.0);
|
||||||
|
ptr += 2;
|
||||||
|
|
||||||
|
// Examine the AU Header. Get the size of the AAC data
|
||||||
|
int aac_frame_size = (((rtp_payload[ptr] << 8) + (rtp_payload[ptr+1] << 0)) >> 3); // 13 bits
|
||||||
|
int aac_index_delta = rtp_payload[ptr+1] & 0x03; // 3 bits
|
||||||
|
ptr += au_headers_length;
|
||||||
|
|
||||||
|
// extract the AAC block
|
||||||
|
if (ptr + aac_frame_size > rtp_payload.Length) break; // not enough data to copy
|
||||||
|
byte[] aac_data = new byte[aac_frame_size];
|
||||||
|
System.Array.Copy(rtp_payload, ptr, aac_data, 0, aac_frame_size);
|
||||||
|
audio_data.Add(aac_data);
|
||||||
|
ptr += aac_frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audio_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
34
framework/Inspectron.HawkEye/RTSP/AMRPayload.cs
Normal file
34
framework/Inspectron.HawkEye/RTSP/AMRPayload.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
// This class handles the AMR Payload
|
||||||
|
// It has methods to process the RTP Payload
|
||||||
|
|
||||||
|
public class AMRPayload
|
||||||
|
{
|
||||||
|
// Constructor
|
||||||
|
public AMRPayload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> Process_AMR_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
// Octet-Aligned Mode (RFC 4867 Section 4.4.1)
|
||||||
|
|
||||||
|
// First byte is the Payload Header
|
||||||
|
if (rtp_payload.Length < 1) return null;
|
||||||
|
byte payloadHeader = rtp_payload[0];
|
||||||
|
|
||||||
|
// The rest of the RTP packet is the AMR data
|
||||||
|
List<byte[]> audio_data = new List<byte[]>();
|
||||||
|
|
||||||
|
byte[] amr_data = new byte[rtp_payload.Length - 1];
|
||||||
|
System.Array.Copy(rtp_payload,1,amr_data,0,rtp_payload.Length-1);
|
||||||
|
audio_data.Add(amr_data);
|
||||||
|
|
||||||
|
return audio_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
183
framework/Inspectron.HawkEye/RTSP/Authentication.cs
Normal file
183
framework/Inspectron.HawkEye/RTSP/Authentication.cs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
using System;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using Inspectron.HawkEye.RTSP.Messages;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
|
||||||
|
// WWW-Authentication and Authorization Headers
|
||||||
|
public class Authentication
|
||||||
|
{
|
||||||
|
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
public enum Type {Basic, Digest};
|
||||||
|
|
||||||
|
private String username = null;
|
||||||
|
private String password = null;
|
||||||
|
private String realm = null;
|
||||||
|
private String nonce = null;
|
||||||
|
private Type authentication_type = Type.Digest;
|
||||||
|
private readonly MD5 md5 = System.Security.Cryptography.MD5.Create();
|
||||||
|
|
||||||
|
|
||||||
|
private const char quote = '\"';
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Authentication(String username, String password, String realm, Type authentication_type) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.realm = realm;
|
||||||
|
this.authentication_type = authentication_type;
|
||||||
|
|
||||||
|
this.nonce = new Random().Next(100000000,999999999).ToString(); // random 9 digit number
|
||||||
|
}
|
||||||
|
|
||||||
|
public String GetHeader() {
|
||||||
|
if (authentication_type == Type.Basic) {
|
||||||
|
return "Basic realm=" + quote + realm + quote;
|
||||||
|
}
|
||||||
|
if (authentication_type == Type.Digest) {
|
||||||
|
return "Digest realm=" + quote + realm + quote + ", nonce=" + quote + nonce + quote;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsValid(RtspMessage received_message) {
|
||||||
|
|
||||||
|
string authorization = received_message.Headers["Authorization"];
|
||||||
|
|
||||||
|
|
||||||
|
// Check Username and Password
|
||||||
|
if (authentication_type == Type.Basic && authorization.StartsWith("Basic ")) {
|
||||||
|
string base64_str = authorization.Substring(6); // remove 'Basic '
|
||||||
|
byte[] data = Convert.FromBase64String(base64_str);
|
||||||
|
string decoded = Encoding.UTF8.GetString(data);
|
||||||
|
int split_position = decoded.IndexOf(':');
|
||||||
|
string decoded_username = decoded.Substring(0, split_position);
|
||||||
|
string decoded_password = decoded.Substring(split_position + 1);
|
||||||
|
|
||||||
|
if ((decoded_username == username) && (decoded_password == password)) {
|
||||||
|
_logger.Debug("Basic Authorization passed");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_logger.Debug("Basic Authorization failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check Username, URI, Nonce and the MD5 hashed Response
|
||||||
|
if (authentication_type == Type.Digest && authorization.StartsWith("Digest ")) {
|
||||||
|
string value_str = authorization.Substring(7); // remove 'Digest '
|
||||||
|
string[] values = value_str.Split(',');
|
||||||
|
string auth_header_username = null;
|
||||||
|
string auth_header_realm = null;
|
||||||
|
string auth_header_nonce = null;
|
||||||
|
string auth_header_uri = null;
|
||||||
|
string auth_header_response = null;
|
||||||
|
string message_method = null;
|
||||||
|
string message_uri = null;
|
||||||
|
try {
|
||||||
|
message_method = received_message.Command.Split(' ')[0];
|
||||||
|
message_uri = received_message.Command.Split(' ')[1];
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
foreach (string value in values) {
|
||||||
|
string[] tuple = value.Trim().Split(new char[] {'='},2); // split on first '='
|
||||||
|
if (tuple.Length == 2 && tuple[0].Equals("username")) {
|
||||||
|
auth_header_username = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
|
||||||
|
}
|
||||||
|
else if (tuple.Length == 2 && tuple[0].Equals("realm")) {
|
||||||
|
auth_header_realm = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
|
||||||
|
}
|
||||||
|
else if (tuple.Length == 2 && tuple[0].Equals("nonce")) {
|
||||||
|
auth_header_nonce = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
|
||||||
|
}
|
||||||
|
else if (tuple.Length == 2 && tuple[0].Equals("uri")) {
|
||||||
|
auth_header_uri = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
|
||||||
|
}
|
||||||
|
else if (tuple.Length == 2 && tuple[0].Equals("response")) {
|
||||||
|
auth_header_response = tuple[1].Trim(new char[] {' ','\"'}); // trim space and quotes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the MD5 Hash using all parameters passed in the Auth Header with the
|
||||||
|
// addition of the 'Password'
|
||||||
|
String hashA1 = CalculateMD5Hash(md5, auth_header_username+":"+auth_header_realm+":"+this.password);
|
||||||
|
String hashA2 = CalculateMD5Hash(md5, message_method + ":" + auth_header_uri);
|
||||||
|
String expected_response = CalculateMD5Hash(md5, hashA1 + ":" + auth_header_nonce + ":" + hashA2);
|
||||||
|
|
||||||
|
// Check if everything matches
|
||||||
|
// ToDo - extract paths from the URIs (ignoring SETUP's trackID)
|
||||||
|
if ((auth_header_username == this.username)
|
||||||
|
&& (auth_header_realm == this.realm)
|
||||||
|
&& (auth_header_nonce == this.nonce)
|
||||||
|
&& (auth_header_response == expected_response)
|
||||||
|
){
|
||||||
|
_logger.Debug("Digest Authorization passed");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
_logger.Debug("Digest Authorization failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Generate Basic or Digest Authorization
|
||||||
|
public string GenerateAuthorization(string username, string password,
|
||||||
|
string auth_type, string realm, string nonce, string url, string command) {
|
||||||
|
|
||||||
|
if (username == null || username.Length == 0) return null;
|
||||||
|
if (password == null || password.Length == 0) return null;
|
||||||
|
if (realm == null || realm.Length == 0) return null;
|
||||||
|
if (auth_type.Equals("Digest") && (nonce == null || nonce.Length == 0)) return null;
|
||||||
|
|
||||||
|
if (auth_type.Equals("Basic")) {
|
||||||
|
byte[] credentials = System.Text.Encoding.UTF8.GetBytes(username+":"+password);
|
||||||
|
String credentials_base64 = Convert.ToBase64String(credentials);
|
||||||
|
String basic_authorization = "Basic " + credentials_base64;
|
||||||
|
return basic_authorization;
|
||||||
|
}
|
||||||
|
else if (auth_type.Equals("Digest")) {
|
||||||
|
|
||||||
|
MD5 md5 = System.Security.Cryptography.MD5.Create();
|
||||||
|
String hashA1 = CalculateMD5Hash(md5, username+":"+realm+":"+password);
|
||||||
|
String hashA2 = CalculateMD5Hash(md5, command + ":" + url);
|
||||||
|
String response = CalculateMD5Hash(md5, hashA1 + ":" + nonce + ":" + hashA2);
|
||||||
|
|
||||||
|
const String quote = "\"";
|
||||||
|
String digest_authorization = "Digest username=" + quote + username + quote +", "
|
||||||
|
+ "realm=" + quote + realm + quote + ", "
|
||||||
|
+ "nonce=" + quote + nonce + quote + ", "
|
||||||
|
+ "uri=" + quote + url + quote + ", "
|
||||||
|
+ "response=" + quote + response + quote;
|
||||||
|
|
||||||
|
return digest_authorization;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// MD5 (lower case)
|
||||||
|
private string CalculateMD5Hash(MD5 md5_session, string input)
|
||||||
|
{
|
||||||
|
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
|
||||||
|
byte[] hash = md5_session.ComputeHash(inputBytes);
|
||||||
|
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
for (int i = 0; i < hash.Length; i++) {
|
||||||
|
output.Append(hash[i].ToString("x2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
framework/Inspectron.HawkEye/RTSP/BitStream.cs
Normal file
88
framework/Inspectron.HawkEye/RTSP/BitStream.cs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
// (c) 2018 Roger Hardiman, RJH Technical Consultancy Ltd
|
||||||
|
// Simple class to Read and Write bits in a bit stream.
|
||||||
|
// Data is written to the end of the bit stream and the bit stream can be returned as a Byte Array
|
||||||
|
// Data can be read from the head of the bit stream
|
||||||
|
// Example
|
||||||
|
// bitstream.AddValue(0xA,4); // Write 4 bit value
|
||||||
|
// bitstream.AddValue(0xB,4);
|
||||||
|
// bitstream.AddValue(0xC,4);
|
||||||
|
// bitstream.AddValue(0xD,4);
|
||||||
|
// bitstream.ToArray() -> {0xAB, 0xCD} // Return Byte Array
|
||||||
|
// bitstream.Read(8) -> 0xAB // Read 8 bit value
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
|
||||||
|
// Very simple bitstream
|
||||||
|
public class BitStream {
|
||||||
|
|
||||||
|
private List <byte> data = new List<byte>(); // List only stores 0 or 1 (one 'bit' per List item)
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public BitStream() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddValue(int value, int num_bits) {
|
||||||
|
// Add each bit to the List
|
||||||
|
for (int i = num_bits-1; i >= 0; i--) {
|
||||||
|
data.Add((byte)((value>>i) & 0x01));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddHexString(String hex_string) {
|
||||||
|
char[] hex_chars = hex_string.ToUpper().ToCharArray();
|
||||||
|
foreach (char c in hex_chars) {
|
||||||
|
if ((c.Equals('0'))) this.AddValue(0,4);
|
||||||
|
else if ((c.Equals('1'))) this.AddValue(1, 4);
|
||||||
|
else if ((c.Equals('2'))) this.AddValue(2, 4);
|
||||||
|
else if ((c.Equals('3'))) this.AddValue(3, 4);
|
||||||
|
else if ((c.Equals('4'))) this.AddValue(4, 4);
|
||||||
|
else if ((c.Equals('5'))) this.AddValue(5, 4);
|
||||||
|
else if ((c.Equals('6'))) this.AddValue(6, 4);
|
||||||
|
else if ((c.Equals('7'))) this.AddValue(7, 4);
|
||||||
|
else if ((c.Equals('8'))) this.AddValue(8, 4);
|
||||||
|
else if ((c.Equals('9'))) this.AddValue(9, 4);
|
||||||
|
else if ((c.Equals('A'))) this.AddValue(10, 4);
|
||||||
|
else if ((c.Equals('B'))) this.AddValue(11, 4);
|
||||||
|
else if ((c.Equals('C'))) this.AddValue(12, 4);
|
||||||
|
else if ((c.Equals('D'))) this.AddValue(13, 4);
|
||||||
|
else if ((c.Equals('E'))) this.AddValue(14, 4);
|
||||||
|
else if ((c.Equals('F'))) this.AddValue(15, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint Read(int num_bits) {
|
||||||
|
// Read and remove items from the front of the list of bits
|
||||||
|
if (data.Count < num_bits) return 0;
|
||||||
|
uint result = 0;
|
||||||
|
for (int i = 0; i < num_bits; i++) {
|
||||||
|
result = result << 1;
|
||||||
|
result = result + data[0];
|
||||||
|
data.RemoveAt(0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] ToArray() {
|
||||||
|
int num_bytes = (int)Math.Ceiling((double)data.Count/8.0);
|
||||||
|
byte[] array = new byte[num_bytes];
|
||||||
|
int ptr = 0;
|
||||||
|
int shift = 7;
|
||||||
|
for (int i = 0; i < data.Count; i++) {
|
||||||
|
array[ptr] += (byte)(data[i] << shift);
|
||||||
|
if (shift == 0) {
|
||||||
|
shift = 7;
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
shift--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1128
framework/Inspectron.HawkEye/RTSP/Client/RTSPClient.cs
Normal file
1128
framework/Inspectron.HawkEye/RTSP/Client/RTSPClient.cs
Normal file
File diff suppressed because it is too large
Load Diff
64
framework/Inspectron.HawkEye/RTSP/G711Payload.cs
Normal file
64
framework/Inspectron.HawkEye/RTSP/G711Payload.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
// This class handles the G711 Payload
|
||||||
|
// It has methods to process the RTP Payload
|
||||||
|
|
||||||
|
public class G711Payload
|
||||||
|
{
|
||||||
|
// Constructor
|
||||||
|
public G711Payload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> Process_G711_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
List<byte[]> audio_data = new List<byte[]>();
|
||||||
|
audio_data.Add(rtp_payload);
|
||||||
|
|
||||||
|
return audio_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Untested - used with G711.1 and PCMA-WB and PCMU-WB Codec Names */
|
||||||
|
public List<byte[]> Process_G711_1_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
// Look at the Header. This tells us the G711 mode being used
|
||||||
|
|
||||||
|
// Mode Index (MI) is
|
||||||
|
// 1 - R1 40 octets containg Layer 0 data
|
||||||
|
// 2 - R2a 50 octets containing Layer 0 plus Layer 1 data
|
||||||
|
// 3 - R2b 50 octets containing Layer 0 plus Layer 2 data
|
||||||
|
// 4 - R3 60 octets containing Layer 0 plus Layer 1 plus Layer 2 data
|
||||||
|
|
||||||
|
byte mode_index = (byte)(rtp_payload[0] & 0x07);
|
||||||
|
|
||||||
|
int size_of_one_frame = 0; // will be in bytes
|
||||||
|
switch (mode_index) {
|
||||||
|
case 1: size_of_one_frame = 40; break;
|
||||||
|
case 2: size_of_one_frame = 50; break;
|
||||||
|
case 3: size_of_one_frame = 50; break;
|
||||||
|
case 4: size_of_one_frame = 60; break;
|
||||||
|
default: return null; // invalid Mode Index
|
||||||
|
}
|
||||||
|
|
||||||
|
int number_frames = (rtp_payload.Length - 1) / size_of_one_frame;
|
||||||
|
|
||||||
|
|
||||||
|
// Return just the basic u-Law or A-Law audio (the Layer 0 audio)
|
||||||
|
|
||||||
|
List<byte[]> audio_data = new List<byte[]>();
|
||||||
|
|
||||||
|
// Extract each audio frame and place in the audio_data List
|
||||||
|
int frame_start = 1; // starts just after the MI header
|
||||||
|
while (frame_start + size_of_one_frame < rtp_payload.Length) {
|
||||||
|
byte[] layer_0_audio = new byte[40];
|
||||||
|
System.Array.Copy(rtp_payload,frame_start,layer_0_audio,0,40); // 40 octets in Layer 0 data
|
||||||
|
audio_data.Add(layer_0_audio);
|
||||||
|
|
||||||
|
frame_start += size_of_one_frame;
|
||||||
|
}
|
||||||
|
return audio_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
184
framework/Inspectron.HawkEye/RTSP/H264Payload.cs
Normal file
184
framework/Inspectron.HawkEye/RTSP/H264Payload.cs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
// This class handles the H264 Payload
|
||||||
|
// It has methods to parse parameters in the SDP
|
||||||
|
// It has methods to process the RTP Payload
|
||||||
|
|
||||||
|
public class H264Payload
|
||||||
|
{
|
||||||
|
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
int norm, fu_a, fu_b, stap_a, stap_b, mtap16, mtap24 = 0; // used for diagnostics stats
|
||||||
|
|
||||||
|
List<byte[]> temporary_rtp_payloads = new List<byte[]>(); // used to assemble the RTP packets that form one RTP Frame
|
||||||
|
// Eg all the RTP Packets from M=0 through to M=1
|
||||||
|
|
||||||
|
MemoryStream fragmented_nal = new MemoryStream(); // used to concatenate fragmented H264 NALs where NALs are split over RTP packets
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public H264Payload()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> Process_H264_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
// Add to the list of payloads for the current Frame of video
|
||||||
|
temporary_rtp_payloads.Add(rtp_payload); // Todo Could optimise this and go direct to Process Frame if just 1 packet in frame
|
||||||
|
|
||||||
|
if (rtp_marker == 1)
|
||||||
|
{
|
||||||
|
// End Marker is set. Process the list of RTP Packets (forming 1 RTP frame) and save the NALs to a file
|
||||||
|
List<byte[]> nal_units = Process_H264_RTP_Frame(temporary_rtp_payloads);
|
||||||
|
temporary_rtp_payloads.Clear();
|
||||||
|
|
||||||
|
return nal_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // we don't have a frame yet. Keep accumulating RTP packets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Process a RTP Frame. A RTP Frame can consist of several RTP Packets which have the same Timestamp
|
||||||
|
// Returns a list of NAL Units (with no 00 00 00 01 header and with no Size header)
|
||||||
|
private List<byte[]> Process_H264_RTP_Frame(List<byte[]> rtp_payloads)
|
||||||
|
{
|
||||||
|
_logger.Debug("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
|
||||||
|
|
||||||
|
List<byte[]> nal_units = new List<byte[]>(); // Stores the NAL units for a Video Frame. May be more than one NAL unit in a video frame.
|
||||||
|
|
||||||
|
for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++)
|
||||||
|
{
|
||||||
|
// Examine the first rtp_payload and the first byte (the NAL header)
|
||||||
|
int nal_header_f_bit = (rtp_payloads[payload_index][0] >> 7) & 0x01;
|
||||||
|
int nal_header_nri = (rtp_payloads[payload_index][0] >> 5) & 0x03;
|
||||||
|
int nal_header_type = (rtp_payloads[payload_index][0] >> 0) & 0x1F;
|
||||||
|
|
||||||
|
// If the Nal Header Type is in the range 1..23 this is a normal NAL (not fragmented)
|
||||||
|
// So write the NAL to the file
|
||||||
|
if (nal_header_type >= 1 && nal_header_type <= 23)
|
||||||
|
{
|
||||||
|
_logger.Debug("Normal NAL");
|
||||||
|
norm++;
|
||||||
|
nal_units.Add(rtp_payloads[payload_index]);
|
||||||
|
}
|
||||||
|
// There are 4 types of Aggregation Packet (split over RTP payloads)
|
||||||
|
else if (nal_header_type == 24)
|
||||||
|
{
|
||||||
|
_logger.Debug("Agg STAP-A");
|
||||||
|
stap_a++;
|
||||||
|
|
||||||
|
// RTP packet contains multiple NALs, each with a 16 bit header
|
||||||
|
// Read 16 byte size
|
||||||
|
// Read NAL
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int ptr = 1; // start after the nal_header_type which was '24'
|
||||||
|
// if we have at least 2 more bytes (the 16 bit size) then consume more data
|
||||||
|
while (ptr + 2 < (rtp_payloads[payload_index].Length - 1))
|
||||||
|
{
|
||||||
|
int size = (rtp_payloads[payload_index][ptr] << 8) + (rtp_payloads[payload_index][ptr + 1] << 0);
|
||||||
|
ptr = ptr + 2;
|
||||||
|
byte[] nal = new byte[size];
|
||||||
|
System.Array.Copy(rtp_payloads[payload_index], ptr, nal, 0, size); // copy the NAL
|
||||||
|
nal_units.Add(nal); // Add to list of NALs for this RTP frame. Start Codes like 00 00 00 01 get added later
|
||||||
|
ptr = ptr + size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_logger.Debug("H264 Aggregate Packet processing error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (nal_header_type == 25)
|
||||||
|
{
|
||||||
|
_logger.Debug("Agg STAP-B not supported");
|
||||||
|
stap_b++;
|
||||||
|
}
|
||||||
|
else if (nal_header_type == 26)
|
||||||
|
{
|
||||||
|
_logger.Debug("Agg MTAP16 not supported");
|
||||||
|
mtap16++;
|
||||||
|
}
|
||||||
|
else if (nal_header_type == 27)
|
||||||
|
{
|
||||||
|
_logger.Debug("Agg MTAP24 not supported");
|
||||||
|
mtap24++;
|
||||||
|
}
|
||||||
|
else if (nal_header_type == 28)
|
||||||
|
{
|
||||||
|
_logger.Debug("Frag FU-A");
|
||||||
|
fu_a++;
|
||||||
|
|
||||||
|
// Parse Fragmentation Unit Header
|
||||||
|
int fu_header_s = (rtp_payloads[payload_index][1] >> 7) & 0x01; // start marker
|
||||||
|
int fu_header_e = (rtp_payloads[payload_index][1] >> 6) & 0x01; // end marker
|
||||||
|
int fu_header_r = (rtp_payloads[payload_index][1] >> 5) & 0x01; // reserved. should be 0
|
||||||
|
int fu_header_type = (rtp_payloads[payload_index][1] >> 0) & 0x1F; // Original NAL unit header
|
||||||
|
|
||||||
|
_logger.Debug("Frag FU-A s=" + fu_header_s + "e=" + fu_header_e);
|
||||||
|
|
||||||
|
// Check Start and End flags
|
||||||
|
if (fu_header_s == 1 && fu_header_e == 0)
|
||||||
|
{
|
||||||
|
// Start of Fragment.
|
||||||
|
// Initiise the fragmented_nal byte array
|
||||||
|
// Build the NAL header with the original F and NRI flags but use the the Type field from the fu_header_type
|
||||||
|
byte reconstructed_nal_type = (byte)((nal_header_f_bit << 7) + (nal_header_nri << 5) + fu_header_type);
|
||||||
|
|
||||||
|
// Empty the stream
|
||||||
|
fragmented_nal.SetLength(0);
|
||||||
|
|
||||||
|
// Add reconstructed_nal_type byte to the memory stream
|
||||||
|
fragmented_nal.WriteByte(reconstructed_nal_type);
|
||||||
|
|
||||||
|
// copy the rest of the RTP payload to the memory stream
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fu_header_s == 0 && fu_header_e == 0)
|
||||||
|
{
|
||||||
|
// Middle part of Fragment
|
||||||
|
// Append this payload to the fragmented_nal
|
||||||
|
// Data starts after the NAL Unit Type byte and the FU Header byte
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fu_header_s == 0 && fu_header_e == 1)
|
||||||
|
{
|
||||||
|
// End part of Fragment
|
||||||
|
// Append this payload to the fragmented_nal
|
||||||
|
// Data starts after the NAL Unit Type byte and the FU Header byte
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 2, rtp_payloads[payload_index].Length - 2);
|
||||||
|
|
||||||
|
// Add the NAL to the array of NAL units
|
||||||
|
nal_units.Add(fragmented_nal.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (nal_header_type == 29)
|
||||||
|
{
|
||||||
|
_logger.Debug("Frag FU-B not supported");
|
||||||
|
fu_b++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Debug("Unknown NAL header " + nal_header_type + " not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output some statistics
|
||||||
|
_logger.Debug("Norm=" + norm + " ST-A=" + stap_a + " ST-B=" + stap_b + " M16=" + mtap16 + " M24=" + mtap24 + " FU-A=" + fu_a + " FU-B=" + fu_b);
|
||||||
|
|
||||||
|
// Output all the NALs that form one RTP Frame (one frame of video)
|
||||||
|
return nal_units;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
224
framework/Inspectron.HawkEye/RTSP/H265Payload.cs
Normal file
224
framework/Inspectron.HawkEye/RTSP/H265Payload.cs
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
// This class handles the H265 Payload
|
||||||
|
// It has methods to parse parameters in the SDP
|
||||||
|
// It has methods to process the RTP Payload
|
||||||
|
|
||||||
|
// By Roger Hardiman, RJH Technical Consultancy Ltd
|
||||||
|
|
||||||
|
public class H265Payload
|
||||||
|
{
|
||||||
|
// H265 / HEVC structure.
|
||||||
|
// An 'Access Unit' is the set of NAL Units that form one Picture
|
||||||
|
// NAL Units have a 2 byte header comprising of
|
||||||
|
// F Bit, Type, Layer ID and TID
|
||||||
|
|
||||||
|
|
||||||
|
int single, agg, frag = 0; // used for diagnostics stats
|
||||||
|
bool has_donl = false;
|
||||||
|
|
||||||
|
List<byte[]> temporary_rtp_payloads = new List<byte[]>(); // used to assemble the RTP packets that form one RTP Frame
|
||||||
|
// Eg all the RTP Packets from M=0 through to M=1
|
||||||
|
|
||||||
|
MemoryStream fragmented_nal = new MemoryStream(); // used to concatenate fragmented H264 NALs where NALs are split over RTP packets
|
||||||
|
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public H265Payload(bool has_donl)
|
||||||
|
{
|
||||||
|
this.has_donl = has_donl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<byte[]> Process_H265_RTP_Packet(byte[] rtp_payload, int rtp_marker) {
|
||||||
|
|
||||||
|
// Add payload to the List of payloads for the current Frame of Video
|
||||||
|
// ie all the payloads with M=0 up to the final payload where M=1
|
||||||
|
temporary_rtp_payloads.Add(rtp_payload); // Todo Could optimise this and go direct to Process Frame if just 1 packet in frame
|
||||||
|
|
||||||
|
if (rtp_marker == 1)
|
||||||
|
{
|
||||||
|
// End Marker is set. Process the list of RTP Packets (forming 1 RTP frame) and save the NALs to a file
|
||||||
|
List<byte[]> nal_units = Process_H265_RTP_Frame(temporary_rtp_payloads);
|
||||||
|
temporary_rtp_payloads.Clear();
|
||||||
|
|
||||||
|
return nal_units;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // we don't have a frame yet. Keep accumulating RTP packets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Process a RTP Frame. A RTP Frame can consist of several RTP Packets which have the same Timestamp
|
||||||
|
// Returns a list of NAL Units (with no 00 00 00 01 header and with no Size header)
|
||||||
|
private List<byte[]> Process_H265_RTP_Frame(List<byte[]> rtp_payloads)
|
||||||
|
{
|
||||||
|
Console.WriteLine("RTP Data comprised of " + rtp_payloads.Count + " rtp packets");
|
||||||
|
|
||||||
|
List<byte[]> nal_units = new List<byte[]>(); // Stores the NAL units for a Video Frame. May be more than one NAL unit in a video frame.
|
||||||
|
|
||||||
|
for (int payload_index = 0; payload_index < rtp_payloads.Count; payload_index++)
|
||||||
|
{
|
||||||
|
// Examine the first two bytes of the RTP data, the Payload Header
|
||||||
|
// F (Forbidden Bit),
|
||||||
|
// Type of NAL Unit (or VCL NAL Unit if Type is < 32),
|
||||||
|
// LayerId
|
||||||
|
// TID (TemporalID = TID - 1)
|
||||||
|
/*+---------------+---------------+
|
||||||
|
*|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
|
||||||
|
*+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
*|F| Type | LayerId | TID |
|
||||||
|
*+-------------+-----------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
int payload_header = (rtp_payloads[payload_index][0] << 8) | (rtp_payloads[payload_index][1]);
|
||||||
|
int payload_header_f_bit = (payload_header >> 15) & 0x01;
|
||||||
|
int payload_header_type = (payload_header >> 9) & 0x3F;
|
||||||
|
int payload_header_layer_id = (payload_header >> 3) & 0x3F;
|
||||||
|
int payload_header_tid = payload_header & 0x7;
|
||||||
|
|
||||||
|
|
||||||
|
// There are three ways to Packetize NAL units into RTP Packets
|
||||||
|
// Single NAL Unit Packet
|
||||||
|
// Aggregation Packet (payload_header_type = 48)
|
||||||
|
// Fragmentation Unit (payload_header_type = 49)
|
||||||
|
|
||||||
|
|
||||||
|
// Single NAL Unit Packet
|
||||||
|
// 32=VPS
|
||||||
|
// 33=SPS
|
||||||
|
// 34=PPS
|
||||||
|
if (payload_header_type != 48 && payload_header_type != 49)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Single NAL");
|
||||||
|
single++;
|
||||||
|
|
||||||
|
//TODO - Handle DONL
|
||||||
|
|
||||||
|
nal_units.Add(rtp_payloads[payload_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregation Packet
|
||||||
|
else if (payload_header_type == 48)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Aggregation Packet");
|
||||||
|
agg++;
|
||||||
|
|
||||||
|
// RTP packet contains multiple NALs, each with a 16 bit header
|
||||||
|
// Read 16 byte size
|
||||||
|
// Read NAL
|
||||||
|
// Use a Try/Catch to protect from bad RTP data where block sizes exceed the
|
||||||
|
// available data
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int ptr = 2; // start after 16 bit Payload Header
|
||||||
|
|
||||||
|
// loop until the ptr has moved beyond the length of the data
|
||||||
|
while (ptr < (rtp_payloads[payload_index].Length - 1))
|
||||||
|
{
|
||||||
|
if (has_donl) ptr = ptr + 2; // step over the DONL data
|
||||||
|
int size = (rtp_payloads[payload_index][ptr] << 8) + (rtp_payloads[payload_index][ptr + 1] << 0);
|
||||||
|
ptr = ptr + 2;
|
||||||
|
byte[] nal = new byte[size];
|
||||||
|
System.Array.Copy(rtp_payloads[payload_index], ptr, nal, 0, size); // copy the NAL
|
||||||
|
nal_units.Add(nal); // Add to list of NALs for this RTP frame. Start Codes like 00 00 00 01 get added later
|
||||||
|
ptr = ptr + size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
Console.WriteLine("H265 Aggregate Packet processing error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fragmentation Unit
|
||||||
|
else if (payload_header_type == 49)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Fragmentation Unit");
|
||||||
|
frag++;
|
||||||
|
|
||||||
|
// Parse Fragmentation Unit Header
|
||||||
|
int fu_header_s = (rtp_payloads[payload_index][2] >> 7) & 0x01; // start marker
|
||||||
|
int fu_header_e = (rtp_payloads[payload_index][2] >> 6) & 0x01; // end marker
|
||||||
|
int fu_header_type = (rtp_payloads[payload_index][2] >> 0) & 0x3F; // fu type
|
||||||
|
|
||||||
|
Console.WriteLine("Frag FU-A s=" + fu_header_s + "e=" + fu_header_e);
|
||||||
|
|
||||||
|
// Check Start and End flags
|
||||||
|
if (fu_header_s == 1 && fu_header_e == 0)
|
||||||
|
{
|
||||||
|
// Start of Fragment.
|
||||||
|
// Initiise the fragmented_nal byte array
|
||||||
|
|
||||||
|
// Empty the stream
|
||||||
|
fragmented_nal.SetLength(0);
|
||||||
|
|
||||||
|
// Reconstrut the NAL header from the rtp_payload_header, replacing the Type with FU Type
|
||||||
|
int nal_header = (payload_header & 0x81FF); // strip out existing 'type'
|
||||||
|
nal_header = nal_header | (fu_header_type << 9);
|
||||||
|
|
||||||
|
fragmented_nal.WriteByte((byte)((nal_header >> 8) & 0xFF));
|
||||||
|
fragmented_nal.WriteByte((byte)((nal_header >> 0) & 0xFF));
|
||||||
|
|
||||||
|
if (has_donl)
|
||||||
|
{
|
||||||
|
// start copying after the DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// there is no DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fu_header_s == 0 && fu_header_e == 0)
|
||||||
|
{
|
||||||
|
// Middle part of Fragment
|
||||||
|
// Append this payload to the fragmented_nal
|
||||||
|
|
||||||
|
if (has_donl) {
|
||||||
|
// start copying after the DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
|
||||||
|
} else {
|
||||||
|
// there is no DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fu_header_s == 0 && fu_header_e == 1)
|
||||||
|
{
|
||||||
|
// End part of Fragment
|
||||||
|
// Append this payload to the fragmented_nal
|
||||||
|
if (has_donl)
|
||||||
|
{
|
||||||
|
// start copying after the DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 5, rtp_payloads[payload_index].Length - 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// there is no DONL data
|
||||||
|
fragmented_nal.Write(rtp_payloads[payload_index], 3, rtp_payloads[payload_index].Length - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the NAL to the array of NAL units
|
||||||
|
nal_units.Add(fragmented_nal.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Console.WriteLine("Unknown Payload Header Type = " + payload_header_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output some statistics
|
||||||
|
Console.WriteLine("Single=" + single + " Agg=" + agg + " Frag=" + frag);
|
||||||
|
|
||||||
|
// Output all the NALs that form one RTP Frame (one frame of video)
|
||||||
|
return nal_units;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
framework/Inspectron.HawkEye/RTSP/IRTSPTransport.cs
Normal file
41
framework/Inspectron.HawkEye/RTSP/IRTSPTransport.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
namespace Inspectron.HawkEye.RTSP
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Interface for Transport of Rtsp (TCP, TCP+SSL,..)
|
||||||
|
/// </summary>
|
||||||
|
public interface IRtspTransport
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the stream of the transport.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A stream</returns>
|
||||||
|
System.IO.Stream GetStream();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the remote address.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The remote address.</value>
|
||||||
|
string RemoteAddress
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes this instance.
|
||||||
|
/// </summary>
|
||||||
|
void Close();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this <see cref="IRtspTransport"/> is connected.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if connected; otherwise, <c>false</c>.</value>
|
||||||
|
bool Connected { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reconnect this instance.
|
||||||
|
/// <remarks>Must do nothing if already connected.</remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="System.Net.Sockets.SocketException">Error during socket </exception>
|
||||||
|
void Reconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
framework/Inspectron.HawkEye/RTSP/Messages/PortCouple.cs
Normal file
103
framework/Inspectron.HawkEye/RTSP/Messages/PortCouple.cs
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Describe a couple of port used to transfer video and command.
|
||||||
|
/// </summary>
|
||||||
|
public class PortCouple
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the first port number.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The first port.</value>
|
||||||
|
public int First { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the second port number.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If not present the value is 0</remarks>
|
||||||
|
/// <value>The second port.</value>
|
||||||
|
public int Second { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PortCouple"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public PortCouple()
|
||||||
|
{ }
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PortCouple"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="first">The first port.</param>
|
||||||
|
public PortCouple(int first)
|
||||||
|
{
|
||||||
|
First = first;
|
||||||
|
Second = 0;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PortCouple"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="first">The first port.</param>
|
||||||
|
/// <param name="second">The second port.</param>
|
||||||
|
public PortCouple(int first, int second)
|
||||||
|
{
|
||||||
|
First = first;
|
||||||
|
Second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this instance has second port.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <c>true</c> if this instance has second port; otherwise, <c>false</c>.
|
||||||
|
/// </value>
|
||||||
|
public bool IsSecondPortPresent
|
||||||
|
{
|
||||||
|
get { return Second != 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the int values of port.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stringValue">A string value.</param>
|
||||||
|
/// <returns>The port couple</returns>
|
||||||
|
public static PortCouple Parse(string stringValue)
|
||||||
|
{
|
||||||
|
if (stringValue == null)
|
||||||
|
throw new ArgumentNullException("stringValue");
|
||||||
|
Contract.Requires(!string.IsNullOrEmpty(stringValue));
|
||||||
|
|
||||||
|
string[] values = stringValue.Split('-');
|
||||||
|
|
||||||
|
int tempValue;
|
||||||
|
|
||||||
|
int.TryParse(values[0], out tempValue);
|
||||||
|
PortCouple result = new PortCouple(tempValue);
|
||||||
|
|
||||||
|
tempValue = 0;
|
||||||
|
if (values.Length > 1)
|
||||||
|
int.TryParse(values[1], out tempValue);
|
||||||
|
|
||||||
|
result.Second = tempValue;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="System.String"/> that represents this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// A <see cref="System.String"/> that represents this instance.
|
||||||
|
/// </returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (IsSecondPortPresent)
|
||||||
|
return First.ToString(CultureInfo.InvariantCulture) + "-" + Second.ToString(CultureInfo.InvariantCulture);
|
||||||
|
else
|
||||||
|
return First.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
49
framework/Inspectron.HawkEye/RTSP/Messages/RTSPChunk.cs
Normal file
49
framework/Inspectron.HawkEye/RTSP/Messages/RTSPChunk.cs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class wich represent each message echanged on Rtsp socket.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RtspChunk : ICloneable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Logs the message to debug.
|
||||||
|
/// </summary>
|
||||||
|
public void LogMessage()
|
||||||
|
{
|
||||||
|
LogMessage(NLog.LogLevel.Debug);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alevel">The log level.</param>
|
||||||
|
public abstract void LogMessage(NLog.LogLevel aLevel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the data associate with the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>Array of byte transmit with the message.</value>
|
||||||
|
public byte[] Data
|
||||||
|
{ get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the source port wich receive the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The source port.</value>
|
||||||
|
public RtspListener SourcePort { get; set; }
|
||||||
|
|
||||||
|
#region ICloneable Membres
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crée un nouvel objet qui est une copie de l'instance en cours.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Nouvel objet qui est une copie de cette instance.
|
||||||
|
/// </returns>
|
||||||
|
public abstract object Clone();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
45
framework/Inspectron.HawkEye/RTSP/Messages/RTSPData.cs
Normal file
45
framework/Inspectron.HawkEye/RTSP/Messages/RTSPData.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Message wich represent data. ($ limited message)
|
||||||
|
/// </summary>
|
||||||
|
public class RtspData : RtspChunk
|
||||||
|
{
|
||||||
|
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs the message to debug.
|
||||||
|
/// </summary>
|
||||||
|
public override void LogMessage(NLog.LogLevel aLevel)
|
||||||
|
{
|
||||||
|
// Default value to debug
|
||||||
|
if (aLevel == null)
|
||||||
|
aLevel = NLog.LogLevel.Debug;
|
||||||
|
// if the level is not logged directly return
|
||||||
|
if (!_logger.IsEnabled(aLevel))
|
||||||
|
return;
|
||||||
|
_logger.Log(aLevel, "Data message");
|
||||||
|
if (Data == null)
|
||||||
|
_logger.Log(aLevel, "Data : null");
|
||||||
|
else
|
||||||
|
_logger.Log(aLevel, "Data length :-{0}-", Data.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Channel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clones this instance.
|
||||||
|
/// <remarks>Listner is not cloned</remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>a clone of this instance</returns>
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
RtspData result = new RtspData();
|
||||||
|
result.Channel = this.Channel;
|
||||||
|
if (this.Data != null)
|
||||||
|
result.Data = this.Data.Clone() as byte[];
|
||||||
|
result.SourcePort = this.SourcePort;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class containing helper constant for general use headers.
|
||||||
|
/// </summary>
|
||||||
|
public static class RtspHeaderNames
|
||||||
|
{
|
||||||
|
public const string ContentBase = "Content-Base";
|
||||||
|
public const string ContentEncoding = "Content-Encoding";
|
||||||
|
public const string ContentType = "Content-Type";
|
||||||
|
|
||||||
|
public const string Public = "Public";
|
||||||
|
public const string Session = "Session";
|
||||||
|
public const string Transport = "Transport";
|
||||||
|
|
||||||
|
public const string WWWAuthenticate = "WWW-Authenticate";
|
||||||
|
public const string Authorization = "Authorization";
|
||||||
|
}
|
||||||
|
}
|
||||||
309
framework/Inspectron.HawkEye/RTSP/Messages/RTSPMessage.cs
Normal file
309
framework/Inspectron.HawkEye/RTSP/Messages/RTSPMessage.cs
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
public class RtspMessage : RtspChunk
|
||||||
|
{
|
||||||
|
private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The regex to validate the Rtsp message.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Regex _rtspVersionTest = new Regex(@"^RTSP/\d\.\d", RegexOptions.Compiled);
|
||||||
|
/// <summary>
|
||||||
|
/// Create the good type of Rtsp Message from the header.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aRequestLine">A request line.</param>
|
||||||
|
/// <returns>An Rtsp message</returns>
|
||||||
|
public static RtspMessage GetRtspMessage(string aRequestLine)
|
||||||
|
{
|
||||||
|
// We can't determine the message
|
||||||
|
if (string.IsNullOrEmpty(aRequestLine))
|
||||||
|
return new RtspMessage();
|
||||||
|
string[] requestParts = aRequestLine.Split(new char[] { ' ' }, 3);
|
||||||
|
RtspMessage returnValue;
|
||||||
|
if (requestParts.Length == 3)
|
||||||
|
{
|
||||||
|
// A request is : Method SP Request-URI SP RTSP-Version
|
||||||
|
// A response is : RTSP-Version SP Status-Code SP Reason-Phrase
|
||||||
|
// RTSP-Version = "RTSP" "/" 1*DIGIT "." 1*DIGIT
|
||||||
|
if (_rtspVersionTest.IsMatch(requestParts[2]))
|
||||||
|
returnValue = RtspRequest.GetRtspRequest(requestParts);
|
||||||
|
else if (_rtspVersionTest.IsMatch(requestParts[0]))
|
||||||
|
returnValue = new RtspResponse();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
|
||||||
|
returnValue = new RtspMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Warn(CultureInfo.InvariantCulture, "Got a strange message {0}", aRequestLine);
|
||||||
|
returnValue = new RtspMessage();
|
||||||
|
}
|
||||||
|
returnValue.Command = aRequestLine;
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RtspMessage"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public RtspMessage()
|
||||||
|
{
|
||||||
|
Data = new byte[0];
|
||||||
|
Creation = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string, string> _headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
internal protected string[] commandArray;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the creation time.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The creation time.</value>
|
||||||
|
public DateTime Creation { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the command of the message (first line).
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The command.</value>
|
||||||
|
public string Command
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (commandArray == null)
|
||||||
|
return string.Empty;
|
||||||
|
return string.Join(" ", commandArray);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
commandArray = new string[] { String.Empty };
|
||||||
|
else
|
||||||
|
commandArray = value.Split(new char[] {' '}, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Method of the message (eg OPTIONS, DESCRIBE, SETUP, PLAY).
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The Method</value>
|
||||||
|
public string Method
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (commandArray == null)
|
||||||
|
return string.Empty;
|
||||||
|
return commandArray[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the headers of the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The headers.</value>
|
||||||
|
public Dictionary<string, string> Headers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds one header from a string.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line">The string containing header of format Header: Value.</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="line"/> is null</exception>
|
||||||
|
public void AddHeader(string line)
|
||||||
|
{
|
||||||
|
if (line == (string)null)
|
||||||
|
throw new ArgumentNullException("line");
|
||||||
|
|
||||||
|
//spliter
|
||||||
|
string[] elements = line.Split(new char[] { ':' }, 2);
|
||||||
|
if (elements.Length == 2)
|
||||||
|
{
|
||||||
|
_headers[elements[0].Trim()] = elements[1].TrimStart();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Warn(CultureInfo.InvariantCulture, "Invalid Header received : -{0}-", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Ccommande Seqquence number.
|
||||||
|
/// <remarks>If the header is not define or not a valid number it return 0</remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The sequence number.</value>
|
||||||
|
public int CSeq
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string returnStringValue;
|
||||||
|
int returnValue;
|
||||||
|
if (!(_headers.TryGetValue("CSeq", out returnStringValue) &&
|
||||||
|
int.TryParse(returnStringValue, out returnValue)))
|
||||||
|
returnValue = 0;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_headers["CSeq"] = value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the session ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The session ID.</value>
|
||||||
|
public virtual string Session
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_headers.ContainsKey("Session"))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return _headers["Session"];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_headers["Session"] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialises the length of the data byte array from content lenth header.
|
||||||
|
/// </summary>
|
||||||
|
public void InitialiseDataFromContentLength()
|
||||||
|
{
|
||||||
|
int dataLength;
|
||||||
|
if (!(_headers.ContainsKey("Content-Length")
|
||||||
|
&& int.TryParse(_headers["Content-Length"], out dataLength)))
|
||||||
|
{
|
||||||
|
dataLength = 0;
|
||||||
|
}
|
||||||
|
this.Data = new byte[dataLength];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adjusts the content length header.
|
||||||
|
/// </summary>
|
||||||
|
public void AdjustContentLength()
|
||||||
|
{
|
||||||
|
if (Data.Length > 0)
|
||||||
|
{
|
||||||
|
_headers["Content-Length"] = Data.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_headers.Remove("Content-Length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends to the message to a stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The stream.</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="stream"/> is empty</exception>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="stream"/> can't be written.</exception>
|
||||||
|
public void SendTo(Stream stream)
|
||||||
|
{
|
||||||
|
// <pex>
|
||||||
|
if (stream == null)
|
||||||
|
throw new ArgumentNullException("stream");
|
||||||
|
if (!stream.CanWrite)
|
||||||
|
throw
|
||||||
|
new ArgumentException("Stream CanWrite == false, can't send message to it", "stream");
|
||||||
|
// </pex>
|
||||||
|
Contract.EndContractBlock();
|
||||||
|
|
||||||
|
Encoding encoder = ASCIIEncoding.UTF8;
|
||||||
|
StringBuilder outputString = new StringBuilder();
|
||||||
|
|
||||||
|
AdjustContentLength();
|
||||||
|
|
||||||
|
// output header
|
||||||
|
outputString.Append(Command);
|
||||||
|
outputString.Append("\r\n");
|
||||||
|
foreach (KeyValuePair<string, string> item in _headers)
|
||||||
|
{
|
||||||
|
outputString.AppendFormat("{0}: {1}\r\n", item.Key, item.Value);
|
||||||
|
}
|
||||||
|
outputString.Append("\r\n");
|
||||||
|
byte[] buffer = encoder.GetBytes(outputString.ToString());
|
||||||
|
lock(stream) {
|
||||||
|
stream.Write(buffer, 0, buffer.Length);
|
||||||
|
|
||||||
|
// Output data
|
||||||
|
if (Data.Length > 0)
|
||||||
|
stream.Write(Data, 0, Data.Length);
|
||||||
|
|
||||||
|
}
|
||||||
|
stream.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs the message.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aLevel">A log level.</param>
|
||||||
|
public override void LogMessage(NLog.LogLevel aLevel)
|
||||||
|
{
|
||||||
|
// Default value to debug
|
||||||
|
if (aLevel == null)
|
||||||
|
aLevel = NLog.LogLevel.Debug;
|
||||||
|
// if the level is not logged directly return
|
||||||
|
if (!_logger.IsEnabled(aLevel))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_logger.Log(aLevel, "Commande : {0}", Command);
|
||||||
|
foreach (KeyValuePair<string, string> item in _headers)
|
||||||
|
{
|
||||||
|
_logger.Log(aLevel, "Header : {0}: {1}", item.Key, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Data.Length > 0)
|
||||||
|
{
|
||||||
|
_logger.Log(aLevel, "Data :-{0}-", ASCIIEncoding.ASCII.GetString(Data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Crée un nouvel objet qui est une copie de l'instance en cours.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// Nouvel objet qui est une copie de cette instance.
|
||||||
|
/// </returns>
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
RtspMessage returnValue = GetRtspMessage(this.Command);
|
||||||
|
|
||||||
|
foreach (var item in this.Headers)
|
||||||
|
{
|
||||||
|
if (item.Value == null)
|
||||||
|
returnValue.Headers.Add(item.Key.Clone() as string, null);
|
||||||
|
else
|
||||||
|
returnValue.Headers.Add(item.Key.Clone() as string, item.Value.Clone() as string);
|
||||||
|
}
|
||||||
|
returnValue.Data = this.Data.Clone() as byte[];
|
||||||
|
returnValue.SourcePort = this.SourcePort;
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
191
framework/Inspectron.HawkEye/RTSP/Messages/RTSPRequest.cs
Normal file
191
framework/Inspectron.HawkEye/RTSP/Messages/RTSPRequest.cs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Inspectron.HawkEye.RTSP.Messages
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An Rtsp Request
|
||||||
|
/// </summary>
|
||||||
|
public class RtspRequest : RtspMessage
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request type.
|
||||||
|
/// </summary>
|
||||||
|
public enum RequestType
|
||||||
|
{
|
||||||
|
UNKNOWN,
|
||||||
|
DESCRIBE,
|
||||||
|
ANNOUNCE,
|
||||||
|
GET_PARAMETER,
|
||||||
|
OPTIONS,
|
||||||
|
PAUSE,
|
||||||
|
PLAY,
|
||||||
|
RECORD,
|
||||||
|
REDIRECT,
|
||||||
|
SETUP,
|
||||||
|
SET_PARAMETER,
|
||||||
|
TEARDOWN,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parses the request command.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aStringRequest">A string request command.</param>
|
||||||
|
/// <returns>The typed request.</returns>
|
||||||
|
internal static RequestType ParseRequest(string aStringRequest)
|
||||||
|
{
|
||||||
|
RequestType returnValue;
|
||||||
|
if (!Enum.TryParse<RequestType>(aStringRequest, true, out returnValue))
|
||||||
|
returnValue = RequestType.UNKNOWN;
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Rtsp request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="aRequestParts">A request parts.</param>
|
||||||
|
/// <returns>the parsed request</returns>
|
||||||
|
internal static RtspMessage GetRtspRequest(string[] aRequestParts)
|
||||||
|
{
|
||||||
|
// <pex>
|
||||||
|
Debug.Assert(aRequestParts != (string[])null, "aRequestParts");
|
||||||
|
Debug.Assert(aRequestParts.Length != 0, "aRequestParts.Length == 0");
|
||||||
|
// </pex>
|
||||||
|
// we already know this is a Request
|
||||||
|
RtspRequest returnValue;
|
||||||
|
switch (ParseRequest(aRequestParts[0]))
|
||||||
|
{
|
||||||
|
case RequestType.OPTIONS:
|
||||||
|
returnValue = new RtspRequestOptions();
|
||||||
|
break;
|
||||||
|
case RequestType.DESCRIBE:
|
||||||
|
returnValue = new RtspRequestDescribe();
|
||||||
|
break;
|
||||||
|
case RequestType.SETUP:
|
||||||
|
returnValue = new RtspRequestSetup();
|
||||||
|
break;
|
||||||
|
case RequestType.PLAY:
|
||||||
|
returnValue = new RtspRequestPlay();
|
||||||
|
break;
|
||||||
|
case RequestType.PAUSE:
|
||||||
|
returnValue = new RtspRequestPause();
|
||||||
|
break;
|
||||||
|
case RequestType.TEARDOWN:
|
||||||
|
returnValue = new RtspRequestTeardown();
|
||||||
|
break;
|
||||||
|
case RequestType.GET_PARAMETER:
|
||||||
|
returnValue = new RtspRequestGetParameter();
|
||||||
|
break;
|
||||||
|
case RequestType.ANNOUNCE:
|
||||||
|
returnValue = new RtspRequestAnnounce();
|
||||||
|
break;
|
||||||
|
case RequestType.RECORD:
|
||||||
|
returnValue = new RtspRequestRecord();
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
case RequestType.REDIRECT:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RequestType.SET_PARAMETER:
|
||||||
|
break;
|
||||||
|
*/
|
||||||
|
case RequestType.UNKNOWN:
|
||||||
|
default:
|
||||||
|
returnValue = new RtspRequest();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RtspRequest"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public RtspRequest()
|
||||||
|
{
|
||||||
|
Command = "OPTIONS * RTSP/1.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the request.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The request in string format.</value>
|
||||||
|
public string Request
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return commandArray[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the request.
|
||||||
|
/// <remarks>The return value is typed with <see cref="Rtsp.RequestType"/> if the value is not
|
||||||
|
/// reconise the value is sent. The string value can be get by <see cref="Request"/></remarks>
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The request.</value>
|
||||||
|
public RequestType RequestTyped
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ParseRequest(commandArray[0]);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Enum.IsDefined(typeof(RequestType), value))
|
||||||
|
commandArray[0] = value.ToString();
|
||||||
|
else
|
||||||
|
commandArray[0] = RequestType.UNKNOWN.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri _RtspUri;
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Rtsp asked URI.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The Rtsp asked URI.</value>
|
||||||
|
/// <remarks>The request with uri * is return with null URI</remarks>
|
||||||
|
public Uri RtspUri
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (commandArray.Length < 2 || commandArray[1]=="*")
|
||||||
|
return null;
|
||||||
|
if (_RtspUri == null)
|
||||||
|
Uri.TryCreate(commandArray[1], UriKind.Absolute, out _RtspUri);
|
||||||
|
return _RtspUri;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_RtspUri = value;
|
||||||
|
if (commandArray.Length < 2)
|
||||||
|
{
|
||||||
|
Array.Resize(ref commandArray, 3);
|
||||||
|
}
|
||||||
|
commandArray[1] = (value != null ? value.ToString().TrimEnd('/') : "*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the assiociate OK response with the request.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>an Rtsp response correcponding to request.</returns>
|
||||||
|
public virtual RtspResponse CreateResponse()
|
||||||
|
{
|
||||||
|
RtspResponse returnValue = new RtspResponse();
|
||||||
|
returnValue.ReturnCode = 200;
|
||||||
|
returnValue.CSeq = this.CSeq;
|
||||||
|
if (this.Headers.ContainsKey(RtspHeaderNames.Session))
|
||||||
|
{
|
||||||
|
returnValue.Headers[RtspHeaderNames.Session] = this.Headers[RtspHeaderNames.Session];
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object ContextData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user