528 lines
21 KiB
C#
528 lines
21 KiB
C#
using AngleSharp.Dom.Events;
|
||
using CapMachine.Wpf.ChannelModel;
|
||
using CapMachine.Wpf.Services;
|
||
using ImTools;
|
||
using Masuit.Tools;
|
||
using Masuit.Tools.Hardware;
|
||
using Prism.Mvvm;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Channels;
|
||
using System.Threading.Tasks;
|
||
using System.Timers;
|
||
|
||
namespace CapMachine.Wpf.Models.ProModelPars
|
||
{
|
||
/// <summary>
|
||
/// 程序执行模型
|
||
/// 包括程序名称和程序步骤集合、程序执行信息
|
||
/// 一个参数(速度、排气压力)对应一个程序执行模型
|
||
/// </summary>
|
||
public class ProExModel : BindableBase
|
||
{
|
||
/// <summary>
|
||
/// 实例化函数
|
||
/// </summary>
|
||
public ProExModel(Channel<ProRunChannelData> channel)
|
||
{
|
||
ProRunChannel = channel;
|
||
|
||
//秒触发一次
|
||
CycleTimer = new System.Timers.Timer(1000);
|
||
CycleTimer.Elapsed += SlopExCycleAction;
|
||
CycleTimer.AutoReset = true;
|
||
CycleTimer.Enabled = true;
|
||
CycleTimer.Start();
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 是否是速度仪表参数
|
||
/// </summary>
|
||
public bool IsSpeed { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 程序执行管道
|
||
/// </summary>
|
||
public Channel<ProRunChannelData> ProRunChannel { get; set; }
|
||
|
||
/// <summary>
|
||
/// 名称
|
||
/// </summary>
|
||
public string MeterName { get; set; }
|
||
|
||
/// <summary>
|
||
/// 当前模型是否启用
|
||
/// 当前有很多参数都是预设,但是有些参数是为其他的项目准备的,可能在当前的项目上都不会配置和使用。
|
||
/// 故设置Enable属性
|
||
/// </summary>
|
||
public bool Enable { get; set; } = true;
|
||
|
||
/// <summary>
|
||
/// 程序步骤集合
|
||
/// 在ProRuntimeService中已经初始实例化
|
||
/// </summary>
|
||
public List<ProStepExe> ListProStepExe { get; set; }
|
||
|
||
/// <summary>
|
||
/// 仪表步骤执行时间信息
|
||
/// </summary>
|
||
public ProRunTime ProRunTimeModel { get; set; }
|
||
|
||
/// <summary>
|
||
/// 下一步步骤数据执行
|
||
/// </summary>
|
||
public ProStepExe NextProStepExe { get; set; }
|
||
|
||
/// <summary>
|
||
/// 当前步骤数据执行
|
||
/// 给看步骤是否变化使用
|
||
/// </summary>
|
||
public ProStepExe CurProStepExe { get; set; }
|
||
|
||
/// <summary>
|
||
/// 上一步步骤数据执行
|
||
/// 给看步骤是否变化使用
|
||
/// </summary>
|
||
public ProStepExe LastProStepExe { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 当前程序段时间长
|
||
/// </summary>
|
||
public int CurProTimeSum { get; set; }
|
||
|
||
/// <summary>
|
||
/// 当前程序段开始时间
|
||
/// </summary>
|
||
public DateTime CurProStartTime { set; get; }
|
||
|
||
|
||
#region 仪表步骤执行时间信息
|
||
|
||
/// <summary>
|
||
/// 速度运行结束事件
|
||
/// </summary>
|
||
public event EventHandler<string> SpeedRunEndEvent;
|
||
|
||
|
||
/// <summary>
|
||
/// 是否启用
|
||
/// </summary>
|
||
public bool RunEnable { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// RunTimeCallSglEvent 是否注册关联方法
|
||
/// </summary>
|
||
public bool EventRegister { get; set; } = false;
|
||
|
||
/// <summary>
|
||
/// 当前步骤开始运行时间
|
||
/// </summary>
|
||
public DateTime StepStartDt { get; set; }
|
||
|
||
/// <summary>
|
||
/// 当前步骤结束运行时间
|
||
/// </summary>
|
||
public DateTime StepEndDt { get; set; }
|
||
|
||
|
||
/// <summary>
|
||
/// 循环扫描每个参数的结束时间是否到达的线程是一个线程循环执行,逐个执行,理论上每个都不同步,有一点点时间差,就是执行步骤会延后一点点
|
||
/// 我们的打点斜坡数据是自己的定时器执行的,每个参数同步执行,而且是有一点点提前执行的,因为先打点执行循环后再计算结束时间
|
||
/// 以上的就会造成,当新步骤到来执行时,其实之前的步骤的斜率打点的步骤可能已经打点完毕了,那么是乐见这样的状态,防止了两个的时间冲突问题。
|
||
/// </summary>
|
||
|
||
private DateTime _CurrentDateTime;
|
||
/// <summary>
|
||
/// 当前时间
|
||
/// </summary>
|
||
public DateTime CurrentDateTime
|
||
{
|
||
get
|
||
{
|
||
return _CurrentDateTime;
|
||
}
|
||
set
|
||
{
|
||
//value.ToString("yyyy-MM-dd HH:mm:ss") == EndDateTime.ToString("yyyy-MM-dd HH:mm:ss")
|
||
if (RunEnable == true && value >= StepEndDt)
|
||
{
|
||
_CurrentDateTime = value;
|
||
//达到后不再触发
|
||
//RunEnable = false;
|
||
//时间到了触发下载下一步步骤
|
||
|
||
//不为空的数据
|
||
if (NextProStepExe == null)
|
||
{
|
||
Console.WriteLine($"【时间】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 【参数名称】:{MeterName} " +
|
||
$"【Msg】:当前仪表参数全部执行完毕 ");
|
||
|
||
//速度执行结束发布事件
|
||
if (IsSpeed) SpeedRunEndEvent.Invoke(this, "OK");
|
||
|
||
//为空时不执行后续的数据
|
||
RunEnable = false;
|
||
return;
|
||
}
|
||
|
||
Console.WriteLine($"【时间】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 【参数名称】:{MeterName} " +
|
||
$"【程序Seg】{CurProStepExe.ProSegName} " +
|
||
$"【程序步骤】{CurProStepExe.MeterStep} " +
|
||
$"【Msg】:新步骤开始执行-----------> ");
|
||
|
||
////////首先判断参数是否相同///////////
|
||
|
||
var SvResult = false;//True代表不同,false代表相同
|
||
//判断PID是否跟上一次一样
|
||
if (CurProStepExe.EndSV != NextProStepExe.EndSV)
|
||
{
|
||
SvResult = true;
|
||
}
|
||
var PidResult = false;//True代表不同,false代表相同
|
||
//判断PID是否跟上一次一样
|
||
if (CurProStepExe.PIDNo != NextProStepExe.PIDNo)
|
||
{
|
||
PidResult = true;
|
||
}
|
||
var LimitResult = false;//True代表不同,false代表相同
|
||
//判断Limit是否跟上一次一样
|
||
if (CurProStepExe.LimitNo != NextProStepExe.LimitNo)
|
||
{
|
||
LimitResult = true;
|
||
}
|
||
|
||
////////发送步骤信息///////////
|
||
if (NextProStepExe.ExistSlop)
|
||
{
|
||
//存在斜坡数据,SV是通过集合陆续打点写入,但是PID和Limit需要判断是否要写入,只要写一次即可
|
||
ProRunChannel.Writer.WriteAsync(new ProRunChannelData()
|
||
{
|
||
MeterName = MeterName,
|
||
SV = NextProStepExe.EndSV,
|
||
ProSegName = NextProStepExe.ProSegName,
|
||
MeterStep = NextProStepExe.MeterStep,
|
||
CurLoadLimit = LimitResult == true ? new Limit() { Up = (short)NextProStepExe.CurConfigLimitDto.Up, Down = (short)NextProStepExe.CurConfigLimitDto.Down } : new Limit(),
|
||
CurLoadPID = PidResult == true ? new PID() { P = (short)NextProStepExe.CurConfigPIDDto.P, I = (short)NextProStepExe.CurConfigPIDDto.I, D = (short)NextProStepExe.CurConfigPIDDto.D } : new PID(),
|
||
RunStepType = GetRunStepType(PidResult, LimitResult, false),
|
||
IsSpeed=IsSpeed,
|
||
|
||
ListStepExd = GetStepExds(NextProStepExe),//拓展参数
|
||
|
||
});
|
||
|
||
//存在坡度数据
|
||
var SecStepDur = NextProStepExe.EndSV - NextProStepExe.StartSV;
|
||
var SecStepValue = SecStepDur * 1.0 / NextProStepExe.KeepTime;
|
||
|
||
//如果ListSlopExStep上还在执行打点任务的话,则如何处理
|
||
//先清除数据,并暂停
|
||
StopSlopExStep();
|
||
//组装斜坡数据,按照秒为间隔发送
|
||
for (var i = 1; i <= NextProStepExe.KeepTime; i++)
|
||
{
|
||
|
||
if (i == NextProStepExe.KeepTime)
|
||
{
|
||
//最后一个打点步骤数据数据,防止上面的相除得到误差,故在最后一个打点数据用NextProStepExe.EndSV,这样就确保了最后一个打点数据就是EndSV
|
||
var SlopExStep = new SlopExStep()
|
||
{
|
||
StepNo = i,
|
||
SV = NextProStepExe.EndSV,
|
||
IsHasEx = false,
|
||
};
|
||
ListSlopExStep.Add(SlopExStep);
|
||
}
|
||
else//非最后一个数据,正常累加
|
||
{
|
||
var SlopExStep = new SlopExStep()
|
||
{
|
||
StepNo = i,
|
||
SV = NextProStepExe.StartSV + (int)(SecStepValue * i),
|
||
IsHasEx = false,
|
||
};
|
||
ListSlopExStep.Add(SlopExStep);
|
||
}
|
||
}
|
||
|
||
//需要发送一次StartSV数据吗?
|
||
|
||
//组装完成开始循环打点
|
||
StartSlopExStep();
|
||
|
||
}
|
||
else//不存在斜坡数据的话,则直接发送EndSV数据即可
|
||
{
|
||
ProRunChannel.Writer.WriteAsync(new ProRunChannelData()
|
||
{
|
||
MeterName = MeterName,
|
||
SV = NextProStepExe.EndSV,
|
||
ProSegName = NextProStepExe.ProSegName,
|
||
MeterStep = NextProStepExe.MeterStep,
|
||
CurLoadLimit = LimitResult == true ? new Limit() { Up = (short)NextProStepExe.CurConfigLimitDto.Up, Down = (short)NextProStepExe.CurConfigLimitDto.Down } : new Limit(),
|
||
CurLoadPID = PidResult == true ? new PID() { P = (short)NextProStepExe.CurConfigPIDDto.P, I = (short)NextProStepExe.CurConfigPIDDto.I, D = (short)NextProStepExe.CurConfigPIDDto.D } : new PID(),
|
||
RunStepType = GetRunStepType(PidResult, LimitResult, true),
|
||
IsSpeed = IsSpeed,
|
||
|
||
ListStepExd = GetStepExds(NextProStepExe),//拓展参数
|
||
});
|
||
Console.WriteLine($"【时间】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 【参数名称】:{MeterName} " +
|
||
$"【程序Seg】{CurProStepExe.ProSegName} " +
|
||
$"【程序步骤】{CurProStepExe.MeterStep} " +
|
||
$"【SV】{NextProStepExe.EndSV} " +
|
||
$"【保持时间】{NextProStepExe.KeepTime} 秒"
|
||
);
|
||
}
|
||
|
||
|
||
////////准备新的数据///////////
|
||
|
||
LastProStepExe = CurProStepExe.DeepClone();
|
||
//准备下一步步骤数据
|
||
CurProStepExe = NextProStepExe.DeepClone();
|
||
|
||
//标记状态-当前执行中
|
||
if (ListProStepExe.Where(a => a.MeterStep == CurProStepExe.MeterStep).Any())
|
||
{
|
||
ListProStepExe.FindFirst(a => a.MeterStep == CurProStepExe.MeterStep).MeterStepIsExeing = true;
|
||
}
|
||
//标记状态-上一个执行完毕标记
|
||
if (ListProStepExe.Where(a => a.MeterStep == LastProStepExe.MeterStep).Any())
|
||
{
|
||
ListProStepExe.FindFirst(a => a.MeterStep == LastProStepExe.MeterStep).MeterStepIsExeing = false;
|
||
}
|
||
//标记状态-上一个执行完成
|
||
if (ListProStepExe.Where(a => a.MeterStep == LastProStepExe.MeterStep).Any())
|
||
{
|
||
ListProStepExe.FindFirst(a => a.MeterStep == LastProStepExe.MeterStep).MeterStepIsOK = true;
|
||
}
|
||
|
||
//设置下一步步骤数据
|
||
if (ListProStepExe.Where(x => x.MeterStep == CurProStepExe.MeterStep + 1).Any())
|
||
{
|
||
//存在下一步数据
|
||
NextProStepExe = ListProStepExe.FirstOrDefault(x => x.MeterStep == CurProStepExe.MeterStep + 1)!;
|
||
}
|
||
else
|
||
{
|
||
//没有下一步数据则判定为最后一步
|
||
NextProStepExe = null;
|
||
}
|
||
|
||
//设置步骤开始时间
|
||
StepStartDt = DateTime.Now;
|
||
|
||
//设置步骤结束时间
|
||
StepEndDt = StepStartDt.AddSeconds(CurProStepExe.KeepTime);
|
||
|
||
//设置下一步步骤运行时间
|
||
RunEnable = true;
|
||
|
||
}
|
||
else
|
||
{
|
||
_CurrentDateTime = value;
|
||
RunTime = (int)(_CurrentDateTime - StepEndDt).TotalSeconds;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取拓展信息
|
||
/// 一般是速度有这些参数
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private List<StepExd> GetStepExds(ProStepExe proStepExe)
|
||
{
|
||
if (proStepExe.ListStepExd != null && proStepExe.ListStepExd.Count() > 0)
|
||
{
|
||
return proStepExe.ListStepExd;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前步骤已经运行时长-秒
|
||
/// </summary>
|
||
public int RunTime { get; set; }
|
||
|
||
/// <summary>
|
||
/// 通过组合Pid和Limit组合出运行步骤类型
|
||
/// </summary>
|
||
/// <param name="pid">PID是否不同</param>
|
||
/// <param name="limit">Limit是否不同</param>
|
||
/// <param name="sv">SV是否不同</param>
|
||
/// <returns>运行步骤类型</returns>
|
||
private RunStepType GetRunStepType(bool pid, bool limit, bool sv)
|
||
{
|
||
if (pid && limit && sv)
|
||
{
|
||
return RunStepType.Step;
|
||
}
|
||
else if (pid && sv)
|
||
{
|
||
return RunStepType.StepPID;
|
||
}
|
||
else if (limit && sv)
|
||
{
|
||
return RunStepType.StepLimit;
|
||
}
|
||
else if (sv)
|
||
{
|
||
return RunStepType.StepSV;
|
||
}
|
||
else if (pid && limit)
|
||
{
|
||
return RunStepType.LimitPid;
|
||
}
|
||
else if (limit)
|
||
{
|
||
return RunStepType.Limit;
|
||
}
|
||
else if (pid)
|
||
{
|
||
return RunStepType.Pid;
|
||
}
|
||
else
|
||
{
|
||
return RunStepType.No;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
#region 步骤斜率执行 打点执行
|
||
|
||
/// <summary>
|
||
/// 开始打点执行
|
||
/// </summary>
|
||
private void StartSlopExStep()
|
||
{
|
||
//用SlopExEnable控制,存在一个可能,就是设置后,立刻执行,那么第一个步骤(第一秒)时间会被压缩,后面就正常了。最大是快一秒了,第一秒提前打点,这是可以接受的,
|
||
//结合步骤的扫描执行,形成时间差,就是新步骤到来时,斜率打点已经执行完毕,防止冲突,乐见这个状态
|
||
SlopExEnable = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止执行打点任务的执行
|
||
/// </summary>
|
||
private void StopSlopExStep()
|
||
{
|
||
SlopExEnable = false;
|
||
//清空数据
|
||
ListSlopExStep.Clear();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 斜率执行步骤集合
|
||
/// 打点集合
|
||
/// </summary>
|
||
public List<SlopExStep> ListSlopExStep { get; set; } = new List<SlopExStep>();
|
||
|
||
/// <summary>
|
||
/// 斜率执行循环周期
|
||
/// </summary>
|
||
/// <param name="sender"></param>
|
||
/// <param name="e"></param>
|
||
/// <exception cref="NotImplementedException"></exception>
|
||
private void SlopExCycleAction(object? sender, ElapsedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
//如果Execute执行的是一个很耗时的方法,会导致方法未执行完毕,定时器又启动了一个线程来执行Execute方法
|
||
//CycleTimer.Stop(); //先关闭定时器
|
||
if (SlopExEnable)
|
||
{
|
||
var NoExData = ListSlopExStep.Where(a => a.IsHasEx == false).OrderBy(a => a.StepNo).ToList();
|
||
if (NoExData.Any())
|
||
{
|
||
//发送步骤信息
|
||
ProRunChannel.Writer.TryWrite(new ProRunChannelData()
|
||
{
|
||
MeterName = MeterName,
|
||
SV = NoExData.First().SV,
|
||
IsSpeed = IsSpeed,
|
||
|
||
ProSegName = CurProStepExe.ProSegName,
|
||
MeterStep = CurProStepExe.MeterStep,
|
||
SlopStepNo = NoExData.First().StepNo,
|
||
CurLoadLimit = null,
|
||
CurLoadPID = null,
|
||
RunStepType = RunStepType.SlopCell,
|
||
});
|
||
//发送完毕后进行标记数据
|
||
NoExData.First().IsHasEx = true;
|
||
|
||
Console.WriteLine($"【时间】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 【参数名称】:{MeterName} " +
|
||
$"【程序Seg】{CurProStepExe.ProSegName} " +
|
||
$"【程序步骤】{CurProStepExe.MeterStep} " +
|
||
$"【斜坡打点步骤】{NoExData.First().StepNo} " +
|
||
$"【斜坡打点值SV】{NoExData.First().SV} " +
|
||
$"【Msg】:发送斜坡打点 ");
|
||
|
||
//执行一个循环打点后判断是否整个打点循环执行完毕
|
||
if (!ListSlopExStep.Where(a => a.IsHasEx == false).Any())
|
||
{
|
||
//执行完毕就标记状态
|
||
SlopExEnable = false;
|
||
Console.WriteLine($"【时间】{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")} 【参数名称】:{MeterName} " +
|
||
$"【程序Seg】{CurProStepExe.ProSegName} " +
|
||
$"【程序步骤】{CurProStepExe.MeterStep} " +
|
||
$"【Msg】:斜坡打点执行完毕 ");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//执行完毕了,没有要执行的数据效率
|
||
SlopExEnable = false;
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
//CycleTimer.Start(); //执行完毕后再开启器
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
//CycleTimer.Start(); //执行完毕后再开启器
|
||
//LogService.Info($"时间:{DateTime.Now.ToString()}-【PwAnalyze-CycleAction】-{ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 周期斜坡打点暂停执行
|
||
/// </summary>
|
||
public void PauseSlopExCyclePause()
|
||
{
|
||
SlopExEnable = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 周期斜坡打点 继续运行
|
||
/// </summary>
|
||
public void ContinueSlopExCyclePause()
|
||
{
|
||
SlopExEnable = true;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 周期定时器
|
||
/// </summary>
|
||
private System.Timers.Timer CycleTimer { get; set; }
|
||
|
||
/// <summary>
|
||
/// 步骤斜率执行使能状态
|
||
/// </summary>
|
||
public bool SlopExEnable { get; set; } = false;
|
||
|
||
#endregion
|
||
|
||
}
|
||
}
|