Files
2025-08-28 16:31:25 +02:00

309 lines
9.8 KiB
C#

using Hawkeye.VisionBuilder.Workflow.Datatypes;
using Hawkeye.VisionBuilder.Workflow.Links;
using Hawkeye.VisionBuilder.Workflow.Operations.Attributes;
using Microsoft.Scripting.Hosting;
using Ninject.Infrastructure.Language;
using OpenCvSharp;
using System.Diagnostics;
using System.Reflection;
using System.Runtime;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using Serilog;
using RectangleElement = Hawkeye.VisionBuilder.Workflow.Datatypes.Elements.Rectangle.RectangleElement;
namespace Hawkeye.VisionBuilder.Workflow
{
public abstract class BaseOperation
{
private static readonly ScriptScope _scope;
private static readonly ScriptEngine _engine;
static BaseOperation()
{
_engine = IronPython.Hosting.Python.CreateEngine();
_scope = _engine.CreateScope();
}
protected BaseOperation()
{
TypeName = MakeOperationName(this.GetType().Name);
Label = MakeOperationName(this.GetType().Name,false);
}
public static void SetScriptValue<T>(string name, T value)
{
_scope.SetVariable(name,value);
}
public static T GetScriptValue<T>(ScriptValue scriptValue)
{
return _engine.Execute<T>(scriptValue.Script,_scope);
}
[NotForTool]
public bool CanHaveProcessingError { get; set; } = true;
[NotForTool]
public string Status { get; set; }
protected bool CheckImageExists(Context context)
{
if (context.ActiveImage == null)
{
Status = "No image available";
Result = false;
return false;
}
return true;
}
protected bool CheckColorful(Context context)
{
if (context.ActiveImage.ImageData.Type() != MatType.CV_8UC3)
{
Status="Image is not colorful";
Result = false;
return false;
}
return true;
}
protected bool CheckGrayscale(Context context)
{
if (context.ActiveImage.ImageData.Type() != MatType.CV_8UC1)
{
Status="Image is not grayscale";
Result = false;
return false;
}
return true;
}
[NotForTool]
public bool NeedsUpdate { get; set; } = true;
[NotForTool]
public Guid Id { get; protected set; }= Guid.NewGuid();
public static string MakeOperationName(string typeName,bool useSpaces=true)
{
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
{
return this.ToString();
}
}
[NotForTool]
public string TypeName { get; private set; }
[NotForTool]
public string Label { get; set; }
[NotForTool]
public bool Enabled { get; set; } = true;
[NotForTool]
public TimeSpan ExecutionTime { get; set; }
[NotForTool]
public string ExecutionTimeString
{
get
{
return ((int)ExecutionTime.TotalMilliseconds) + " ms";
}
}
public void Interpret(Context context)
{
Result = true;
Status = "";
Stopwatch sw = Stopwatch.StartNew();
InterpretInternal(context);
sw.Stop();
ExecutionTime = sw.Elapsed;
_scope.SetVariable(Label,this);
}
protected abstract void InterpretInternal(Context context);
public Dictionary<string, object> GetParameters()
{
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)!);
}
public virtual void SetParameters(Dictionary<string, object> parameters)
{
foreach (KeyValuePair<string, object> pair in parameters)
{
this.GetType().GetProperty(pair.Key,
BindingFlags.Public | BindingFlags.Instance )
?.SetValue(this,pair.Value);
}
}
public virtual void SaveXML(XmlWriter writer)
{
writer.WriteAttributeString("Id", Id.ToString());
writer.WriteAttributeString("Label", Label);
// Save all public properties that are not marked with NotForTool
var properties = GetParameters();
foreach (var property in properties)
{
if (property.Value != null)
{
writer.WriteStartElement("Property");
writer.WriteAttributeString("Name", property.Key);
writer.WriteAttributeString("Type", property.Value.GetType().FullName);
// Handle different types appropriately
if (property.Value is string || property.Value.GetType().IsPrimitive || property.Value.GetType().IsEnum)
{
writer.WriteString(property.Value.ToString());
}
else if (property.Value is Guid guid)
{
writer.WriteString(guid.ToString());
}
else
{
// For complex types, convert to string representation
writer.WriteString(property.Value.ToString());
}
writer.WriteEndElement();
}
}
}
public virtual void LoadXML(XmlReader reader)
{
if (reader.GetAttribute("Id") != null)
Id = new Guid(reader.GetAttribute("Id"));
if (reader.GetAttribute("Label") != null)
Label = reader.GetAttribute("Label");
var properties = new Dictionary<string, object>();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Property")
{
string propertyName = reader.GetAttribute("Name");
string propertyType = reader.GetAttribute("Type");
string propertyValue = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(propertyName) && !string.IsNullOrEmpty(propertyValue))
{
// Convert string value back to appropriate type
object convertedValue = ConvertFromString(propertyValue, propertyType);
if (convertedValue != null)
{
properties[propertyName] = convertedValue;
}
}
}
else if (reader.NodeType == XmlNodeType.EndElement && reader.Name == GetOperationElementName())
{
break;
}
}
if (properties.Count > 0)
{
SetParameters(properties);
}
}
private string GetOperationElementName()
{
return this.GetType().Name;
}
private object ConvertFromString(string value, string typeName)
{
try
{
var type = Type.GetType(typeName);
if (type == null) return value;
if (type == typeof(string))
return value;
else if (type == typeof(int))
return int.Parse(value);
else if (type == typeof(double))
return double.Parse(value);
else if (type == typeof(float))
return float.Parse(value);
else if (type == typeof(bool))
return bool.Parse(value);
else if (type == typeof(Guid))
return new Guid(value);
else if (type.IsEnum)
return Enum.Parse(type, value);
else
return value; // Fallback to string
}
catch
{
return value; // Fallback to string if conversion fails
}
}
public override string ToString()
{
return Status;
}
public virtual void Save(BinaryWriter bw)
{
bw.Write(Id.ToString());
bw.Write(Label);
}
public virtual void Load(BinaryReader br)
{
Id = new Guid(br.ReadString());
Label = br.ReadString();
}
public virtual void Save(Dictionary<string, object> dict)
{
dict[nameof(Id)] = Id.ToString();
dict[nameof(Label)] = Label;
dict[nameof(Enabled)] = Enabled;
}
public virtual void Load(Dictionary<string, object> dict)
{
if (dict.ContainsKey(nameof(Id)))
Id = new Guid(dict[nameof(Id)].ToString());
if (dict.ContainsKey(nameof(Label)))
Label = dict[nameof(Label)].ToString();
if (dict.ContainsKey(nameof(Enabled)))
Enabled = Convert.ToBoolean(dict[nameof(Enabled)]);
}
public void SetError(string eMessage)
{
Log.Information($"Processing error on {Label}:{eMessage}");
Status=eMessage;
Result = false;
}
}
}