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;
|
||||
|
||||
|
||||
|
||||
public IDSImageSource(IDSImageSourceSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
@@ -68,8 +67,7 @@ namespace Inspectron.Camera.UEye
|
||||
|
||||
Byte[] 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.SetArray(u8img);
|
||||
return mat;
|
||||
@@ -82,11 +80,11 @@ namespace Inspectron.Camera.UEye
|
||||
|
||||
uEye.Defines.Status statusRet = 0;
|
||||
|
||||
// Open _camera
|
||||
// Open camera
|
||||
statusRet = _camera.Init(cameraIdx);
|
||||
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();
|
||||
_camera.Information.GetSensorInfo(out info);
|
||||
@@ -101,7 +99,6 @@ namespace Inspectron.Camera.UEye
|
||||
if (statusRet != uEye.Defines.Status.Success)
|
||||
{
|
||||
throw new Exception("Allocate Memory failed");
|
||||
|
||||
}
|
||||
_camera.EventFrame += onFrameEvent;
|
||||
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");
|
||||
}
|
||||
|
||||
[NotForTool]
|
||||
public bool Result { get; set; } = true;
|
||||
[NotForTool]
|
||||
public string ResultString
|
||||
{
|
||||
get
|
||||
@@ -104,7 +105,12 @@ namespace Hawkeye.VisionBuilder.Workflow
|
||||
[NotForTool]
|
||||
public string Label { get; set; }
|
||||
|
||||
[NotForTool]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[NotForTool]
|
||||
public TimeSpan ExecutionTime { get; set; }
|
||||
[NotForTool]
|
||||
public string ExecutionTimeString
|
||||
{
|
||||
get
|
||||
@@ -132,7 +138,7 @@ namespace Hawkeye.VisionBuilder.Workflow
|
||||
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)));
|
||||
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)
|
||||
{
|
||||
this.GetType().GetProperty(pair.Key,
|
||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
||||
BindingFlags.Public | BindingFlags.Instance )
|
||||
?.SetValue(this,pair.Value);
|
||||
}
|
||||
}
|
||||
@@ -275,6 +281,23 @@ namespace Hawkeye.VisionBuilder.Workflow
|
||||
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)
|
||||
{
|
||||
Status=eMessage;
|
||||
|
||||
@@ -9,6 +9,7 @@ public class BaseOperationDecorator
|
||||
_baseOperation = baseOperation;
|
||||
}
|
||||
|
||||
|
||||
public bool Result => Operation.Result;
|
||||
public string ResultString => Operation.ToString();
|
||||
public string TypeName => Operation.TypeName;
|
||||
@@ -21,4 +22,10 @@ public class BaseOperationDecorator
|
||||
public string ExecutionTimeString => Operation.ExecutionTimeString;
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
@@ -30,4 +30,20 @@ public class Color128BinaryOperation:ColorAIOperation
|
||||
FilterClasses = 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();
|
||||
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();
|
||||
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));
|
||||
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();
|
||||
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();
|
||||
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);
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (path.StartsWith(".."))
|
||||
|
||||
@@ -94,4 +94,35 @@ public class GaborFilterOperation : BaseOperation
|
||||
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);
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
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();
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -124,6 +124,48 @@ public class FindEdgeOperation : BaseOperation, IHaveEdge, IHaveOrigin
|
||||
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 OriginElement Origin { get; set; }
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.ComponentModel.Design.Serialization;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Reflection.PortableExecutable;
|
||||
using System.Text.Json;
|
||||
using System.Xml;
|
||||
using VisionBuilder.UI.Common.RecipeProcessing;
|
||||
using Size = OpenCvSharp.Size;
|
||||
@@ -32,6 +33,11 @@ public class WorkflowList
|
||||
long total = 0;
|
||||
foreach (BaseOperation operation in Operations)
|
||||
{
|
||||
if (!operation.Enabled)
|
||||
{
|
||||
operation.Result=true;
|
||||
continue;
|
||||
}
|
||||
if (Context.CancellationToken.IsCancellationRequested) break;
|
||||
operation.Interpret(Context);
|
||||
}
|
||||
@@ -62,6 +68,7 @@ public class WorkflowList
|
||||
UpdateConfigVariables();
|
||||
foreach (BaseOperation operation in Operations)
|
||||
{
|
||||
if(!operation.Enabled)continue;
|
||||
if(operation == op) break;
|
||||
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 });
|
||||
_allFiles.Add(filePath);
|
||||
UpdateBranchText(_goodNode, "Good");
|
||||
treeView.ExpandAll();
|
||||
//treeView.ExpandAll();
|
||||
}
|
||||
|
||||
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 });
|
||||
_allFiles.Add(filePath);
|
||||
UpdateBranchText(_badNode, "Bad");
|
||||
treeView.ExpandAll();
|
||||
//treeView.ExpandAll();
|
||||
}
|
||||
|
||||
private void UpdateBranchText(TreeNode node, string name)
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
btnTest = new Button();
|
||||
button1 = new Button();
|
||||
Indicator = new DataGridViewTextBoxColumn();
|
||||
Link = new DataGridViewTextBoxColumn();
|
||||
Enabled = new DataGridViewCheckBoxColumn();
|
||||
UserName = new DataGridViewTextBoxColumn();
|
||||
Result = new DataGridViewTextBoxColumn();
|
||||
TypeName = new DataGridViewTextBoxColumn();
|
||||
@@ -50,14 +50,13 @@
|
||||
dataGridView1.AllowUserToAddRows = false;
|
||||
dataGridView1.AllowUserToDeleteRows = false;
|
||||
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.Location = new Point(0, 40);
|
||||
dataGridView1.MultiSelect = false;
|
||||
dataGridView1.Name = "dataGridView1";
|
||||
dataGridView1.ReadOnly = true;
|
||||
dataGridView1.RowHeadersVisible = false;
|
||||
dataGridView1.RowTemplate.Height = 25;
|
||||
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||
dataGridView1.Size = new Size(540, 562);
|
||||
dataGridView1.TabIndex = 0;
|
||||
@@ -126,12 +125,12 @@
|
||||
Indicator.ReadOnly = true;
|
||||
Indicator.Width = 32;
|
||||
//
|
||||
// Link
|
||||
// Enabled
|
||||
//
|
||||
Link.HeaderText = "";
|
||||
Link.Name = "Link";
|
||||
Link.ReadOnly = true;
|
||||
Link.Width = 32;
|
||||
Enabled.HeaderText = "";
|
||||
Enabled.Name = "Enabled";
|
||||
Enabled.ReadOnly = true;
|
||||
Enabled.Width = 32;
|
||||
//
|
||||
// UserName
|
||||
//
|
||||
@@ -182,7 +181,7 @@
|
||||
private Label lblProcessingTime;
|
||||
private Label label1;
|
||||
private DataGridViewTextBoxColumn Indicator;
|
||||
private DataGridViewTextBoxColumn Link;
|
||||
private DataGridViewCheckBoxColumn Enabled;
|
||||
private DataGridViewTextBoxColumn UserName;
|
||||
private DataGridViewTextBoxColumn Result;
|
||||
private DataGridViewTextBoxColumn TypeName;
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
||||
|
||||
dataGridView1.DataSource = _bindingSource;
|
||||
dataGridView1.Columns[0].DataPropertyName = nameof(BaseOperationDecorator.Result);
|
||||
dataGridView1.Columns[1].DataPropertyName = nameof(BaseOperationDecorator.IsEnabled);
|
||||
dataGridView1.Columns[2].DataPropertyName = nameof(BaseOperationDecorator.Label);
|
||||
dataGridView1.Columns[2].ReadOnly = false;
|
||||
dataGridView1.ReadOnly = false;
|
||||
@@ -88,29 +89,6 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
||||
|
||||
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,13 +97,19 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
||||
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 cy = e.CellBounds.Height / 2 + e.CellBounds.Top;
|
||||
var radius = e.CellBounds.Height / 2 - 3;
|
||||
var c1 = dataGridView1.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, true);
|
||||
e.PaintBackground(c1, true);
|
||||
|
||||
|
||||
|
||||
if ((bool) e.Value)
|
||||
{
|
||||
@@ -135,9 +119,11 @@ namespace Hawkeye.VisionBuilder.Features.VisionSteps
|
||||
{
|
||||
e.Graphics.FillEllipse(Brushes.Red, cx - radius, cy - radius, radius * 2, radius * 2);
|
||||
}
|
||||
|
||||
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:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<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">
|
||||
<value>True</value>
|
||||
</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>
|
||||
</metadata>
|
||||
<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)
|
||||
{
|
||||
OpenFileDialog dialog = new OpenFileDialog();
|
||||
dialog.Filter = "*.hrcp|*.hrcp";
|
||||
dialog.Filter = "Suported types|*.jhrcp;*.hrcp";
|
||||
PrepareDialog(dialog);
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
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;
|
||||
|
||||
|
||||
public void LoadRecipe(string path)
|
||||
public void LoadRecipeBinary(string path)
|
||||
{
|
||||
_loadedPath = path;
|
||||
var bytes = File.ReadAllBytes(path);
|
||||
@@ -218,13 +231,23 @@ namespace Hawkeye.VisionBuilder
|
||||
BinaryReader reader = new BinaryReader(stream);
|
||||
|
||||
|
||||
_workflowList.PythonMissing += _workflowList_PythonMissing;
|
||||
|
||||
_workflowList.Load(reader, _discoveryService);
|
||||
ReinitCameraPanel(_workflowList);
|
||||
OrganizeWindows(null, null);
|
||||
_visionStepsPanel.RefreshWorkflow();
|
||||
}
|
||||
|
||||
public void LoadRecipeJSON(string path)
|
||||
{
|
||||
_loadedPath = path;
|
||||
_workflowList.LoadJSON(path,_discoveryService);
|
||||
ReinitCameraPanel(_workflowList);
|
||||
OrganizeWindows(null, null);
|
||||
_visionStepsPanel.RefreshWorkflow();
|
||||
|
||||
}
|
||||
|
||||
private void _workflowList_PythonMissing()
|
||||
{
|
||||
SetupConfig();
|
||||
@@ -234,9 +257,23 @@ namespace Hawkeye.VisionBuilder
|
||||
private void saveRecipeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveFileDialog dialog = new SaveFileDialog();
|
||||
dialog.Filter = "*.hrcp|*.hrcp";
|
||||
dialog.Filter = "*.jhrcp|*.jhrcp|*.hrcp|*.hrcp";
|
||||
PrepareDialog(dialog);
|
||||
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();
|
||||
BinaryWriter bw = new BinaryWriter(ms);
|
||||
@@ -244,8 +281,7 @@ namespace Hawkeye.VisionBuilder
|
||||
_workflowList.RecipeImage = _workflowList.Context.LastCameraImage.ImageData;
|
||||
_workflowList.Save(bw);
|
||||
ms.Close();
|
||||
File.WriteAllBytes(dialog.FileName, ms.ToArray());
|
||||
}
|
||||
File.WriteAllBytes(path, ms.ToArray());
|
||||
}
|
||||
|
||||
private void toolBtnConfiguration_Click(object sender, EventArgs e)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Hawkeye.VisionBuilder.UI.Sources.Emulation;
|
||||
using Hawkeye.VisionBuilder.UI.Sources.Hawkeye;
|
||||
using Inspectron.Camera.UEye;
|
||||
using Ninject;
|
||||
using Ninject.Extensions.ChildKernel;
|
||||
@@ -26,6 +27,7 @@ public static class ModuleExtensions
|
||||
kernel.Bind<IImageSource, IVisionBuilderModule>().To<EmulationCameraImageSource>().InSingletonScope();
|
||||
break;
|
||||
case CameraSettings.EImageSource.Hawkeye:
|
||||
kernel.Bind<IImageSource, IVisionBuilderModule>().To<HawkeyeCameraImageSource>().InSingletonScope();
|
||||
break;
|
||||
case CameraSettings.EImageSource.IDS:
|
||||
kernel.Bind<IImageSource, IVisionBuilderModule>().To<IDSImageSource>().InSingletonScope();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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="..\VisionBuilder.UI.Common\VisionBuilder.UI.Common.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -75,6 +75,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "B24SiemensEmulator", "Plugi
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestPlugin", "Plugins\TestPlugin\TestPlugin.csproj", "{F7D39916-489A-3583-09A0-175AE82D08B7}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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}.Release|Any CPU.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{B62D435C-3572-4B5B-A381-992345D343B0} = {F3414823-B70E-435E-B4EA-80ABF4371449}
|
||||
{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}
|
||||
{E8868ABD-E4D0-1B7E-494E-06FB1F7D1AF5} = {2B8C63F7-B7FD-464C-9045-180ED8F595F1}
|
||||
{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}
|
||||
{52D6A9AE-D0F1-4C52-B688-E0219169E179} = {583A77DF-A293-4F3E-AB96-7310BC495822}
|
||||
{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
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
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