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
{
///
/// 程序执行模型
/// 包括程序名称和程序步骤集合、程序执行信息
/// 一个参数(速度、排气压力)对应一个程序执行模型
///
public class ProExModel : BindableBase
{
///
/// 实例化函数
///
public ProExModel(Channel channel)
{
ProRunChannel = channel;
//秒触发一次
CycleTimer = new System.Timers.Timer(1000);
CycleTimer.Elapsed += SlopExCycleAction;
CycleTimer.AutoReset = true;
CycleTimer.Enabled = true;
CycleTimer.Start();
}
///
/// 是否是速度仪表参数
///
public bool IsSpeed { get; set; }=false;
///
/// 程序执行管道
///
public Channel ProRunChannel { get; set; }
///
/// 名称
///
public string MeterName { get; set; }
///
/// 当前模型是否启用
/// 当前有很多参数都是预设,但是有些参数是为其他的项目准备的,可能在当前的项目上都不会配置和使用。
/// 故设置Enable属性
///
public bool Enable { get; set; } = true;
///
/// 程序步骤集合
/// 在ProRuntimeService中已经初始实例化
///
public List ListProStepExe { get; set; }
///
/// 仪表步骤执行时间信息
///
public ProRunTime ProRunTimeModel { get; set; }
///
/// 下一步步骤数据执行
///
public ProStepExe NextProStepExe { get; set; }
///
/// 当前步骤数据执行
/// 给看步骤是否变化使用
///
public ProStepExe CurProStepExe { get; set; }
///
/// 上一步步骤数据执行
/// 给看步骤是否变化使用
///
public ProStepExe LastProStepExe { get; set; }
///
/// 当前程序段时间长
///
public int CurProTimeSum { get; set; }
///
/// 当前程序段开始时间
///
public DateTime CurProStartTime { set; get; }
#region 仪表步骤执行时间信息
///
/// 速度运行结束事件
///
public event EventHandler SpeedRunEndEvent;
///
/// 是否启用
///
public bool RunEnable { get; set; } = false;
///
/// RunTimeCallSglEvent 是否注册关联方法
///
public bool EventRegister { get; set; } = false;
///
/// 当前步骤开始运行时间
///
public DateTime StepStartDt { get; set; }
///
/// 当前步骤结束运行时间
///
public DateTime StepEndDt { get; set; }
///
/// 循环扫描每个参数的结束时间是否到达的线程是一个线程循环执行,逐个执行,理论上每个都不同步,有一点点时间差,就是执行步骤会延后一点点
/// 我们的打点斜坡数据是自己的定时器执行的,每个参数同步执行,而且是有一点点提前执行的,因为先打点执行循环后再计算结束时间
/// 以上的就会造成,当新步骤到来执行时,其实之前的步骤的斜率打点的步骤可能已经打点完毕了,那么是乐见这样的状态,防止了两个的时间冲突问题。
///
private DateTime _CurrentDateTime;
///
/// 当前时间
///
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),
});
//存在坡度数据
var SecStepDur = NextProStepExe.EndSV - NextProStepExe.StartSV;
var SecStepValue = SecStepDur * 1.0 / NextProStepExe.KeepTime;
//如果ListSlopExStep上还在执行打点任务的话,则如何处理
//先清除数据,并暂停
StopSlopExStep();
//组装斜坡数据,按照秒为间隔发送
for (var i = 1; i <= NextProStepExe.KeepTime; i++)
{
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),
});
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;
}
}
}
///
/// 当前步骤已经运行时长-秒
///
public int RunTime { get; set; }
///
/// 通过组合Pid和Limit组合出运行步骤类型
///
/// PID是否不同
/// Limit是否不同
/// SV是否不同
/// 运行步骤类型
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.Step;
}
}
#endregion
#region 步骤斜率执行 打点执行
///
/// 开始打点执行
///
private void StartSlopExStep()
{
//用SlopExEnable控制,存在一个可能,就是设置后,立刻执行,那么第一个步骤(第一秒)时间会被压缩,后面就正常了。最大是快一秒了,第一秒提前打点,这是可以接受的,
//结合步骤的扫描执行,形成时间差,就是新步骤到来时,斜率打点已经执行完毕,防止冲突,乐见这个状态
SlopExEnable = true;
}
///
/// 停止执行打点任务的执行
///
private void StopSlopExStep()
{
SlopExEnable = false;
//清空数据
ListSlopExStep.Clear();
}
///
/// 斜率执行步骤集合
/// 打点集合
///
public List ListSlopExStep { get; set; } = new List();
///
/// 斜率执行循环周期
///
///
///
///
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,
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}");
}
}
///
/// 周期斜坡打点暂停执行
///
public void PauseSlopExCyclePause()
{
SlopExEnable = false;
}
///
/// 周期斜坡打点 继续运行
///
public void ContinueSlopExCyclePause()
{
SlopExEnable = true;
}
///
/// 周期定时器
///
private System.Timers.Timer CycleTimer { get; set; }
///
/// 步骤斜率执行使能状态
///
public bool SlopExEnable { get; set; } = false;
#endregion
}
}