using Microsoft.CodeAnalysis;
|
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.Emit;
|
using Newtonsoft.Json.Linq;
|
using System;
|
using System.Collections.Generic;
|
using System.Diagnostics;
|
using System.Linq;
|
using System.Reflection;
|
using System.Text;
|
using System.Threading.Tasks;
|
|
namespace LB_VisionProcesses.Processes.ScriptTool
|
{
|
[Process("脚本工具", Category = "其他工具", Description = "创建脚本工具")]
|
public class ScriptTool : BaseProcess
|
{
|
MemoryStream ms = new MemoryStream();
|
|
Type programType = null;
|
|
// 定义带ref参数的委托(必须完全匹配签名)
|
delegate bool CustomDelegate(ref Dictionary<string, object> dicInputs, ref Dictionary<string, object> dicOutputs, ref string Msg);
|
|
// 处理带两个ref参数的方法签名:bool Method(ref int a, ref string b)
|
CustomDelegate delegateCall = null;
|
|
// 获取方法并创建委托
|
MethodInfo method = null;
|
|
Assembly assembly = null;
|
|
// 需要先创建UserScript实例
|
object instance = null;
|
|
public ScriptTool()
|
{
|
strProcessClass = "LB_VisionProcesses.Processes.ScriptTool";
|
strProcessName = "脚本工具";
|
|
Params.Inputs.Add("Scrip", $"/*\nTime: {DateTime.Now.ToLocalTime()}\nUser: {Environment.UserName}\nLogs: \n*/\n" +
|
"public partial class UserScript\n{\n " +
|
"bool ScripCode(ref Dictionary<string, object> inputs, ref Dictionary<string, object> outputs, ref string msg)\n " +
|
"{\n Init(ref inputs, ref outputs);\n" +
|
" // TODO: Add Script Here!\n" +
|
" // public object GetInputs(string key);\n" +
|
" // public object GetGlobalVars(string key);\n" +
|
" // public void AddOutputs(string key, object value);\n" +
|
" // public void AddGlobalVars(string key, object value);\n \n" +
|
" return true;\n }\n}");
|
|
Params.Inputs.Add("OtherDllPaths", new List<string>());
|
}
|
|
public override bool Run()
|
{
|
try
|
{
|
InitRunParams();
|
|
// 通过反射,调用实例
|
if (ms == null)
|
{
|
Msg = "脚本未编译";
|
Result = false;
|
return Result;
|
}
|
|
if (method == null)
|
{
|
Msg = "方法[ScripCode]不存在";
|
Result = false;
|
return Result;
|
}
|
|
Dictionary<string, object> dicInputs = GetInputs();
|
|
Dictionary<string, object> dicOutputs = null;
|
|
try
|
{
|
dicOutputs = Params.Outputs.GetAllByName();
|
if (dicOutputs == null)
|
dicOutputs = new Dictionary<string, object>();
|
}
|
catch { dicOutputs = new Dictionary<string, object>(); }
|
|
// 初始化实例,传递输入输出字典和全局字典
|
InitializeInstance(dicInputs, dicOutputs);
|
|
// 创建委托
|
delegateCall = (CustomDelegate)Delegate.CreateDelegate(
|
typeof(CustomDelegate),
|
instance, // 实例将在调用时传递
|
method);
|
|
bool result = delegateCall(ref dicInputs, ref dicOutputs, ref Msg);
|
|
foreach (var kvp in dicInputs)
|
Params.Inputs.Add(kvp.Key, kvp.Value);
|
|
foreach (var kvp in dicOutputs)
|
Params.Outputs.Add(kvp.Key, kvp.Value);
|
|
Result = result;
|
}
|
catch (Exception ex)
|
{
|
Msg = $"脚本异常,原因是:{ex.Message}";
|
Result = false;
|
}
|
return Result;
|
}
|
|
/// <summary>
|
/// 获取脚本输入
|
/// </summary>
|
/// <returns></returns>
|
public Dictionary<string, object> GetInputs()
|
{
|
try
|
{
|
Dictionary<string, object> dicInputs = new Dictionary<string, object>();
|
|
for (int i = 0; i <= Params.Inputs.Count - 1; i++)
|
{
|
bool isFindKey = Params.Inputs.GetKey(i, out string key);
|
//忽略不需要的参数
|
if (!isFindKey || key == "Scrip" || key == "OtherDllPaths" || key == "DictionaryInputs")
|
continue;
|
|
dicInputs.TryAdd(key, Params.Inputs[i]);
|
}
|
|
return dicInputs;
|
}
|
catch { return new Dictionary<string, object>(); }
|
}
|
|
/// <summary>
|
/// 设置脚本输入
|
/// </summary>
|
/// <param name="dicInputs"></param>
|
/// <returns></returns>
|
public bool SetInputs(Dictionary<string, object> dicInputs)
|
{
|
try
|
{
|
//设置输入前将原先其他输入清除
|
if (!ClearInputs())
|
return false;
|
|
foreach (var kvp in dicInputs)
|
{
|
string key = kvp.Key;
|
//忽略不需要的参数
|
if (key == "Scrip" || key == "OtherDllPaths" || key == "DictionaryInputs")
|
continue;
|
|
Params.Inputs.Add(key, kvp.Value);
|
}
|
return true;
|
}
|
catch { return false; }
|
}
|
|
public bool ClearInputs()
|
{
|
try
|
{
|
//设置输入前将原先其他输入清除
|
for (int i = 0; i <= Params.Inputs.Count - 1; i++)
|
{
|
bool isFindKey = Params.Inputs.GetKey(i, out string key);
|
//忽略不需要的参数
|
if (!isFindKey || key == "Scrip" || key == "OtherDllPaths" || key == "DictionaryInputs")
|
continue;
|
|
Params.Inputs.Remove(i);
|
}
|
return true;
|
}
|
catch { return false; }
|
}
|
|
public bool ClearOutputs()
|
{
|
try
|
{
|
//设置输入前将原先其他输入清除
|
for (int i = 0; i <= Params.Outputs.Count - 1; i++)
|
{
|
Params.Outputs.Remove(i);
|
}
|
return true;
|
}
|
catch { return false; }
|
}
|
|
public void CompileCode()
|
{
|
try
|
{
|
// 1. 定义程序集名称
|
var assemblyName = Path.GetRandomFileName();
|
|
// 3. 配置引用
|
var references = new List<MetadataReference>
|
{
|
// 添加基础程序集
|
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
|
MetadataReference.CreateFromFile(typeof(Debug).Assembly.Location),
|
MetadataReference.CreateFromFile(typeof(IProcess).Assembly.Location),
|
|
// 添加 System.Collections.Concurrent 程序集
|
MetadataReference.CreateFromFile(typeof(System.Collections.Concurrent.ConcurrentDictionary<string, object>).Assembly.Location),
|
//MetadataReference.CreateFromFile(typeof(System.Collections.Generic.Dictionary<string, object>).Assembly.Location),
|
//MetadataReference.CreateFromFile(typeof(System.Linq.Enumerable).Assembly.Location),
|
|
// 添加其他必要程序集
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Runtime.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"mscorlib.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Core.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Data.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Web.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Xml.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Xml.Linq.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
|
"Newtonsoft.Json.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Collections.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Linq.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Data.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Drawing.dll")),
|
MetadataReference.CreateFromFile(Path.Combine(
|
Path.GetDirectoryName(typeof(object).Assembly.Location)!,
|
"System.Threading.Tasks.dll"))
|
};
|
//compilerParams.ReferencedAssemblies.Add("halcondotnet.dll");
|
|
// 3. 添加自定义DLL引用
|
List<string> dllPaths = (JArray.FromObject(Params.Inputs["OtherDllPaths"])).ToObject<List<string>>();
|
if (dllPaths != null)
|
{
|
foreach (var dllPath in dllPaths)
|
{
|
if (File.Exists(dllPath))
|
references.Add(MetadataReference.CreateFromFile(dllPath));
|
}
|
}
|
|
// 4. 获取代码并构建完整代码
|
string code = ProcessParams.ConvertToString(Params.Inputs["Scrip"]);
|
|
// 构建完整的代码,包含全局字典访问方法
|
string allCode = BuildCompleteCode(code);
|
|
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(allCode);
|
var compilation = CSharpCompilation.Create(
|
assemblyName,
|
new[] { syntaxTree },
|
references,
|
new CSharpCompilationOptions(
|
OutputKind.DynamicallyLinkedLibrary,
|
optimizationLevel: OptimizationLevel.Debug,
|
concurrentBuild: false));
|
|
// 5. 内存流编译
|
ms.Dispose();
|
ms = new MemoryStream();
|
EmitResult result = compilation.Emit(ms);
|
|
// 6. 处理编译错误
|
if (!result.Success)
|
{
|
Msg = "编译失败:\r\n";
|
foreach (Diagnostic diagnostic in result.Diagnostics)
|
{
|
// 获取错误位置信息
|
FileLinePositionSpan lineSpan = diagnostic.Location.GetLineSpan();
|
|
// 格式化行号信息
|
string lineInfo = $"行号: {lineSpan.StartLinePosition.Line + 1},"
|
+ $"列: {lineSpan.StartLinePosition.Character + 1}";
|
|
Msg += $"{lineInfo} → {diagnostic.GetMessage()} [{diagnostic.Id}]\r\n";
|
}
|
return;
|
}
|
|
// 7. 加载程序集
|
ms.Seek(0, SeekOrigin.Begin);
|
assembly = Assembly.Load(ms.ToArray());
|
|
programType = assembly.GetType($"{strProcessClass}.UserScript");
|
if (programType == null)
|
{
|
Msg = $"类[{strProcessClass}.UserScript]不存在";
|
return;
|
}
|
|
method = programType.GetMethod("ScripCode",
|
BindingFlags.NonPublic | BindingFlags.Instance);
|
|
if (method == null)
|
{
|
Msg = "编译失败,方法[ScripCode]不存在";
|
return;
|
}
|
|
// 8. 创建实例并初始化
|
instance = Activator.CreateInstance(programType);
|
if (instance == null)
|
{
|
Msg = $"实例化对象[{strProcessClass}.UserScript.ScripCode]不存在";
|
return;
|
}
|
|
Dictionary<string, object> dicInputs = GetInputs();
|
|
Dictionary<string, object> dicOutputs = null;
|
|
try
|
{
|
dicOutputs = Params.Outputs.GetAllByName();
|
if (dicOutputs == null)
|
dicOutputs = new Dictionary<string, object>();
|
}
|
catch { dicOutputs = new Dictionary<string, object>(); }
|
|
// 9. 初始化实例,传递输入输出字典和全局字典
|
InitializeInstance(dicInputs, dicOutputs);
|
|
// 10. 创建委托
|
delegateCall = (CustomDelegate)Delegate.CreateDelegate(
|
typeof(CustomDelegate),
|
instance, // 实例将在调用时传递
|
method);
|
|
Msg = "编译成功";
|
}
|
catch (Exception ex) { Msg = $"编译发生异常错误:{ex.Message}【{ex.StackTrace}】"; }
|
}
|
|
/// <summary>
|
/// 初始化实例,传递字典引用
|
/// </summary>
|
private void InitializeInstance(Dictionary<string, object> inputs, Dictionary<string, object> outputs)
|
{
|
// 获取Init方法
|
var initMethod = programType.GetMethod("Init",
|
BindingFlags.Public | BindingFlags.Instance);
|
|
if (initMethod != null)
|
{
|
// 调用Init方法,传递字典引用
|
object[] initParams = new object[]
|
{
|
inputs,
|
outputs // 传递字典引用
|
};
|
initMethod.Invoke(instance, initParams);
|
}
|
}
|
|
/// <summary>
|
/// 构建完整的代码,包含全局字典访问方法
|
/// </summary>
|
private string BuildCompleteCode(string userCode)
|
{
|
return $@"
|
using System;
|
using System.Collections.Generic;
|
using System.Collections.Concurrent;
|
using LB_VisionProcesses;
|
|
namespace LB_VisionProcesses.Processes.ScriptTool
|
{{
|
public partial class UserScript
|
{{
|
private Dictionary<string, object> inputs;
|
private Dictionary<string, object> outputs;
|
|
public void Init(
|
ref Dictionary<string, object> inputs,
|
ref Dictionary<string, object> outputs)
|
{{
|
this.inputs = inputs;
|
this.outputs = outputs;
|
}}
|
|
public void AddInputs(string key, object value)
|
{{
|
if (this.inputs.ContainsKey(key))
|
this.inputs[key] = value;
|
else
|
this.inputs.Add(key, value);
|
}}
|
|
public void AddOutputs(string key, object value)
|
{{
|
if (this.outputs.ContainsKey(key))
|
this.outputs[key] = value;
|
else
|
this.outputs.Add(key, value);
|
}}
|
|
public object GetInputs(string key)
|
{{
|
return this.inputs.TryGetValue(key, out object value) ? value : null;
|
}}
|
|
public object GetOutputs(string key)
|
{{
|
return this.outputs.TryGetValue(key, out object value) ? value : null;
|
}}
|
|
// 全局字典操作方法
|
public void AddGlobalVars(string key, object value)
|
{{
|
IProcess.dicGlobalVars?.AddOrUpdate(key, value, (k, v) => value);
|
}}
|
|
public object GetGlobalVars(string key)
|
{{
|
return IProcess.dicGlobalVars.TryGetValue(key, out object value) ? value : null;
|
}}
|
}}
|
|
{userCode}
|
}}
|
";
|
}
|
|
/// <summary>
|
/// 加载算法
|
/// </summary>
|
/// <param name="fullPath">完整路径带.json</param>
|
/// <returns></returns>
|
public override bool Load(string fullPath = null)
|
{
|
try
|
{
|
if (!base.Load(fullPath))
|
return false;
|
|
CompileCode();
|
return true;
|
}
|
catch { return false; }
|
}
|
}
|
}
|