847 lines
40 KiB
C#
847 lines
40 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
using System.Runtime.Serialization;
|
||
using System.Collections.ObjectModel;
|
||
using System.Threading;
|
||
using System.Reflection;
|
||
using System.Windows.Controls;
|
||
using System.IO;
|
||
using System.Windows.Media.Animation;
|
||
|
||
namespace Future.Contract
|
||
{
|
||
/// <summary>
|
||
/// 所有可执行指令的公共抽象基类;主要是设计支持逻辑和运行校验逻辑
|
||
/// </summary>
|
||
[DataContract]
|
||
public abstract class FutureInstruct : DependencyObject, IFuture,IConfigurableObject
|
||
{
|
||
#region 参数引用符号
|
||
/// <summary>
|
||
/// 参数引用标识符号
|
||
/// </summary>
|
||
public static readonly char PR_StartChar = '&';
|
||
/// <summary>
|
||
/// 参数引用分隔符号
|
||
/// </summary>
|
||
public static readonly char PR_SplitChar = '.';
|
||
/// <summary>
|
||
/// 参数引用标识符号
|
||
/// </summary>
|
||
public static readonly string PR_StartString = "&";
|
||
/// <summary>
|
||
/// 参数引用分隔符号
|
||
/// </summary>
|
||
public static readonly string PR_SplitString = ".";
|
||
#endregion
|
||
|
||
#region 基本属性定义
|
||
/// <summary>
|
||
/// 类型名称
|
||
/// </summary>
|
||
public virtual string ClassName { get; } = "原始指令";
|
||
/// <summary>
|
||
/// 指令的归类,用于引擎规范指令的固有行为
|
||
/// </summary>
|
||
public EInstructionKind InstKind
|
||
{
|
||
get
|
||
{
|
||
InstructionDefineAttribute ida = this.GetType().GetTypeInfo().GetCustomAttribute<InstructionDefineAttribute>();
|
||
if (ida != null) return ida.InstKind;
|
||
return EInstructionKind.Invalid;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 一个指令的识别码,用于构建指令Label的识别码部分
|
||
/// </summary>
|
||
/// <returns>只有一个字符的识别码</returns>
|
||
public string InstSign => this.InstKind.ToString().Substring(0, 1);
|
||
/// <summary>
|
||
/// 指令的标题
|
||
/// </summary>
|
||
[DataMember]
|
||
public string Title
|
||
{
|
||
get { return (string)GetValue(TitleProperty); }
|
||
set { SetValue(TitleProperty, value); }
|
||
}
|
||
|
||
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
|
||
public static readonly DependencyProperty TitleProperty =
|
||
DependencyProperty.Register("Title", typeof(string), typeof(FutureInstruct), new PropertyMetadata(""));
|
||
/// <summary>
|
||
/// 指令的描述
|
||
/// </summary>
|
||
[DataMember]
|
||
public string Remark
|
||
{
|
||
get { return (string)GetValue(RemarkProperty); }
|
||
set { SetValue(RemarkProperty, value); }
|
||
}
|
||
|
||
// Using a DependencyProperty as the backing store for Remark. This enables animation, styling, binding, etc...
|
||
public static readonly DependencyProperty RemarkProperty =
|
||
DependencyProperty.Register("Remark", typeof(string), typeof(FutureInstruct), new PropertyMetadata(""));
|
||
|
||
/// <summary>
|
||
/// 指令的标签,用于在上下文中对一条指令进行唯一标识
|
||
/// </summary>
|
||
[DataMember]
|
||
public string InstLabel
|
||
{
|
||
get { return (string)GetValue(InstLabelProperty); }
|
||
set { SetValue(InstLabelProperty, value); }
|
||
}
|
||
|
||
// Using a DependencyProperty as the backing store for Label. This enables animation, styling, binding, etc...
|
||
public static readonly DependencyProperty InstLabelProperty =
|
||
DependencyProperty.Register("InstLabel", typeof(string), typeof(FutureInstruct), new PropertyMetadata(""));
|
||
#endregion
|
||
|
||
#region 参数管理逻辑
|
||
/// <summary>
|
||
/// 获取当前对象可以注册的所有类型的表达式
|
||
/// </summary>
|
||
/// <param name="kind">要进行筛选的表达式类型</param>
|
||
/// <returns>筛选后符合条件的表达式集合迭代器</returns>
|
||
public virtual IEnumerable<ExpressionHandle> GetExpressionHandles(EParameterKind kind)
|
||
{
|
||
if ((kind & EParameterKind.General) == EParameterKind.General)
|
||
{
|
||
yield return new ExpressionHandle() { Handle = this.BuildContextExpression(), Title = this.Title, Remark = this.Remark };
|
||
foreach (ParameterDefineAttribute pda in this.EnumerateAllParameters())
|
||
{
|
||
if ((pda.Kind & kind) == 0) continue;
|
||
yield return new ExpressionHandle() { Handle = this.BuildContextExpression(pda.Name), Title = pda.Title, Remark = pda.Remark };
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 是否允许自由变更参数,需要使用特性【InstructDefine】进行声明
|
||
/// </summary>
|
||
public bool AllowAddParameter { get => (this.GetType().GetCustomAttribute<InstructionDefineAttribute>()?.AllowAddParameter) ?? false; }
|
||
/// <summary>
|
||
/// 是否允许自由编辑参数的分组名称
|
||
/// </summary>
|
||
public bool AllowManageGroups { get => (this.GetType().GetCustomAttribute<InstructionDefineAttribute>()?.AllowManageGroup) ?? false; }
|
||
/// <summary>
|
||
/// 通过特性标注的动态参数原始分组
|
||
/// </summary>
|
||
private string[] OriginalGroups { get => (this.GetType().GetCustomAttribute<InstructionDefineAttribute>()?.ParameterGroups).Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[] { "动态参数" }; }
|
||
/// <summary>
|
||
/// 当前指令可以允许的自定义参数的可选分组
|
||
/// </summary>
|
||
public List<string> ParameterGroups
|
||
{
|
||
get
|
||
{
|
||
List<string> result = new List<string>();
|
||
result.AddRange(this.OriginalGroups);
|
||
//将动态参数定义表中的分组也加到可用参数分组表中
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
if (!result.Contains(item.Key)) result.Add(item.Key);
|
||
|
||
return result;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 重命名一个动态参数分组名称
|
||
/// </summary>
|
||
/// <param name="newGroup">新名称</param>
|
||
/// <param name="oldGroup">旧名称</param>
|
||
/// <returns>如果失败则返回原因</returns>
|
||
public string RenameGroup(string newGroup, string oldGroup)
|
||
{
|
||
if (string.IsNullOrEmpty(newGroup) || string.IsNullOrEmpty(oldGroup)) return "新分组名与旧分组名均不可为空!";
|
||
if (newGroup == oldGroup) return null;
|
||
if (this.OriginalGroups.Contains(oldGroup)) return "指令定义中的原始分组不可以重命名!";
|
||
if (this.ParameterGroups.Contains(newGroup)) return "新分组名称已经被使用,不可重复使用!";
|
||
ObservableCollection<ParameterDefineAttribute> pdas = null;
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
{
|
||
if (item.Key == oldGroup)
|
||
{
|
||
pdas = item.Value;
|
||
this.DynamicParameterDefines.Remove(item);
|
||
break;
|
||
}
|
||
}
|
||
if (pdas != null) this.DynamicParameterDefines.Add(new KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>(newGroup, pdas));
|
||
return null;
|
||
}
|
||
/// <summary>
|
||
/// 删除一个动态参数分组
|
||
/// </summary>
|
||
/// <param name="grpupName">要删除的参数分组</param>
|
||
/// <returns>如果失败则返回失败原因</returns>
|
||
public string DeleteGroup(string grpupName)
|
||
{
|
||
if (string.IsNullOrEmpty(grpupName)) return "要删除的分组名称不可为空!";
|
||
if (this.OriginalGroups.Contains(grpupName)) return "原始指令定义的分组不可删除!";
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
{
|
||
if (item.Key == grpupName)
|
||
{
|
||
if (item.Value.Count == 0)
|
||
{
|
||
this.DynamicParameterDefines.Remove(item);
|
||
break;
|
||
}
|
||
else return "分组内参数不为空,不能删除分组!";
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
/// <summary>
|
||
/// 动态自定义参数集合;这个集合不支持序列化,所以使用【DynamicParameters】属性来进行序列化
|
||
/// </summary>
|
||
private readonly ObservableCollection<KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>> DynamicParameterDefines = new ObservableCollection<KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>>();
|
||
/// <summary>
|
||
/// 【设计器属性】专为指令序列设计器准备的动态参数集合
|
||
/// </summary>
|
||
public ObservableCollection<KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>> DynamicParametersForDesigner { get => this.DynamicParameterDefines; }
|
||
/// <summary>
|
||
/// 由于ObservableCollection不支持序列化,所以使用这个集合对动态参数进行序列化与反序列化支持
|
||
/// </summary>
|
||
[DataMember]
|
||
private readonly Dictionary<string, List<ParameterDefineAttribute>> DynamicParameters = new Dictionary<string, List<ParameterDefineAttribute>>();
|
||
/// <summary>
|
||
/// 序列化动态参数集
|
||
/// </summary>
|
||
public void EncodeDynamicParameter()
|
||
{
|
||
this.DynamicParameters.Clear();
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
{
|
||
List<ParameterDefineAttribute> parameters = new List<ParameterDefineAttribute>();
|
||
foreach (var parameter in item.Value) parameters.Add(parameter);
|
||
DynamicParameters.Add(item.Key, parameters);
|
||
}
|
||
foreach (var inst in this.SubInstructs) inst.EncodeDynamicParameter();
|
||
}
|
||
/// <summary>
|
||
/// 反序列化动态参数
|
||
/// </summary>
|
||
public void DecodeDynamicParameter()
|
||
{
|
||
this.DynamicParameterDefines.Clear();
|
||
foreach (var item in this.DynamicParameters)
|
||
{
|
||
ObservableCollection<ParameterDefineAttribute> parameters = new ObservableCollection<ParameterDefineAttribute>();
|
||
foreach (var parameter in item.Value) parameters.Add(parameter);
|
||
DynamicParameterDefines.Add(new KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>(item.Key, parameters));
|
||
}
|
||
foreach (var inst in this.SubInstructs) inst.DecodeDynamicParameter();
|
||
}
|
||
/// <summary>
|
||
/// 指令运行参数,初始值为属性注解获得;使用指令序列编辑器进行编辑;在指令执行之前会根据这些配置对指令的运行参数进行初始化
|
||
/// </summary>
|
||
[DataMember]
|
||
private readonly List<ParameterDefineAttribute> RuntimeParameterDefines = new List<ParameterDefineAttribute>();
|
||
/// <summary>
|
||
/// 【设计器属性】专为指令序列设计器使用的运行参数集合属性
|
||
/// </summary>
|
||
public List<ParameterDefineAttribute> RuntimeParametersForDesigner { get => this.MappingRuntimeParameters(); }
|
||
/// <summary>
|
||
/// 用于标记当前对象的运行参数是否已经完成映射
|
||
/// </summary>
|
||
private bool __RuntimeParametersMappingCompleted = false;
|
||
/// <summary>
|
||
/// 对当前对象的运行参数进行映射,以便指令序列设计器与原始定义进行使用
|
||
/// </summary>
|
||
/// <returns>返回映射完成的运行参数定义集合</returns>
|
||
private List<ParameterDefineAttribute> MappingRuntimeParameters()
|
||
{
|
||
if (this.__RuntimeParametersMappingCompleted) return this.RuntimeParameterDefines;
|
||
|
||
if (this.RuntimeParameterDefines.Count == 0)
|
||
{
|
||
PropertyInfo[] properties = this.GetType().GetProperties();
|
||
foreach (PropertyInfo property in properties)
|
||
{
|
||
ParameterDefineAttribute pda = property.GetCustomAttribute<ParameterDefineAttribute>();
|
||
if (pda != null)
|
||
{
|
||
if (string.IsNullOrEmpty(pda.Name)) pda.Name = property.Name;
|
||
|
||
pda.AttachProperty = property;
|
||
pda.AttachObject = this;
|
||
this.RuntimeParameterDefines.Add(pda);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//如果以前做过映射工作则重新检视映射的有效性
|
||
Dictionary<string, ParameterDefineAttribute> mapping = new Dictionary<string, ParameterDefineAttribute>();
|
||
foreach (ParameterDefineAttribute pda in this.RuntimeParameterDefines) mapping.Add(pda.Name, pda);
|
||
//先将集合清空,然后重新对其填充
|
||
this.RuntimeParameterDefines.Clear();
|
||
|
||
PropertyInfo[] properties = this.GetType().GetProperties();
|
||
foreach (PropertyInfo property in properties)
|
||
{
|
||
ParameterDefineAttribute pda = property.GetCustomAttribute<ParameterDefineAttribute>();
|
||
if (pda != null)
|
||
{
|
||
if (!mapping.ContainsKey(pda.Name))
|
||
{
|
||
mapping[pda.Name].AttachObject = this;
|
||
mapping[pda.Name].AttachProperty = property;
|
||
this.RuntimeParameterDefines.Add(mapping[pda.Name]);
|
||
}
|
||
else
|
||
{
|
||
//应对在指令序列设计完成之后指令的运行参数定义变更的问题
|
||
pda.AttachProperty = property;
|
||
pda.AttachObject = this;
|
||
this.RuntimeParameterDefines.Add(pda);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
this.__RuntimeParametersMappingCompleted = true;
|
||
return this.RuntimeParameterDefines;
|
||
}
|
||
/// <summary>
|
||
/// 将设计态的运行参数配置绑定到执行态的真实的运行参数上,为指令运行做准备
|
||
/// </summary>
|
||
private void BindingRuntimeParameters()
|
||
{
|
||
foreach (var item in MappingRuntimeParameters())
|
||
{
|
||
item.AttachProperty.SetValue(item.AttachObject, this.GetParameterValExp(item));
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 用于遍历一个指令中所有参数的迭代器,仅限于内部使用
|
||
/// </summary>
|
||
/// <returns>参数定义</returns>
|
||
protected IEnumerable<ParameterDefineAttribute> EnumerateAllParameters()
|
||
{
|
||
foreach (var item in this.MappingRuntimeParameters())
|
||
yield return item;
|
||
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
foreach (ParameterDefineAttribute pda in item.Value)
|
||
{
|
||
pda.AttachObject = this;
|
||
yield return pda;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 穷举所有动态参数,可以按照分组进行穷举
|
||
/// </summary>
|
||
/// <param name="group">要进行参数穷举的组名,不设置则穷举所有动态参数</param>
|
||
/// <returns>符合条件的参数的迭代器</returns>
|
||
protected IEnumerable<ParameterDefineAttribute> EnumerateDynamicParameters(string group = null)
|
||
{
|
||
if (string.IsNullOrEmpty(group))
|
||
{
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
foreach (ParameterDefineAttribute pda in item.Value)
|
||
{
|
||
pda.AttachObject = this;
|
||
yield return pda;
|
||
}
|
||
}
|
||
Dictionary<string, ObservableCollection<ParameterDefineAttribute>> dictionay = new Dictionary<string, ObservableCollection<ParameterDefineAttribute>>();
|
||
foreach (var item in this.DynamicParameterDefines) dictionay.Add(item.Key, item.Value);
|
||
if (dictionay.ContainsKey(group))
|
||
{
|
||
foreach (var item in dictionay[group])
|
||
{
|
||
item.AttachObject = this;
|
||
yield return item;
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 检查参数名称是否可用
|
||
/// </summary>
|
||
/// <param name="name">要检查的参数名称</param>
|
||
/// <param name="oldName">如果是修改操作则需要传入这个参数以定义原始参数</param>
|
||
/// <returns>可用=True,不可用=False</returns>
|
||
private bool CheckParameterNameValidity(string name, string oldName = null)
|
||
{
|
||
if (oldName != null && name == oldName) return true;
|
||
foreach (var item in this.EnumerateAllParameters())
|
||
if (item.Name == name) return false;
|
||
return true;
|
||
}
|
||
/// <summary>
|
||
/// 添加动态参数;注意:动态参数名称全局不可重复,包括所有由属性定义的参数名称
|
||
/// </summary>
|
||
/// <param name="parameter">参数对象</param>
|
||
/// <returns>如果不成功则返回错误原因</returns>
|
||
public string AddDynamicParameter(ParameterDefineAttribute parameter)
|
||
{
|
||
if (parameter == null) return "无法添加空的参数定义!";
|
||
if (this.CheckParameterNameValidity(parameter.Name)) return "参数名称重复!";
|
||
|
||
Dictionary<string, ObservableCollection<ParameterDefineAttribute>> dictionary = new Dictionary<string, ObservableCollection<ParameterDefineAttribute>>();
|
||
foreach (var item in this.DynamicParameterDefines) dictionary.Add(item.Key, item.Value);
|
||
|
||
string group = (string.IsNullOrEmpty(parameter.Name)) ? parameter.Name : "动态参数";
|
||
parameter.Name = group;
|
||
if (!dictionary.ContainsKey(group))
|
||
{
|
||
dictionary.Add(group, new ObservableCollection<ParameterDefineAttribute>());
|
||
this.DynamicParameterDefines.Add(new KeyValuePair<string, ObservableCollection<ParameterDefineAttribute>>(group, dictionary[group]));
|
||
}
|
||
dictionary[group].Add(parameter);
|
||
return null;
|
||
}
|
||
/// <summary>
|
||
/// 删除指令名称的动态参数
|
||
/// </summary>
|
||
/// <param name="name">要删除的动态参数名</param>
|
||
/// <returns>如果删除失败则返回失败原因</returns>
|
||
public string DelDynamicParameter(string name)
|
||
{
|
||
bool sign=false;
|
||
foreach (var item in this.DynamicParameterDefines)
|
||
{
|
||
foreach (var attribute in item.Value)
|
||
{
|
||
if (attribute.Name == name)
|
||
{
|
||
sign = true;
|
||
item.Value.Remove(attribute);
|
||
break;
|
||
}
|
||
}
|
||
if (sign) break;
|
||
}
|
||
return null;
|
||
}
|
||
/// <summary>
|
||
/// 修改一个动态参数,注意:运行参数不可以使用这种方式进行修改
|
||
/// </summary>
|
||
/// <param name="parameter">要修改的参数</param>
|
||
/// <param name="oldName">原参数名</param>
|
||
/// <returns>如果失败则返回错误原因</returns>
|
||
public string ModifyDynamicParameter(ParameterDefineAttribute parameter, string oldName)
|
||
{
|
||
if (parameter == null) return "无法添加空的参数定义!";
|
||
if (string.IsNullOrEmpty(oldName)) return "原始参数名传入不正确!";
|
||
if (this.CheckParameterNameValidity(parameter.Name)) return "参数名称重复!";
|
||
foreach (var item in this.EnumerateAllParameters())
|
||
{
|
||
if (item.Name == oldName)
|
||
{
|
||
item.Cover(parameter);
|
||
return null;
|
||
}
|
||
}
|
||
return "找不到原始参数,请正确传入原始参数名!";
|
||
}
|
||
/// <summary>
|
||
/// 通过参数名访问参数
|
||
/// </summary>
|
||
/// <param name="key">参数名</param>
|
||
/// <returns>参数值</returns>
|
||
public ParameterDefineAttribute this[string key]
|
||
{
|
||
get
|
||
{
|
||
foreach (ParameterDefineAttribute pda in this.EnumerateAllParameters())
|
||
if (pda.Name == key) return pda;
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 获取一个参数真正的“值表达式”;注意:这个返回值并不是真正的“值”还是一个“值表达式”
|
||
/// </summary>
|
||
/// <param name="parameter">参数对象</param>
|
||
/// <returns>以字符串表示的参数“真值”</returns>
|
||
public string GetParameterValExp(ParameterDefineAttribute parameter)
|
||
{
|
||
if (parameter == null) return "";
|
||
string result = "";
|
||
|
||
if (!string.IsNullOrEmpty(parameter.Expression))
|
||
result = (!parameter.Expression.StartsWith(FutureInstruct.PR_StartString)) ? parameter.Expression :
|
||
this.InstSpace.GetParameter(parameter.Expression, parameter.Default ?? "");
|
||
|
||
return result;
|
||
}
|
||
/// <summary>
|
||
/// 获取一个参数真正的“值表达式”;注意:这个返回值并不是真正的“值”还是一个“值表达式”
|
||
/// </summary>
|
||
/// <param name="paramName"></param>
|
||
/// <returns>如果找不到参数则返回空字符串,否则从上下文中寻找参数的真值</returns>
|
||
public string GetParameterValExp(string paramName)
|
||
{
|
||
ParameterDefineAttribute parameter = this[paramName];
|
||
if (parameter == null) return "";
|
||
return this.GetParameterValExp(parameter);
|
||
}
|
||
#endregion
|
||
|
||
#region 指令控制逻辑
|
||
/// <summary>
|
||
/// 是否允许添加子指令,需要使用特性【InstructDefine】进行声明
|
||
/// </summary>
|
||
public bool AllowAddChildren { get => (this.GetType().GetCustomAttribute<InstructionDefineAttribute>()?.AllowAddChildren) ?? false; }
|
||
/// <summary>
|
||
/// 子指令集合
|
||
/// </summary>
|
||
[DataMember]
|
||
public ObservableCollection<FutureInstruct> SubInstructs
|
||
{
|
||
get { return (ObservableCollection<FutureInstruct>)GetValue(SubInstructsPropertyKey.DependencyProperty); }
|
||
protected set { SetValue(SubInstructsPropertyKey, value); }
|
||
}
|
||
// Using a DependencyProperty as the backing store for SubInstructs. This enables animation, styling, binding, etc...
|
||
public static readonly DependencyPropertyKey SubInstructsPropertyKey =
|
||
DependencyProperty.RegisterReadOnly("SubInstructs", typeof(ObservableCollection<FutureInstruct>), typeof(FutureInstruct), new PropertyMetadata(new ObservableCollection<FutureInstruct>()));
|
||
|
||
/// <summary>
|
||
/// 指示当前指令是否为指令空间
|
||
/// </summary>
|
||
public bool IsInstSpace => this is SpaceInstructBase;
|
||
/// <summary>
|
||
/// 指示当前指令是否为迭代空间
|
||
/// </summary>
|
||
public bool IsLoopSpace => this is LoopInstructBase;
|
||
/// <summary>
|
||
/// 指示当前指令是否为结构空间
|
||
/// </summary>
|
||
public bool IsStructSpace => this is StructInstructBase;
|
||
/// <summary>
|
||
/// 指令空间,不参与序列化;指令空间的引用,存储参数表与信道信道表组成的运行上下文
|
||
/// </summary>
|
||
public SpaceInstructBase InstSpace
|
||
{
|
||
get
|
||
{
|
||
FutureInstruct current = this;
|
||
do
|
||
{
|
||
if (current is SpaceInstructBase)
|
||
return current as SpaceInstructBase;
|
||
current = current.InstParent;
|
||
} while (current != null);
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 父级指令,不参与序列化;用于逆向追溯指令层级结构,验证指令嵌套关系时使用
|
||
/// </summary>
|
||
public FutureInstruct InstParent { get; private set; }
|
||
/// <summary>
|
||
/// 迭代空间,不参与序列化;可以使用Continue和Break实现迭代控制逻辑
|
||
/// </summary>
|
||
public LoopInstructBase LoopSpace
|
||
{
|
||
get
|
||
{
|
||
FutureInstruct current = this;
|
||
do
|
||
{
|
||
if (current is LoopInstructBase)
|
||
return current as LoopInstructBase;
|
||
current = current.InstParent;
|
||
} while (current != null);
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 指令编组,不参与序列化;可以使用Return指令放弃后续指令执行
|
||
/// </summary>
|
||
public StructInstructBase InstStruct
|
||
{
|
||
get
|
||
{
|
||
FutureInstruct current = this;
|
||
do
|
||
{
|
||
if (current is StructInstructBase)
|
||
return current as StructInstructBase;
|
||
current = current.InstParent;
|
||
} while (current != null);
|
||
return null;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 指令的根
|
||
/// </summary>
|
||
public FutureInstruct InstRoot
|
||
{
|
||
get
|
||
{
|
||
FutureInstruct current = this.InstParent;
|
||
while (current.InstParent != null) current = current.InstParent;
|
||
return current;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 循环构建参数引用映射表,可以用于加速运行,减小指令与参数上下文
|
||
/// </summary>
|
||
/// <param name="mapping">应用映射表,所有对象都要将自己需要的引用值加入其中</param>
|
||
protected void BuildReferenceMapping(List<string> mapping)
|
||
{
|
||
if (mapping == null) throw new Exception("构建参数映射表时必须传入已经初始化完成的参数映射表的引用!");
|
||
|
||
foreach (ParameterDefineAttribute pda in this.EnumerateAllParameters())
|
||
{
|
||
if (!string.IsNullOrEmpty(pda.Expression) && pda.Expression.StartsWith(FutureInstruct.PR_SplitString))
|
||
if (!mapping.Contains(pda.Expression)) mapping.Add(pda.Expression);
|
||
}
|
||
foreach (var inst in this.SubInstructs) inst.BuildReferenceMapping(mapping);
|
||
}
|
||
/// <summary>
|
||
/// 检查一个标识符是否被其它指令引用
|
||
/// </summary>
|
||
/// <param name="identify">要检查的标识符</param>
|
||
/// <returns>这个标识符是否被相同指令空间中其它指令引用</returns>
|
||
protected bool IsNeedRegistryContext(string identify)
|
||
{
|
||
if (string.IsNullOrEmpty(identify)) return false;
|
||
if (this.InstSpace == null) return false;
|
||
else return this.InstSpace.IsNeedRegistryContext(identify);
|
||
}
|
||
/// <summary>
|
||
/// 获取当前指令的代表值:指令执行前为初始值,执行后则为返回值,如果是迭代执行则可能会有过程值
|
||
/// </summary>
|
||
/// <returns>只返回“string”类型的值表达式,而不是真实的值</returns>
|
||
protected abstract string GetInstructValExp();
|
||
/// <summary>
|
||
/// 用于构建一个参数在上下文中的表达式
|
||
/// </summary>
|
||
/// <param name="paramName">参数名</param>
|
||
/// <returns>一个构建好的上下文表达式</returns>
|
||
protected string BuildContextExpression(string paramName = null) => (paramName == null) ? $"{FutureInstruct.PR_StartString}{this.InstLabel}" : $"{FutureInstruct.PR_StartString}{this.InstLabel}{FutureInstruct.PR_SplitString}{paramName}";
|
||
/// <summary>
|
||
/// 指令执行的外部入口
|
||
/// </summary>
|
||
/// <returns>指令的执行结果</returns>
|
||
public virtual void Execute()
|
||
{
|
||
//如果找不到指令空间则指令不可执行
|
||
if (this.InstSpace == null) return;
|
||
//TODO:指令执行逻辑=>校验执行空间的执行状态码;如果指令空间处于“暂停”状态则需要指令循环等待
|
||
//TODO:指令执行逻辑=>校验结构空间的执行状态码;如果结构空间处于“退出”状态则需要放弃指令执行
|
||
//TODO:指令执行逻辑=>校验迭代空间的执行状态码;如果迭代空间不处于“正常”状态需要放弃指令执行
|
||
|
||
//通过运行参数配置修正运行参数属性的真实值
|
||
this.BindingRuntimeParameters();
|
||
//TODO:指令执行逻辑=>将自身的值写入指令上下文;需要先行检查当前指令的值是否需要注册到上下文中
|
||
//TODO:指令执行逻辑=>将所有参数写入参数下下文;需要先行检查当前指令的值是否需要注册到上下文中
|
||
|
||
InstructResult result = new InstructResult(this.InstLabel, receipt: EInstructReceipt.Normal, this.GetInstructValExp(), message: $"指令【{this.InstLabel}:{this.Title}】执行检查完成,开始执行指令逻辑!");
|
||
try
|
||
{
|
||
result = this.DoExecute(this.InstSpace.OutChannels());
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Exception = ex;
|
||
result.Message = ex.Message;
|
||
result.Receipt = EInstructReceipt.Failed;
|
||
}
|
||
finally
|
||
{
|
||
this.DoFinally(this.InstSpace.OutChannels(), result);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 指令的前段执行逻辑,在子指令被执行前执行
|
||
/// </summary>
|
||
/// <param name="outChannels">执行信息输出信道表</param>
|
||
/// <returns>指令的执行结果</returns>
|
||
protected abstract InstructResult DoExecute(IEnumerable<IOutputChannel> outChannels);
|
||
/// <summary>
|
||
/// 指令的后段执行逻辑,在子指令被执行后执行
|
||
/// </summary>
|
||
/// <param name="outChannels">执行信息输出信道表</param>
|
||
/// <param name="result">前段指令执行结果</param>
|
||
/// <returns>执行结果</returns>
|
||
protected virtual void DoFinally(IEnumerable<IOutputChannel> outChannels, InstructResult result)
|
||
{
|
||
foreach (var channel in outChannels) channel.WriteInstructResult(result);
|
||
}
|
||
/// <summary>
|
||
/// 验证指令的有效性;先执行每个指令的特殊校验逻辑,再执行公共校验逻辑(指令上下级从属关系、指令与参数引用时序等)
|
||
/// </summary>
|
||
/// <param name="infoChannel">信息输出信道</param>
|
||
public virtual void Verify(IOutputChannel infoChannel)
|
||
{
|
||
if (infoChannel == null) throw new ArgumentNullException("指令校验必须提供信息输出通道!");
|
||
//调用各性话校验逻辑,包括信道等非通用校验逻辑
|
||
this.DoVerify(infoChannel);
|
||
|
||
//将自身被引用的参数标识符添加到上下文中,以便被后续的指令感知到
|
||
if (this.InstSpace.IsExistReference(this.InstLabel)) this.InstSpace.SetParameter(this.InstLabel, null);
|
||
foreach (var param in this.EnumerateAllParameters())
|
||
{
|
||
string identify = $"{this.InstLabel}{FutureInstruct.PR_SplitChar}{param.Name}";
|
||
if (this.InstSpace.IsExistParameter(identify)) this.InstSpace.SetParameter(identify, null);
|
||
}
|
||
|
||
//校验自身引用的指令或参数是否在执行时序开始前已被加入到上下文中
|
||
foreach (var param in this.EnumerateAllParameters())
|
||
{
|
||
if (string.IsNullOrEmpty(param.Expression)) continue;
|
||
//校验一个引用参数是否可使用
|
||
if (param.Expression.StartsWith(FutureInstruct.PR_StartString) && !this.InstSpace.IsExistParameter(param.Expression))
|
||
infoChannel.WriteInstructResult(
|
||
new InstructResult(
|
||
this.InstLabel,
|
||
EInstructReceipt.Warning,
|
||
message: $"指令【{this.InstLabel.PadLeft(8)}】的参数【{(string.IsNullOrEmpty(param.Title) ? param.Name : param.Title)}】在调用时在上下文中不可见,请检查指令序列的编排顺序!"
|
||
)
|
||
);
|
||
//校验一个参数的配置是否合规
|
||
string paramVerifyResult = param.Verify();
|
||
if (!string.IsNullOrEmpty(paramVerifyResult))
|
||
infoChannel.WriteInstructResult(
|
||
new InstructResult(
|
||
this.InstLabel,
|
||
EInstructReceipt.Warning,
|
||
message: $"指令【{this.InstLabel.PadLeft(8)}】的参数【{(string.IsNullOrEmpty(param.Title) ? param.Name : param.Title)}】配置存在问题:{paramVerifyResult}"
|
||
)
|
||
);
|
||
}
|
||
//调用所有子指令的校验逻辑
|
||
foreach (FutureInstruct fi in this.SubInstructs) fi.Verify(infoChannel);
|
||
}
|
||
/// <summary>
|
||
/// 真实的指令有效性验证逻辑
|
||
/// </summary>
|
||
/// <param name="infoChannel">信息输出信道</param>
|
||
protected abstract void DoVerify(IOutputChannel infoChannel);
|
||
/// <summary>
|
||
/// 根据指令标识符获取指令的引用;一般用于行为指令
|
||
/// </summary>
|
||
/// <param name="lableName">指令在指令序列内的标识</param>
|
||
/// <returns>如果找不到则返回null</returns>
|
||
protected FutureInstruct GetInstructObject(string lableName)
|
||
{
|
||
FutureInstruct root = this.InstSpace;
|
||
if (root == null) return null;
|
||
return root.FindInstruct(lableName);
|
||
}
|
||
/// <summary>
|
||
/// 从本节点开始深度递归查找指定标号的指令对象;一般用于行为指令
|
||
/// </summary>
|
||
/// <param name="lableName">要查找的指令标号</param>
|
||
/// <returns>如果找不到则返回null</returns>
|
||
protected internal FutureInstruct FindInstruct(string lableName)
|
||
{
|
||
if (this.InstLabel == lableName) return this;
|
||
else
|
||
{
|
||
FutureInstruct result = null;
|
||
foreach (FutureInstruct inst in this.SubInstructs)
|
||
{
|
||
result = inst.FindInstruct(lableName);
|
||
if (result != null) return result;
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 把一个指令链接到另一个指令上
|
||
/// </summary>
|
||
/// <param name="parent">父级指令</param>
|
||
/// <param name="isReLink">是否是重新链接;即,是否是在一个指令空间内挪移指令位置</param>
|
||
/// <returns>如果不成功则返回错误信息</returns>
|
||
public string Link(FutureInstruct parent, bool isReLink = false)
|
||
{
|
||
if (parent == null) return "无法将一个指令链接到一个空指令上!";
|
||
StringBuilder resultBuilder = new StringBuilder();
|
||
string checkParentMsg = this.CheckParent(parent);
|
||
string checkChildImsg = parent.CheckChild(this);
|
||
|
||
if (checkParentMsg != null) resultBuilder.AppendLine(checkParentMsg);
|
||
else if (checkChildImsg != null) resultBuilder.AppendLine(checkChildImsg);
|
||
else
|
||
{
|
||
if (isReLink) this.InstParent = parent;
|
||
else this.DoLink(parent);
|
||
}
|
||
return resultBuilder.ToString();
|
||
}
|
||
/// <summary>
|
||
/// 变更指令标签并且变更所有参数的引用表达式值;完成后深度递归所有子孙节点执行相同操作
|
||
/// </summary>
|
||
/// <param name="parent">用于构建父子级关系</param>
|
||
/// <param name="labelMapping"></param>
|
||
internal virtual void DoLink(FutureInstruct parent, Dictionary<string, string> labelMapping = null)
|
||
{
|
||
this.InstParent = parent;
|
||
|
||
Dictionary<string, string> mapping = labelMapping ?? new Dictionary<string, string>();
|
||
string newLabel = (++this.InstSpace.LabelSeed).ToString() + this.InstSign;
|
||
mapping.Add(this.InstLabel, newLabel);
|
||
this.InstLabel = newLabel;
|
||
foreach (var param in this.EnumerateAllParameters())
|
||
{
|
||
if (!string.IsNullOrEmpty(param.Expression))
|
||
{
|
||
string[] args = param.Expression.Split(new char[] { FutureInstruct.PR_SplitChar, FutureInstruct.PR_StartChar }, StringSplitOptions.RemoveEmptyEntries);
|
||
if (args.Length > 0 && mapping.ContainsKey(args[0]))
|
||
param.Expression = param.Expression.Replace(args[0], mapping[args[0]]);
|
||
}
|
||
}
|
||
foreach (FutureInstruct inst in this.SubInstructs) inst.DoLink(this, labelMapping);
|
||
}
|
||
/// <summary>
|
||
/// 校验当前指令及其子指令是否可以链接到指定的父级指令上
|
||
/// </summary>
|
||
/// <param name="parentInst">新的父级指令,如果为空则表示未更换父级指令</param>
|
||
/// <returns>如果不能则返回原因</returns>
|
||
protected abstract string CheckParent(FutureInstruct parentInst);
|
||
/// <summary>
|
||
/// 检验一个指令是否可以成为当前指令的子指令
|
||
/// </summary>
|
||
/// <param name="childInst">要进行校验的指令</param>
|
||
/// <returns>如果不成功则返回原因</returns>
|
||
protected abstract string CheckChild(FutureInstruct childInst);
|
||
/// <summary>
|
||
/// 将一个指令的"依赖属性"值传承给其所有子孙级指令,注意:普通属性或是动态参数不允许传承
|
||
/// </summary>
|
||
/// <param name="property">要传承的属性</param>
|
||
/// <param name="inheritInst">参考指令默认为[null]=[this]</param>
|
||
public void Impart(DependencyProperty property, FutureInstruct inheritInst = null)
|
||
{
|
||
Type type = property?.OwnerType;
|
||
if (type == null) return;
|
||
if (property.ReadOnly) return;
|
||
|
||
//如果当前对象是type的子类或是间接子类,则传承当前属性
|
||
if (inheritInst != null && type.IsInstanceOfType(this))
|
||
this.SetValue(property, inheritInst.GetValue(property));
|
||
|
||
//处理参考指令默认值,如果为[null]则转化为[this].
|
||
FutureInstruct origin = inheritInst ?? this;
|
||
foreach (FutureInstruct inst in this.SubInstructs)
|
||
inst.Impart(property, origin);
|
||
}
|
||
|
||
public virtual IFuture Clone()
|
||
{
|
||
//TODO:采用序列化的方式深度克隆当前指令对象
|
||
throw new NotImplementedException();
|
||
}
|
||
/// <summary>
|
||
/// 对象属性覆盖:用引用对象属性覆盖当前对象属性,对引用属性只会实现引用赋值,不做深拷贝
|
||
/// </summary>
|
||
/// <param name="refer">用于覆盖当前对象属性的源对象</param>
|
||
/// <returns>如果源对象类型不是当前对象或是其子类类型则直接覆盖失败,返回False</returns>
|
||
public virtual bool Cover(IFuture refer)
|
||
{
|
||
//TODO:使用源对象的所有信息覆盖当前对象信息
|
||
throw new NotImplementedException();
|
||
}
|
||
#endregion
|
||
}
|
||
}
|