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 int ErrIntervalMin { get; set; } = 30; /// <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; IGageInfoService mGageInfo; OBJComponents.Server.WarningSystem2 warningSystem; FlyAD7 flyad; 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()]; 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( FlyAD7 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) { 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; tempFilters[i].Reset(); } } /// <summary> /// /// </summary> public void Reset() { for (int i = 0; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; sample.AD = -1; tempFilters[i].Reset(); } } /// <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(); sample.IsFailure = true; } } } enum GetSampleAdResult { OK, /// <summary> /// 没有采集数据 /// </summary> Invalid, /// <summary> /// AD值为0 /// </summary> Zero, /// <summary> /// 滤波 异常 /// </summary> FilterErr, /// <summary> /// 与上一个比较,变化太大 /// </summary> TimeCompareErr } 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; } } //获取以前的Ad int lastAd = tempre.GetLastAd(GetErrIntervalTime); if (lastAd != -1) { //找到了 diff = Math.Abs(ad - lastAd); if (diff / ad >= ErrPercent) //大于2% 异常 { return GetSampleAdResult.TimeCompareErr; } } } return GetSampleAdResult.OK; } DateTime GetErrIntervalTime => DateTime.Now - TimeSpan.FromMinutes(ErrIntervalMin); 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; sample.IsFailure = false; } else { return ret; } } } if (Samples[0].AD > 0 && Samples[0].IsFailure == false) { //交叉比较 for (int i = 1; i < Samples.Count(); i++) { SampleCell sample = Samples[i]; if ((directions[i] == direction) && (sample.Enable)) { double p = 1.0 * Samples[i].AD / Samples[0].AD; if (p <= 0) break; int lastAdi = tempFilters[i].GetLastAd(GetErrIntervalTime); int lastAd0 = tempFilters[0].GetLastAd(GetErrIntervalTime); if (lastAd0 == 0) break; double last_p = 1.0 * lastAdi / lastAd0; double diff = Math.Abs(last_p - p); if (diff / p >= CrossErrPercent) //大于2% 异常 { //TODO //TODO return GetSampleAdResult.TimeCompareErr; } } } } 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}:滤波异常"); } break; } if(ret != GetSampleAdResult.OK) { //有问题,全部复位 SetFailure(direction); } _end: SampleChangedEvent?.Invoke(this); } /// <summary> /// 检测是否成功 /// </summary> /// <returns></returns> public virtual bool OK() { if (Enable) { foreach (SampleCell sample in Samples) { if (sample.Enable) { if ((sample.AD <= 0)) return false; } } } return true; } /// <summary> /// 刚才报警了,采样失败 /// </summary> /// <returns></returns> public bool IsFailure() { if (Enable) { foreach (SampleCell sample in Samples) { if (sample.Enable) { if ((sample.AD <= 0) || (sample.IsFailure)) 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 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 int ErrIntervalMin { get; set; } = 30; public SampleCellParam[] Samples { get; set; } public int SearchRange { get; set; } = 100; public SampleFeatureParam[] Features { get; set; } } }