using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.ComponentModel; using System.Xml.Serialization; using Misc; using FObjBase; using FlyADBase; using FLY.Thick.Base.IService; using PropertyChanged; using Newtonsoft.Json; using static Misc.ReverserInfo; using FLY.Thick.Base.Common; using AutoMapper; namespace FLY.Thick.Base.Server { public class GSample : IGetSampleService { /// <summary> /// 报警测试 /// </summary> #region IGetSampleService 接口 /// <summary> /// 参数:使能 /// </summary> public bool Enable { get; set; } = true; /// <summary> /// 参数:速度 /// </summary> public UInt32 Velocity { get; set; } = 500; /// <summary> /// 参数:样品点范围 单位:脉冲 /// 样品点直径为 Range*2 /// </summary> public int SampleRange { get; set; } = 100; /// <summary> /// 滤波窗口,单位 min /// </summary> public int Window { get; set; } /// <summary> /// 使用%方式检查异常 /// </summary> public bool IsCheckByPercent { get; set; } = true; /// <summary> /// 异常比例 单位不是% /// </summary> public double ErrPercent { get; set; } = 0.02; /// <summary> /// 异常值 /// </summary> public int ErrValue { get; set; } = 200; /// <summary> /// 当前 样品X_AD/样品0_AD 与 上一次比较 差异比例 单位不是% /// </summary> public double CrossErrPercent { get; set; } = 0.02; /// <summary> /// 样品 /// </summary> public SampleCell[] Samples { get; set; } /// <summary> /// 参数:特征查找范围 单位:脉冲 /// 取样范围 会扩大为 -SearchRange ~ +SearchRange /// </summary> public int SearchRange { get; set; } = 100; /// <summary> /// 特征 相识度 0 正, 1 反 /// </summary> public SampleFeature[] Features { get; set; } #endregion TempFilter2[] tempFilters; DIRECTION[] directions; /// <summary> /// 采集状态 /// </summary> public SampleState[] states; public enum SampleState { /// <summary> /// 准备采集 /// </summary> ReadyToCapture, /// <summary> /// 采集成功 /// </summary> Ok, /// <summary> /// 采集失败 /// </summary> IsFailure } IGageInfoService mGageInfo; OBJComponents.Server.WarningSystem2 warningSystem; IFlyADClientAdv flyad; SampleData last_sampleData; public event SampleChangedEventHandler SampleChangedEvent; private string param_path; public GSample():this("gsample.json") { } public GSample(string param_path) { this.param_path = param_path; Samples = new SampleCell[3]; Features = new SampleFeature[2]; tempFilters = new TempFilter2[Samples.Count()]; directions = new DIRECTION[Samples.Count()]; states = new SampleState[Samples.Count()]; for (int i = 0; i < Samples.Count(); i++) tempFilters[i] = new TempFilter2(); Enable = true; SampleRange = 100;//样品范围 Velocity = 200;//取样速度 Window = 3;//移动窗口 IsCheckByPercent = true; ErrPercent = 2; ErrValue = 200; Samples[0] = new SampleCell() { Enable = true, Position = 200, OrgAD = 50300 }; directions[0] = DIRECTION.BACKWARD; Samples[1] = new SampleCell() { Enable = false, Position = 0, OrgAD = 0 }; directions[1] = DIRECTION.FORWARD; Samples[2] = new SampleCell() { Enable = false, Position = 0, OrgAD = 0 }; directions[2] = DIRECTION.FORWARD; SearchRange = 100; Features[0] = new SampleFeature() { StartPos = 0, EndPos = 0 }; Features[1] = new SampleFeature() { StartPos = 2000, EndPos = 2100 }; Load(); } public virtual void Init( IFlyADClientAdv flyad, IGageInfoService gageInfo, OBJComponents.Server.WarningSystem2 warningSystem) { this.flyad = flyad; this.mGageInfo = gageInfo; this.warningSystem = warningSystem; } public bool Load() { return GSampleJsonDb.Load(this, param_path); } /// <summary> /// 保存 /// </summary> public bool Save() { return GSampleJsonDb.Save(this, param_path); } /// <summary> /// 1.判断样品取样方向 /// 2.复位间隔计数 /// 3.复位所有样品取样状态 /// </summary> /// <param name="scanbegin"></param> /// <param name="scanend"></param> public virtual void Reset(int scanbegin, int scanend) { last_sampleData = null; for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; int pos = sample.Position; directions[i] = (Math.Abs(pos - scanbegin) < Math.Abs(pos - scanend)) ? Misc.DIRECTION.BACKWARD : Misc.DIRECTION.FORWARD; sample.AD = -1; states[i] = SampleState.ReadyToCapture; tempFilters[i].Reset(); } } /// <summary> /// 进入采集周期,设置全部采样点 为准备采集状态 /// </summary> public void ReadyToCapture() { for (int i = 0; i < Samples.Count(); i++) { states[i] = SampleState.ReadyToCapture; } } /// <summary> /// 获取样品测量范围 /// </summary> /// <param name="direction"></param> /// <returns>当没有样品,也没有特征,返回null</returns> public Range GetSampleRange(Misc.DIRECTION direction) { int b, e; if (GetSampleRange(direction, out b, out e)) { return new Range() { Begin = b, End = e }; } else { return new Range(); } } /// <summary> /// 获取样品测量范围 /// </summary> /// <param name="direction"></param> /// <param name="start"></param> /// <param name="end"></param> /// <returns>当没有样品,也没有特征,返回False</returns> public virtual bool GetSampleRange(Misc.DIRECTION direction, out int start,out int end) { start = 0; end = 0; bool valid = false; int b=0, e=0; int r_idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1; if (Features[r_idx].Enable) { b = Features[r_idx].StartPos; e = Features[r_idx].EndPos; valid = true; } for(int i=0;i< Samples.Count(); i++) { SampleCell sample = Samples[i]; if ((sample.Enable) && (directions[i] == direction)) { int _b = sample.Position - SampleRange; int _e = sample.Position + SampleRange; if (!valid) { b = _b; e = _e; valid = true; } else { if (b > _b) b = _b; if (e < _e) e = _e; } } } if (valid) { if (Features[r_idx].Enable) { b -= SearchRange; e += SearchRange; } start = b; end = e; } return valid; } /// <summary> /// 检测参数合法性 /// </summary> /// <returns></returns> protected bool CheckParamIsValid() { //样品位置 要在 范围 foreach(SampleCell sample in Samples) { if (sample.Enable) { if(((sample.Position - SampleRange)<0) || ((sample.Position+SampleRange)>= flyad.PosLen)) return false; } } foreach(SampleFeature feature in Features) { if (feature.Enable) { if(((feature.StartPos - SearchRange)<0) || ((feature.EndPos+SearchRange)>= flyad.PosLen)) return false; } } return true; } /// <summary> /// 样品取样,推送给thickm, 参数一定合法,不用检测 /// </summary> /// <param name="direction"></param> public void Do(Misc.DIRECTION direction) { int[] dat; flyad.GetGrid(direction,0, flyad.PosLen/flyad.PosOfGrid, out dat); Do(direction, 0, dat); } bool CalGridOffset(SampleFeature psr, Misc.DIRECTION direction,int grid_start, int[] buf, out int grid_offset) { grid_offset = 0; //检测机架数据,当为空时,不能作相识性计算 if (!mGageInfo.DataOK) return true; int pos1 = psr.StartPos; int pos2 = psr.EndPos; int posOfGrid = flyad.PosOfGrid; int grid1 = pos1 / posOfGrid; int grid2 = pos2 / posOfGrid; int grid_searchtol = SearchRange / posOfGrid; //获取相同方向的机架信息数据 int index = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1; int[] gagedata = null; mGageInfo.GetGageInfo(index, (asyncContext, retData) => { var reponse = retData as GetGageInfoResponse; gagedata = reponse.Data; }, this); //合格,查找最大相识性的点 int grid_b = grid1 - grid_searchtol; int grid_e = grid2 + grid_searchtol; int grid_len = grid_e - grid_b + 1; int[] scanData = new int[grid_len];//这个方向的需要 特征查找范围内的数据,不用考虑 buf 不够 grid_len Array.Copy(buf, grid_b - grid_start, scanData, 0, grid_len); double max_relvency = 0; int max_grid_offset = 0; for (int i = 0; i < grid_searchtol * 2; i++) { double r = Misc.MyMath.Correl(gagedata, grid1, scanData, i, grid2 - grid1 + 1); if (r > max_relvency) { max_relvency = r; max_grid_offset = i - grid_searchtol; } } psr.MaxRelevancy = max_relvency; psr.MaxOffset = max_grid_offset * posOfGrid; if (max_relvency > 0.8) //相识度必须高于0.8 { grid_offset = max_grid_offset;//位置修正 return true; } else { //相似性太低,取样失败!!! return false; } } /// <summary> /// 设置这个方向 全部样品为失败 /// </summary> /// <param name="direction"></param> void SetFailure(Misc.DIRECTION direction) { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; var tempre = tempFilters[i]; if ((directions[i] == direction) && (sample.Enable)) { tempre.Reset(); states[i] = SampleState.IsFailure; } } } /// <summary> /// 设置全部样品为失败 /// </summary> void SetFailure() { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; var tempre = tempFilters[i]; if (sample.Enable) { tempre.Reset(); states[i] = SampleState.IsFailure; } } } public bool HasSampleAd() { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if (sample.Enable) { if (sample.AD <= 0) return false; } } return true; } enum GetSampleAdResult { OK, /// <summary> /// 没有采集数据 /// </summary> Invalid, /// <summary> /// AD值为0 /// </summary> Zero, /// <summary> /// 滤波 异常 /// </summary> FilterErr } GetSampleAdResult GetSampleAd(SampleCell sample, TempFilter2 tempre, int grid_start, int[] buf, int grid_offset, out int ad) { //获取grid 数据 int posOfGrid = flyad.PosOfGrid; int grid = sample.Position / posOfGrid + grid_offset; int grid_range = SampleRange / posOfGrid; int grid1 = grid - grid_range; int grid_len = grid_range * 2; int grid_b = grid1 - grid_start; int grid_e = grid_b + grid_len - 1; //计算平均值 ad = Misc.MyMath.Avg(buf, grid_b, grid_e); if (!Misc.MyBase.ISVALIDATA(ad))//AD无效。。。异常,复位 { ad = -1; return GetSampleAdResult.Invalid; } else { int orgAd = ad; ad = tempre.CalSampleAD(orgAd, TimeSpan.FromMinutes(Window));//ad 滤波 double diff = Math.Abs(orgAd - ad); if (IsCheckByPercent) { if (diff == 0) { } else if (ad == 0) { ad = -1; return GetSampleAdResult.Zero; } else if (diff / ad >= ErrPercent) //大于2% 异常 { return GetSampleAdResult.FilterErr; } } else { if (diff >= ErrValue) { return GetSampleAdResult.FilterErr; } } } return GetSampleAdResult.OK; } GetSampleAdResult SetSampleAds(Misc.DIRECTION direction, int grid_start, int[] buf, int grid_offset) { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; var tempre = tempFilters[i]; if ((directions[i] == direction) && (sample.Enable)) { //获取滤波后的AD值 var ret = GetSampleAd(sample, tempre, grid_start, buf, grid_offset, out int ad); if(ret == GetSampleAdResult.OK) { sample.AD = ad; states[i] = SampleState.Ok; } else { return ret; } } } return GetSampleAdResult.OK; } /// <summary> /// 样品取样,推送给thickm, 参数一定合法,不用检测 /// </summary> /// <param name="direction">方向</param> /// <param name="grid_start">开始grid序号</param> /// <param name="buf">grid数据</param> public virtual void Do(Misc.DIRECTION direction, int grid_start, int[] buf) { if (CheckParamIsValid() == false) { this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:参数异常"); return; } int grid_offset = 0;//经过相识性计算后的偏移量 //数据在 gsample -> gagedata[0] SampleFeature psr = Features[(direction == Misc.DIRECTION.FORWARD) ? 0 : 1]; //这个方向的特征查找使能了,位置修正!!!!!! if (psr.Enable) { //通过特征 计算样品偏移, //当没有机架信息,会返回 true //成功 计算出偏移 返回 true //相关性太低,返回 false if (!CalGridOffset(psr, direction, grid_start, buf, out grid_offset)) { //有问题,全部复位 SetFailure(direction); this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:特征查找失败"); goto _end; } } //获取这个方向全部样品AD var ret = SetSampleAds(direction, grid_start, buf, grid_offset); switch (ret) { case GetSampleAdResult.Zero: { this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:AD值为0"); } break; case GetSampleAdResult.Invalid: { this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:没有采集数据"); } break; case GetSampleAdResult.FilterErr: { this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:滤波数据与原始数据差异{ErrPercent:P0}"); } break; } if (ret != GetSampleAdResult.OK) { //有问题,全部复位 SetFailure(direction); } if (OK()) { //全部都采集完成 if (!IsErr())//没有异常 { //备份 last_sampleData = new SampleData(); last_sampleData.Time = DateTime.Now; last_sampleData.SampleADs = new int[Samples.Count()]; for (int i = 0; i < Samples.Count(); i++) { last_sampleData.SampleADs[i] = Samples[i].AD; } } } _end: SampleChangedEvent?.Invoke(this); } bool IsErr() { if (last_sampleData == null) return false; if (IsPercentErr()) { //历史比较有问题 this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:样品AD与上次大于{ErrPercent:P0}变化"); //设置全部都是异常,重新测量 SetFailure(); last_sampleData = null; return true; } if (IsCrossErr()) { //交叉比较有问题 this.warningSystem.Update( ERRNOs.Instance.SAMPLE_ERRNO_Failure.Code, $"{ERRNOs.Instance.SAMPLE_ERRNO_Failure.Descrption}:样品比值与上次大于{CrossErrPercent:P0}变化"); //设置全部都是异常,重新测量 SetFailure(); last_sampleData = null; return true; } return false; } /// <summary> /// 检测是否成功 /// </summary> /// <returns></returns> public virtual bool OK() { return !IsFailure(); } /// <summary> /// 刚才报警了,采样失败 /// </summary> /// <returns></returns> public bool IsFailure() { if (Enable) { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if (sample.Enable) { if (states[i] != SampleState.Ok) return true; } } } return false; } /// <summary> /// 全部采集完成,与上次采集比较 大于2% 异常 /// </summary> /// <returns></returns> bool IsPercentErr() { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if (sample.Enable) { int ad = sample.AD; if (ad <= 0)//写错程序 break; int lastAd = last_sampleData.SampleADs[i]; double diff = Math.Abs(ad - lastAd); if (diff / ad > ErrPercent) //大于2% 异常 { return true; } } } return false; } /// <summary> /// 全部采集完成,再做 交叉比较 /// </summary> /// <returns></returns> bool IsCrossErr() { //交叉比较 for (int i = 1; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if (sample.Enable) { double p = 1.0 * sample.AD / Samples[0].AD; if (p <= 0)//写错程序 break; int lastAdi = last_sampleData.SampleADs[i]; int lastAd0 = last_sampleData.SampleADs[0]; if (lastAd0 <= 0)//写错程序 break; double last_p = 1.0 * lastAdi / lastAd0; double diff = Math.Abs(last_p - p); if (diff / p > CrossErrPercent) //大于2% 异常 { return true; } } } return false; } /// <summary> /// 获取N秒的数据, /// </summary> /// <param name="idx">序号</param> /// <param name="buf">数据</param> /// <returns></returns> public void DoFix(int idx, int[] buf) { if (idx >= Samples.Count()) return; else if (idx < 0) return; int avg = Misc.MyMath.Avg(buf); SampleCell sample = Samples[idx]; sample.AD = avg; SampleChangedEvent?.Invoke(this); } public event PropertyChangedEventHandler PropertyChanged; public void Apply() { //通知上层 Save(); } /// <summary> /// 返回 List(List(TempFilterData)) /// </summary> /// <param name="asyncCB"></param> /// <param name="asyncContext"></param> public void GetTempFilterDatas(AsyncCBHandler asyncDelegate, object asyncContext) { List<List<TempFilterData>> ret = new List<List<TempFilterData>>(); for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if (sample.Enable) ret.Add(tempFilters[i].GetDatas()); } asyncDelegate(asyncContext, ret); } } public class SampleData : INotifyPropertyChanged { public DateTime Time { get; set; } /// <summary> /// 样品采集AD值 /// </summary> public int[] SampleADs { get; set; } public event PropertyChangedEventHandler PropertyChanged; } public delegate void SampleChangedEventHandler(GSample gsample); public class GSampleJsonDb { static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c => { c.CreateMap<GSample, GSampleJsonDb>() .ForMember(s => s.Samples, opt => { opt.MapFrom(s => Map_SampleCell2SampleCellParam(s.Samples)); }).ForMember(s => s.Features, opt => { opt.MapFrom(s => Map_SampleFeature2SampleFeatureParam(s.Features)); }) .ReverseMap() .ForMember(s => s.Samples, opt => { opt.MapFrom(s => Map_SampleCellParam2SampleCell(s.Samples)); }) .ForMember(s => s.Features, opt => { opt.MapFrom(s => Map_SampleFeatureParam2SampleFeature(s.Features)); }); })); static SampleCell[] Map_SampleCellParam2SampleCell(SampleCellParam[] sampleCellParams) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleCellParams); return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleCell[]>(json); } static SampleCellParam[] Map_SampleCell2SampleCellParam(SampleCell[] sampleCells) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleCells); return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleCellParam[]>(json); } static SampleFeature[] Map_SampleFeatureParam2SampleFeature(SampleFeatureParam[] sampleFeatureParams) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleFeatureParams); return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleFeature[]>(json); } static SampleFeatureParam[] Map_SampleFeature2SampleFeatureParam(SampleFeature[] sampleFeatures) { string json = Newtonsoft.Json.JsonConvert.SerializeObject(sampleFeatures); return Newtonsoft.Json.JsonConvert.DeserializeObject<SampleFeatureParam[]>(json); } public static bool Load(GSample src, string filePath) { try { if (File.Exists(filePath)) { string json = File.ReadAllText(filePath); var p = JsonConvert.DeserializeObject<GSampleJsonDb>(json); Mapper.Map(p, src); return true; } } catch { //异常,没有json 解码失败 } return false; } public static bool Save(GSample src, string filePath) { var p = Mapper.Map<GSampleJsonDb>(src); try { File.WriteAllText(filePath, JsonConvert.SerializeObject(p, Formatting.Indented)); return true; } catch { //异常,没有json 编码失败 } return false; } public bool Enable { get; set; } = true; public int Window { get; set; } public int SampleRange { get; set; } = 100; public bool IsCheckByPercent { get; set; } = true; public double ErrPercent { get; set; } = 2; public int ErrValue { get; set; } = 200; public double CrossErrPercent { get; set; } = 0.02; public SampleCellParam[] Samples { get; set; } public int SearchRange { get; set; } = 100; public SampleFeatureParam[] Features { get; set; } } }