using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Windows.Threading; using FlyADBase; namespace Flyad7_WPF { public class AutoSync : INotifyPropertyChanged, Misc.ISaveToXml { IFlyADClient mFlyAD; const int OutIdxOfVSensor = 1; /// <summary> /// 在边界等待的时间,s /// </summary> const int ExtraTime = 2; /// <summary> /// 开始等待时间 /// </summary> const int StartWaitTime = 60; /// <summary> /// 温修消耗时间,s /// </summary> public int ConsumeOfSampleBackw { get; set; } /// <summary> /// 样品消耗时间,s /// </summary> public int ConsumeOfSampleForw { get; set; } /// <summary> /// 扫描消耗时间,s /// </summary> public int ConsumeOfScan { get; set; } /// <summary> /// 等待grid完成耗时,s /// </summary> public int ConsumeOfGrid { get; set; } /// <summary> /// 温修主轴脉冲消耗 /// </summary> public int Pos2ConsumeOfSampleBackw { get; protected set; } /// <summary> /// 样品主轴脉冲消耗 /// </summary> public int Pos2ConsumeOfSampleForw { get; protected set; } /// <summary> /// 扫描单次 主轴脉冲消耗 /// </summary> public int Pos2ConsumeOfScan { get; set; } /// <summary> /// 等待下次扫描 主轴脉冲消耗 /// </summary> public int Pos2ConsumeOfWaitNextScan { get; set; } public int SampleInterval { get; set; } /// <summary> /// 扫描宽度,单位脉冲 /// </summary> public int ScanWidth { get; set; } /// <summary> /// 扫描开始位置,单位脉冲 /// </summary> public int ScanBegin { get; set; } public Misc.Range SamplePos_Backw = new Misc.Range(); public Misc.Range SamplePos_Forw = new Misc.Range(); public UInt32 Velocity { get; set; } public UInt32 VSample { get; set; } double VDistanceWithHeaderInUsed; public double DistanceWithHeader { get; set; } = 0.486; public int sampleCount { get; set; } public int last_pos2 { get; set; } /// <summary> /// pos1 脉冲比例, 如 Velocity = 8000, 所以 Speed1 = Velocity*pos1_scale /// </summary> public double Pos1Scale { get; set; } = 0.25; /// <summary> /// 主轴脉冲 mm/脉冲 /// </summary> public double Mmpp2 { get; set; } = 0.326; /// <summary> /// 当前线速度 /// </summary> public double CurrFilmVelocity { get; set; } /// <summary> /// 当前速度 /// </summary> public double CurrVelocity { get; set; } /// <summary> /// 电机脉冲 mm/脉冲 /// </summary> public double Mmpp1 { get; set; } = 0.16; /// <summary> /// m/min /// </summary> public double FilmVelocity { get; set; } public AutoSync(FlyAD7 flyad) { ScanBegin = 484; ScanWidth = 4000; SamplePos_Backw.Begin = 67 - 50; SamplePos_Backw.End = 67 + 50; SamplePos_Forw.Begin = 5200 - 50; SamplePos_Forw.End = 5200 + 50; Velocity = 2000;// 8000; VSample = 1000;// 3000; FilmVelocity = 18;//m/min SampleInterval = 10; Pos1Scale = 0.25; ConsumeOfSampleBackw = 6; ConsumeOfSampleForw = 6; ConsumeOfScan = 8; ConsumeOfGrid = 3; Load(); Init(flyad); } void Init(IFlyADClient flyad) { mFlyAD = flyad; Misc.BindingOperations.SetBinding(mFlyAD, new string[] { "Ratio02", "Ratio01" }, delegate() { Pos1Scale = (double)mFlyAD.Ratio02 / mFlyAD.Ratio01; }); Misc.BindingOperations.SetBinding(mFlyAD, "Speed2", delegate() { CurrFilmVelocity = (mFlyAD.Speed2 * Mmpp2 * 60) / 1000.0; }); Misc.BindingOperations.SetBinding(mFlyAD, "Speed", delegate() { CurrVelocity = (mFlyAD.Speed * Mmpp1 * 60) / 1000.0; }); TestInit(); } void GM_SyncScan_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == "DistanceWithHeader") //滞后,应该增大 { double d = DistanceWithHeader - VDistanceWithHeaderInUsed; d *= 1000.0; d = -d; int offset = (int)(d / Mmpp2); mFlyAD.SetPos2Offset(offset);//提前 VDistanceWithHeaderInUsed = DistanceWithHeader; } } void mFlyAD_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { if (e.PropertyName == "Marker") { for (int i = 0; i < syncOrderList.Count(); i++) { if (syncOrderList[i].Marker == mFlyAD.Marker) { //当前正在运作此同步指令 //在它之前的,肯定运行完了,删除 syncOrderList.RemoveRange(0, i); OnPoll_SyncOrder(); break; } } } else if (e.PropertyName == "IsSync") { if (!mFlyAD.IsSync) { Stop(); } } } public void Start() { //清空同步指令 syncOrderList.Clear(); mFlyAD.SyncPos2Clear(); mFlyAD.PropertyChanged += mFlyAD_PropertyChanged; //InitSyncParam(); mFlyAD.Pos1LCShift = -ScanBegin; //ScanWidth = syncparam.ScanWidth; double wait_mm = StartWaitTime * FilmVelocity / 60 * 1000.0; int pos2 = (int)((-DistanceWithHeader * 1000 - wait_mm) / Mmpp2); int pos2at01 = (int)((-DistanceWithHeader * 1000) / Mmpp2); last_pos2 = 0; sampleCount = 0; //当前last_pos2 >= pos2 LastOrder = new SyncOrder() { Order = SyncOrder.OrderType.Init }; OnPoll_SyncOrder(); mFlyAD.SyncBegin(pos2); mFlyAD.SyncPos2Clear(); mFlyAD.SetPos2At01(pos2at01, true); //设置纵向边界信号为0 ClearVSensor(); DispatcherTimer timer = new DispatcherTimer(); timer.Tick += (s, e) => { ((DispatcherTimer)s).Stop(); SetVSensor(); }; timer.Interval = TimeSpan.FromSeconds(0.5); timer.Start(); } public void Stop() { mFlyAD.SyncEnd(); //mFlyAD.SyncClear(); mFlyAD.PropertyChanged -= mFlyAD_PropertyChanged; } public void InitSyncParam() { double speed1 = Velocity * Pos1Scale;// pos1 pps double speed2 = FilmVelocity * 1000 / 60 / Mmpp2; //pos2 pps //反向取样时间 double consumeTOfTemp = ConsumeOfSampleForw; Pos2ConsumeOfSampleBackw = (int)(speed2 * consumeTOfTemp); //正向取样时间 double consumeTOfSample = consumeTOfTemp; Pos2ConsumeOfSampleForw = (int)(speed2 * consumeTOfSample); //单次扫描时间 double consumeTOfScan; consumeTOfScan = ConsumeOfScan; Pos2ConsumeOfScan = (int)(speed2 * consumeTOfScan); //在边界等1秒 Pos2ConsumeOfWaitNextScan = (int)(speed2 * (ConsumeOfGrid + 1)); //mFlyAD.SyncEnd(); mFlyAD.SyncClear(); } #region 测试模式 /// <summary> /// 当前线速度 /// </summary> public int TestInterval { get; set; } = 60; /// <summary> /// 当前线速度 /// </summary> public int TestCnt { get; set; } public enum TEST_STATE { IDLE, INIT, START, POLL, END, ERROR } public TEST_STATE TestState { get; set; } = TEST_STATE.IDLE; DispatcherTimer timer = new DispatcherTimer(); void TestInit() { TestState = TEST_STATE.INIT; timer.Interval = TimeSpan.FromSeconds(1); timer.Tick += new EventHandler(timer_Tick); mFlyAD.PropertyChanged += (s, e) => { if (e.PropertyName == "IsConnected") { if (mFlyAD.IsConnected == false) { TestState = TEST_STATE.ERROR; timer.Stop(); } } }; } public void StartTest() { if (!timer.IsEnabled) { TestState = TEST_STATE.INIT; timer.Start(); } } public void StopTest() { TestState = TEST_STATE.IDLE; timer.Stop(); } void timer_Tick(object sender, EventArgs e) { switch (TestState) { case TEST_STATE.INIT: InitSyncParam(); TestState = TEST_STATE.START; break; case TEST_STATE.START: Start(); TestState = TEST_STATE.POLL; TestCnt = 0; break; case TEST_STATE.POLL: { TestCnt++; if (TestCnt >= TestInterval) { TestState = TEST_STATE.END; } }break; case TEST_STATE.END: Stop(); TestState = TEST_STATE.INIT; break; } } #endregion /// <summary> /// 令虚拟边界传感器 输出1 (OUT1,输出1 ,OUT1 接 机头 与 机尾 AD卡的同步信号) /// </summary> public void SetVSensor() { mFlyAD.SetOutput( (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1), (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1)); } /// <summary> /// 令虚拟边界传感器 输出0 (OUT1,输出0 ,OUT1 接 机头 与 机尾 AD卡的同步信号) /// </summary> public void ClearVSensor() { mFlyAD.SetOutput( (UInt16)Misc.MyBase.BIT(OutIdxOfVSensor - 1), (UInt16)~Misc.MyBase.BIT(OutIdxOfVSensor - 1)); } #region 同步指令管理 /// <summary> /// 它就是flyad7 当中的同步指令列表 /// 每收到grid后面带的 marker,就要判断是 哪句order,做哪件事情 /// </summary> public List<SyncOrder> syncOrderList = new List<SyncOrder>(); private SyncOrder lastorder; [PropertyChanged.DoNotCheckEquality] public SyncOrder LastOrder { get; set; } public struct SyncOrder { /// <summary> /// 都是从 ScanRangeLC.Begin 开始运行的 /// -----样品取样 & 归0周期: /// 1.归0, /// 2.反向取样 /// 3.正向扫描 /// 4.正向取样 /// 5.反向扫描 /// ------正常周期: /// 1.正向扫描 /// 2.反向扫描 /// </summary> public enum OrderType { Init,//刚开始,下一条就是First /// <summary> /// 老板说,要同步扫描前,必须有一条 SyncRunTo 或 SyncRunToLC /// </summary> First, /// <summary> /// 【归零】,SyncOrigin() /// </summary> Origin, /// <summary> /// 反向到 【反向样品】结束位,SyncRunTo(SamplePos_Backw.End) /// </summary> SampleBackw_ready, /// <summary> /// 【反向样品】取样,SyncRunTo(SamplePos_Backw.Begin) /// </summary> SampleBackw, /// <summary> /// 【反向样品】完成后,正向回到边界,SyncRunToLC(ScanRangeLC.Begin) /// </summary> SampleBackw_complete, /// <summary> /// 【正向扫描】, SyncRunAtLC(pos2_begin,pos2_end, ScanRangeLC.End) /// </summary> ScanForw, /// <summary> /// 【正向扫描】等待grid完成,SyncWait(ms) /// </summary> ScanForw_waitGridComplete, /// <summary> /// 正向到 【正向样品】开始位,SyncRunTo(SamplePos_Forw.Begin) /// </summary> SampleForw_ready, /// <summary> /// 【正向样品】取样,SyncRunTo(SamplePos_Forw.End) /// </summary> SampleForw, /// <summary> /// 【正向样品】完成后,反向回到边界,SyncRunToLC(ScanRangeLC.End) /// </summary> SampleForw_complete, /// <summary> /// 【反向扫描】, SyncRunAtLC(pos2_begin,pos2_end, ScanRangeLC.Begin) /// </summary> ScanBackw, /// <summary> /// 【反向扫描】等待grid完成,SyncWait(ms) /// </summary> ScanBackw_waitGridComplete, } public OrderType Order; /// <summary> /// 主轴脉冲 /// </summary> public Int32 Marker; public override string ToString() { return Order.ToString() + "|" + Marker.ToString(); } } bool IsSample() { if ((sampleCount >= SampleInterval) && (SampleInterval > 0)) return true; return false; } /// <summary> /// 每次调用创造1条同步指令!!!! /// </summary> public void CreateSyncOrder() { SyncOrder order = new SyncOrder(); switch (LastOrder.Order) { case SyncOrder.OrderType.Init: { order.Order = SyncOrder.OrderType.First; } break; case SyncOrder.OrderType.ScanBackw: { order.Order = SyncOrder.OrderType.ScanBackw_waitGridComplete; } break; case SyncOrder.OrderType.First: case SyncOrder.OrderType.ScanBackw_waitGridComplete: { if (sampleCount < SampleInterval) sampleCount++; if (IsSample()) order.Order = SyncOrder.OrderType.Origin; else order.Order = SyncOrder.OrderType.ScanForw; }break; case SyncOrder.OrderType.ScanForw: { order.Order = SyncOrder.OrderType.ScanForw_waitGridComplete; }break; case SyncOrder.OrderType.ScanForw_waitGridComplete: { if (IsSample()) { sampleCount = 0; order.Order = SyncOrder.OrderType.SampleForw_ready; } else order.Order = SyncOrder.OrderType.ScanBackw; }break; case SyncOrder.OrderType.Origin: order.Order = SyncOrder.OrderType.SampleBackw_ready; break; case SyncOrder.OrderType.SampleBackw_ready: order.Order = SyncOrder.OrderType.SampleBackw; break; case SyncOrder.OrderType.SampleBackw: order.Order = SyncOrder.OrderType.SampleBackw_complete; break; case SyncOrder.OrderType.SampleBackw_complete: order.Order = SyncOrder.OrderType.ScanForw; break; case SyncOrder.OrderType.SampleForw_ready: order.Order = SyncOrder.OrderType.SampleForw; break; case SyncOrder.OrderType.SampleForw: order.Order = SyncOrder.OrderType.SampleForw_complete; break; case SyncOrder.OrderType.SampleForw_complete: order.Order = SyncOrder.OrderType.ScanBackw; break; } DoOrder(ref order); syncOrderList.Add(order); LastOrder = order; } void DoOrder(ref SyncOrder order) { switch (order.Order) { case SyncOrder.OrderType.First: { order.Marker = last_pos2; mFlyAD.SyncRunAtLC( last_pos2, last_pos2 + Pos2ConsumeOfScan, 0, true, order.Marker); last_pos2 += Pos2ConsumeOfScan; } break; case SyncOrder.OrderType.Origin: { order.Marker = last_pos2; mFlyAD.SyncOrigin(order.Marker); } break; case SyncOrder.OrderType.SampleBackw_ready: { order.Marker = last_pos2 + 1; mFlyAD.SyncRunTo(SamplePos_Backw.End, Velocity, false, order.Marker); } break; case SyncOrder.OrderType.SampleBackw: { order.Marker = (last_pos2 + 2); mFlyAD.SyncRunTo(SamplePos_Backw.Begin, VSample, true, order.Marker); } break; case SyncOrder.OrderType.SampleBackw_complete: { order.Marker = (last_pos2 + 3); mFlyAD.SyncRunToLC(0, Velocity, false, order.Marker); last_pos2 += Pos2ConsumeOfSampleBackw; } break; case SyncOrder.OrderType.ScanBackw: { order.Marker = last_pos2; mFlyAD.SyncRunAtLC( last_pos2, last_pos2 + Pos2ConsumeOfScan, 0, true, order.Marker); last_pos2 += Pos2ConsumeOfScan; } break; case SyncOrder.OrderType.ScanBackw_waitGridComplete: { order.Marker = last_pos2; mFlyAD.SyncRunAtLC( last_pos2, last_pos2 + Pos2ConsumeOfWaitNextScan, 0, false, order.Marker); last_pos2 += Pos2ConsumeOfWaitNextScan; }break; case SyncOrder.OrderType.SampleForw_ready: { order.Marker = last_pos2; mFlyAD.SyncRunTo(SamplePos_Forw.Begin, Velocity, false, order.Marker); } break; case SyncOrder.OrderType.SampleForw: { order.Marker = (last_pos2 + 1); mFlyAD.SyncRunTo(SamplePos_Forw.End, VSample, true, order.Marker); } break; case SyncOrder.OrderType.SampleForw_complete: { order.Marker = (last_pos2 + 2); mFlyAD.SyncRunToLC(ScanWidth - 1, Velocity, false, order.Marker); last_pos2 += Pos2ConsumeOfSampleForw; } break; case SyncOrder.OrderType.ScanForw: { order.Marker = last_pos2; mFlyAD.SyncRunAtLC( last_pos2, last_pos2 + Pos2ConsumeOfScan, ScanWidth - 1, true, order.Marker); last_pos2 += Pos2ConsumeOfScan; } break; case SyncOrder.OrderType.ScanForw_waitGridComplete: { order.Marker = last_pos2; mFlyAD.SyncRunAtLC( last_pos2, last_pos2 + Pos2ConsumeOfWaitNextScan, ScanWidth - 1, false, order.Marker); last_pos2 += Pos2ConsumeOfWaitNextScan; }break; } } /// <summary> /// 保证列表有10条以上,少于10条,每次添加10条 /// </summary> void OnPoll_SyncOrder() { if (syncOrderList.Count() < 6) { for (int i = 0; i < 6; i++) CreateSyncOrder(); } } #endregion public event PropertyChangedEventHandler PropertyChanged; public void Save() { Misc.SaveToXmlHepler.Save("autosync.xml", this); } public void Load() { Misc.SaveToXmlHepler.Load("autosync.xml", this); } public string[] GetSavePropertyNames() { return new string[]{ "ScanBegin", "ScanWidth", "Mmpp1", "Mmpp2", "SamplePos_Backw", "SamplePos_Backw", "SamplePos_Forw", "SamplePos_Forw", "Velocity", "VSample", "FilmVelocity", "SampleInterval", "TestInterval", "ConsumeOfSampleBackw", "ConsumeOfSampleForw", "ConsumeOfScan", "ConsumeOfGrid" }; } } }