using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using Misc;
using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using FObjBase;
using AutoMapper;
using System.IO;
using Newtonsoft.Json;
namespace FLY.Thick.Base.Server
{
///
/// 边界查找
///
public class BorderSearch : IBorderSearchService, INotifyPropertyChanged
{
///
/// 报警测试
///
//public static FLY.OBJComponents.Common.ERRNO BORDER_ERRNO_NOFILM = new FLY.OBJComponents.Common.ERRNO() { Code = 50, Descrption = "边界异常,没有被测物" };
#region 延时执行,Markno
public const int MARKNO_SAVE = 1;
#endregion
#region 属性,成员变量的代理
///
/// 启动与否
///
public bool Enable { get; set; }
///
/// 单一材料,找空气与膜均值的 中间位置
///
public bool IsOneMaterial { get; set; }
///
/// 边界拐点检测,找到的边界更加精确
///
public bool IsBreakDetect { get; set; } = true;
///
/// 有效范围
///
public RangeStruct Valid { get; set; } = RangeStruct.InvalidValue;
///
/// 边界
///
public Range Border_Forw { get { return borders[0].value; } }
///
/// 边界
///
public Range Border_Backw { get { return borders[1].value; } }
///
/// 膜范围
///
public RangeStruct Border { get; set; } = RangeStruct.InvalidValue;
///
/// 当前膜宽,测量出来的,单位是 脉冲
///
public int Width { get; protected set; }
///
/// 膜中间位置 单位 脉冲
///
public int Mid { get; protected set; }
///
/// 手动设置温修AD值
///
public bool TempADBySet { get; set; }
///
/// 温修AD值
///
public int CurrTempAD { get; set; } = 50000;
///
/// 温修AD值
///
public int TempAD { get; set; } = 50000;
///
/// AD超过了范围, 就认为开始找到边界
///
public int TempRange { get; set; } = 1000;
///
/// 温修范围是温修的百分比
///
public bool IsTempRangeByPercent { get; set; } = true;
///
/// 温修范围百分比
///
public double TempRangePercent { get; set; } = 0.02;
///
/// 有滤波器,只有非空的连续N个pos以上,才开始算边界开始
///
public int N { get; set; }
///
/// 探头直径,单位脉冲, 膜宽 = 边界范围 - 探头直径
///
public int SensorWidth { get; set; }
///
/// 找到边界后,再往内缩N2个脉冲,防止边界找得不准的问题。
///
public int N2 { get; set; }
///
/// 记录两个边界以后扫描,以它们再外扩N3个脉冲,作为扫描范围
///
public int N3 { get; set; }
///
/// 数据更新时间
///
public DateTime UpdateTime { get; protected set; }
///
/// 产品宽度,设置值 单位:脉冲
/// 当膜超过范围(没有经过阀值),就使用它,猜边界在哪
///
public int ProductWidth { get; protected set; }
#endregion
///
/// 边界
///
protected class BorderWithOk
{
///
/// 边界值
///
public Range value = new Range();
///
/// 开始边界100%可靠
///
public bool isBeginOk;
///
/// 结束边界100%可靠
///
public bool isEndOk;
///
/// 真实可靠的边界值
///
public Range real = new Range();
public void UpdateReal(int sensorWidth, int expect, int mid, bool isBeginOk, bool isEndOk)
{
if (value.IsValid)
{
real.Begin = value.Begin + sensorWidth / 2;
real.End = value.End - sensorWidth / 2;
//修正!!!
if (real.Width < expect)
{
if (isBeginOk && isEndOk)
{
}
else if (isBeginOk)
{
real.End = real.Begin + expect;
}
else if (isEndOk)
{
real.Begin = real.End - expect;
}
else //开始边界与结束边界都是假的
{
//居中处理
real.Begin = mid - expect / 2;
real.End = real.Begin + expect;
}
}
}
}
public void UpdateReal(int sensorWidth, int expect, int mid)
{
UpdateReal(sensorWidth, expect, mid, isBeginOk, isEndOk);
}
public void UpdateReal(int sensorWidth)
{
if (value.IsValid)
{
real.Begin = value.Begin + sensorWidth / 2;
real.End = value.End - sensorWidth / 2;
}
}
}
///
/// 正反向边界数据
///
protected BorderWithOk[] borders = new BorderWithOk[2] { new BorderWithOk(), new BorderWithOk() };
OBJComponents.Server.WarningSystem2 warningSystem;
///
/// 用于调试, 进入到边界查找的数据
///
protected BorderSearchGetViewReponse getViewReponse;
private string param_path = "bordersearch.json";
public BorderSearch()
{
Default();
this.PropertyChanged += new PropertyChangedEventHandler(BorderSearch_PropertyChanged);
}
public BorderSearch(string param_path)
{
if (!string.IsNullOrEmpty(param_path))
this.param_path = param_path;
Default();
this.PropertyChanged += new PropertyChangedEventHandler(BorderSearch_PropertyChanged);
this.Border_Forw.PropertyChanged += (s, e) => {
NotifyPropertyChanged(nameof(Border_Forw));
};
this.Border_Backw.PropertyChanged += (s, e) => {
NotifyPropertyChanged(nameof(Border_Backw));
};
if (!Load()) {
Save();
}
}
public void Init(OBJComponents.Server.WarningSystem2 warningSystem)
{
this.warningSystem = warningSystem;
}
void Default()
{
Enable = true;
Valid = new RangeStruct(1200,7400);
TempAD = -1;
N = 20;
N2 = 400;
N3 = 1000;
SensorWidth = 250;
TempRange = 500;
TempRangePercent = 500.0 / 50000;
IsTempRangeByPercent = true;
Reset();
}
void BorderSearch_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CurrTempAD))
{
if (!TempADBySet)
TempAD = CurrTempAD;
}
else if (e.PropertyName == nameof(TempAD))
{
if (IsTempRangeByPercent)
{
TempRange = (int)(TempAD * TempRangePercent);
}
}
else if ((e.PropertyName == nameof(TempRangePercent)) || (e.PropertyName == nameof(IsTempRangeByPercent)))
{
if (IsTempRangeByPercent)
{
TempRange = (int)(TempAD * TempRangePercent);
}
}
}
public void Reset()
{
Border_Backw.Reset();
Border_Forw.Reset();
Width = 0;
Border = RangeStruct.InvalidValue;
Mid = Valid.Mid;
}
public void SetProductWidth(int productWidth) {
ProductWidth = productWidth;
}
///
/// 获取扫描范围
///
///
public Range GetScanRange()
{
GetScanRange(out int b, out int e);
return new Range() { Begin = b, End = e };
}
///
/// 获取扫描范围
///
///
///
public void GetScanRange(out int posBegin, out int posEnd)
{
int min, max;
if (borders.All(b => b.value.IsValid))
{
min = borders.Min(b => b.value.Begin) - N3;
max = borders.Max(b => b.value.End) + N3;
}
else if (borders[0].value.IsValid)
{
min = borders[0].value.Begin - N3;
max = borders[0].value.End + N3;
}
else if (borders[1].value.IsValid)
{
min = borders[1].value.Begin - N3;
max = borders[1].value.End + N3;
}
else
{
//都不能找到边界
min = Valid.Begin;
max = Valid.End;
}
//范围限制
posBegin = Math.Max(min, Valid.Begin);
posEnd = Math.Min(max, Valid.End);
}
///
/// 初略操作,超过范围 就是边界
///
/// 全部数据
/// 有效范围开始
/// 有效范围结束
/// 输出的开始边界
/// 输出的结束边界
/// 开始边界经过阀值线
/// 结束边界经过阀值线
///
protected bool FindFilm_Threshold(
int[] dat,
int validBegin_grid,
int validEnd_grid,
out int borderBegin_grid,
out int borderEnd_grid,
out bool isBorderBeginOk,
out bool isBorderEndOk)
{
borderBegin_grid = -1;
borderEnd_grid = -1;
isBorderBeginOk = false;
isBorderEndOk = false;
int mid_grid = (validBegin_grid + validEnd_grid) / 2;
//从中间开始向左边找
//必须先找到膜
for (int i = mid_grid; i >= validBegin_grid; i--)
{
if (i >= 0 && i < dat.Length)
{
if (Misc.MyBase.ISVALIDATA(dat[i]))
{
if (Math.Abs(TempAD - dat[i]) > TempRange)
{
//膜
borderBegin_grid = i;
}
else
{
//空气
if (borderBegin_grid != -1)
{
isBorderBeginOk = true;
//之前有膜
break;
}
}
}
}
else if (i < 0)
{
break;
}
}
if (borderBegin_grid == -1)//没有膜
{
//从左边开始找
//找左边界 ,找阀值点
for (int i = mid_grid; i <= validEnd_grid; i++)
{
if (i >= 0 && i < dat.Length)
{
if (Misc.MyBase.ISVALIDATA(dat[i]))
{
if (Math.Abs(TempAD - dat[i]) > TempRange)
{
//膜
borderBegin_grid = i;
isBorderBeginOk = true;
break;
}
}
}
}
if (borderBegin_grid == -1)//没有膜
return false;
}
//从左边界开始找
for (int i = borderBegin_grid; i <= validEnd_grid; i++)
{
if (i >= 0 && i < dat.Length)
{
if (Misc.MyBase.ISVALIDATA(dat[i]))
{
if (Math.Abs(TempAD - dat[i]) > TempRange)
{
//膜
borderEnd_grid = i;
}
else
{
//空气
if (borderEnd_grid != -1)
{
isBorderEndOk = true;
//之前有膜
break;
}
}
}
}
}
if (borderEnd_grid == -1)//没有膜
return false;
return true;
}
///
/// 精细操作,再向外找 折点
///
///
/// 探头直径
/// 滤波
///
///
///
bool FindFilm_Break(int[] dat, int sensor_grid, int filter_grid, ref int borderbegin_grid, ref int borderend_grid)
{
//当前肯定在斜波上
//边界肯定在 粗边界以外 探头半径 内
//int N4 = SensorWidth / posOfGrid;
if (sensor_grid <= 2)
{
//不用找,传感器直径非常小,已经很准确了
return true;
}
//int num = N / posOfGrid / 2;//滤波半径
int num = filter_grid / 2;
if (num < 1) num = 1;
int[] filterdats = new int[dat.Length];//数据滤波
for (int i = 0; i < dat.Length; i++)
{
int b = i - num;
int e = i + num;
if (b < 0)
{
b = 0;
e = b + num * 2;
}
else if (e >= (dat.Length - 1))
{
e = dat.Length - 1;
b = e - num * 2;
}
filterdats[i] = Misc.MyMath.Avg(dat, b, e);
}
int bg = borderbegin_grid;
int eg = borderend_grid;
int[] breakdats = new int[dat.Length];
num = 1;
for (int i = 0; i < dat.Length; i++)
{
if ((i - num) < 0)
breakdats[i] = Misc.MyBase.NULL_VALUE;
else if ((i + num) >= dat.Length)
breakdats[i] = Misc.MyBase.NULL_VALUE;
else if (!Misc.MyBase.ISVALIDATA(filterdats[i - num]))
breakdats[i] = Misc.MyBase.NULL_VALUE;
else if (!Misc.MyBase.ISVALIDATA(filterdats[i + num]))
breakdats[i] = Misc.MyBase.NULL_VALUE;
else if (!Misc.MyBase.ISVALIDATA(filterdats[i]))
breakdats[i] = Misc.MyBase.NULL_VALUE;
else
{
breakdats[i] = Math.Abs((filterdats[i] - filterdats[i - num]) * (filterdats[i + num] - filterdats[i]));
}
}
bool isdown = false;
//从第1次找到的边界开始,往外找,找折点的最小值
for (int i = borderbegin_grid; i >= (borderbegin_grid - sensor_grid); i--)
{
if ((i - num) < 0)
break;
if ((i + num) >= dat.Length)
break;
if (!Misc.MyBase.ISVALIDATA(breakdats[i - num]))
break;
if (!Misc.MyBase.ISVALIDATA(breakdats[i + num]))
break;
if (!Misc.MyBase.ISVALIDATA(breakdats[i]))
break;
if (breakdats[i] <= breakdats[i + num])
{
//在减少
bg = i;
isdown = true;
}
if (isdown)
{
if (breakdats[i] < breakdats[i - num])
{
//之前那个就是最小值
break;
}
}
}
isdown = false;
for (int i = borderend_grid; i <= (borderend_grid + sensor_grid); i++)
{
if ((i - num) < 0)
break;
if ((i + num) >= dat.Length)
break;
if (!Misc.MyBase.ISVALIDATA(filterdats[i - num]))
break;
if (!Misc.MyBase.ISVALIDATA(filterdats[i + num]))
break;
if (!Misc.MyBase.ISVALIDATA(filterdats[i]))
break;
if (breakdats[i] <= breakdats[i - num])
{
//在减少
eg = i;
isdown = true;
}
if (isdown)
{
if (breakdats[i] < breakdats[i + num])
{
//之前那个就是最小值
break;
}
}
}
borderbegin_grid = bg;
borderend_grid = eg;
return true;
}
///
/// 放入grid数据,分析边界
///
///
///
///
///
///
bool FindFilm_Break(DIRECTION direction, int posOfGrid, int gridBegin, int[] dat)
{
//把数据记录下来
getViewReponse = new BorderSearchGetViewReponse()
{
direction = direction,
posOfGrid = posOfGrid,
gridBegin = gridBegin,
dat = dat
};
UpdateTime = DateTime.Now;
//转为 dat 的序号
int validbegin_grid = Valid.Begin / posOfGrid - gridBegin;
int validend_grid = Valid.End / posOfGrid - gridBegin;
if (!FindFilm_Threshold(dat,
validbegin_grid, validend_grid,
out int borderbegin_grid, out int borderend_grid,
out bool isBorderBeginOk, out bool isBorderEndOk))
{
//warningSystem.Add(BORDER_ERRNO_NOFILM.Code, BORDER_ERRNO_NOFILM.Descrption, OBJComponents.Common.ERR_STATE.ON);
return false;
}
else {
//warningSystem.Add(BORDER_ERRNO_NOFILM.Code, BORDER_ERRNO_NOFILM.Descrption, OBJComponents.Common.ERR_STATE.OFF);
}
//N2 只是用来判断一下膜够不够宽而已,没用
int N2_grid = (N2*3+SensorWidth) / posOfGrid;
if ((borderend_grid - borderbegin_grid) < N2_grid)//膜太短
return false;
if (IsBreakDetect)
{
int sensor_grid = SensorWidth / posOfGrid;
int filter_grid = N / posOfGrid;
//精细查找
FindFilm_Break(dat, sensor_grid, filter_grid, ref borderbegin_grid, ref borderend_grid);
}
int idx = (direction == DIRECTION.FORWARD) ? 0 : 1;
var border = borders[idx];
//转为 脉冲
border.value.Begin = (borderbegin_grid + gridBegin) * posOfGrid;
border.value.End = (borderend_grid +gridBegin) * posOfGrid;
border.isBeginOk = isBorderBeginOk;
border.isEndOk = isBorderEndOk;
if (borders.All(b => b.value.IsValid))
{
foreach(var bo in borders)
bo.UpdateReal(SensorWidth, ProductWidth, Valid.Mid,
borders.All(b=>b.isBeginOk), borders.All(b => b.isEndOk));
Border = new RangeStruct(
(int)borders.Average(b => b.real.Begin),
(int)borders.Average(b => b.real.End));
}
else if (border.value.IsValid)
{
foreach (var bo in borders)
bo.UpdateReal(SensorWidth, ProductWidth, Valid.Mid);
Border = border.real.ToStruct();
}
getViewReponse.border = border.real;
Width = Border.Width;
Mid = Border.Mid;
return true;
}
///
/// 找空气与膜均值的 中间位置
///
///
void FindFilm_Mean(int[] dat, int sensor_grid, int n2_grid, ref int borderbegin_grid, ref int borderend_grid)
{
Range r1_grid = new Range();
//算膜中间的平均值
r1_grid.Begin = borderbegin_grid + sensor_grid + n2_grid;
r1_grid.End = borderend_grid - (sensor_grid + n2_grid);
int avg = MyMath.Avg(dat, r1_grid.Begin, r1_grid.End);
//算空气的平均值
Range r2_grid = new Range();
r2_grid.Begin = borderbegin_grid - sensor_grid / 3;
r2_grid.End = borderend_grid + sensor_grid / 3;
if (r2_grid.Begin < 0)
r2_grid.Begin = 0;
if (r2_grid.End > dat.Length - 1)
r2_grid.End = dat.Length - 1;
int avg1 = MyMath.Avg(dat, 0, r2_grid.Begin);
int avg2 = MyMath.Avg(dat, r2_grid.End, dat.Length - 1);
int threshold;
if ((Misc.MyBase.ISVALIDATA(avg1)) && (Misc.MyBase.ISVALIDATA(avg2)))
{
threshold = (avg + (avg1 + avg2) / 2) / 2;
}
else if (Misc.MyBase.ISVALIDATA(avg1))
{
threshold = (avg + avg1) / 2;
}
else if (Misc.MyBase.ISVALIDATA(avg2))
{
threshold = (avg + avg2) / 2;
}
else
{
threshold = (TempAD + avg) / 2;
}
//左边界
for (int i = r1_grid.Begin; i >= r2_grid.Begin; i--)
{
if (Misc.MyBase.ISVALIDATA(dat[i]))
{
if (dat[i] < threshold)
{
borderbegin_grid = i;
}
}
}
//右边界
for (int i = r1_grid.End; i < r2_grid.End; i++)
{
if (Misc.MyBase.ISVALIDATA(dat[i]))
{
if (dat[i] < threshold)
{
borderend_grid = i;
}
}
}
}
///
/// 放入grid数据,分析边界
///
///
///
///
///
///
public virtual bool FindFilm(DIRECTION direction, int posOfGrid, int gridBegin, int[] dat)
{
if (IsOneMaterial)
{
return FindFilm_Mean(direction, posOfGrid, gridBegin, dat);
}
else {
return FindFilm_Break(direction, posOfGrid, gridBegin, dat);
}
}
///
/// 放入grid数据,分析边界
///
///
///
///
///
///
bool FindFilm_Mean(DIRECTION direction, int posOfGrid, int gridBegin, int[] dat)
{
//把数据记录下来
getViewReponse = new BorderSearchGetViewReponse()
{
direction = direction,
posOfGrid = posOfGrid,
gridBegin = gridBegin,
dat = dat
};
UpdateTime = DateTime.Now;
//这些是AD值
//转为 dat 的序号
int validbegin_grid = Valid.Begin / posOfGrid - gridBegin;
int validend_grid = Valid.End / posOfGrid - gridBegin;
if (!FindFilm_Threshold(dat,
validbegin_grid, validend_grid,
out int borderbegin_grid, out int borderend_grid,
out bool isBorderBeginOk, out bool isBorderEndOk))
{
return false;
}
//N2 只是用来判断一下膜够不够宽而已,没用
int N2_grid = (N2 * 3 + SensorWidth) / posOfGrid;
if ((borderend_grid - borderbegin_grid) < N2_grid)//膜太短
return false;
FindFilm_Mean(dat, SensorWidth / posOfGrid, N2 / posOfGrid, ref borderbegin_grid, ref borderend_grid);
int idx = (direction == DIRECTION.FORWARD) ? 0 : 1;
var border = borders[idx];
//borderbegin_grid, borderend_grid 是实际边界位置,
//border.value 是空气与膜的拐点
//必须外扩
//转为 脉冲
border.value.Begin = (borderbegin_grid + gridBegin) * posOfGrid - SensorWidth / 2;
border.value.End = (borderend_grid + gridBegin) * posOfGrid + SensorWidth / 2;
border.isBeginOk = true;
border.isEndOk = true;
border.UpdateReal(SensorWidth);
if (borders.All(b => b.value.IsValid))
{
Border = new RangeStruct((int)borders.Average(b => b.real.Begin), (int)borders.Average(b => b.real.End));
}
else if (border.value.IsValid)
{
Border = border.real.ToStruct();
}
getViewReponse.border = border.real;
Width = Border.Width;
Mid = Border.Mid;
return true;
}
///
/// 获取膜边界,当根本找不到膜时,返回找边界范围!!!
/// 膜的边界,只是斜波的中间而已。
///
///
///
///
public void GetBorder(DIRECTION direction, out int posBegin, out int posEnd)
{
int idx = (direction == DIRECTION.FORWARD) ? 0 : 1;
var border = borders[idx];
if (border.value.IsValid)
{
posBegin = border.real.Begin;
posEnd = border.real.End;
}
else
{
idx += 1;
if (idx > 1)
idx = 0;
border = borders[idx];
if (border.value.IsValid)
{
posBegin = border.real.Begin;
posEnd = border.real.End;
}
else {
posBegin = Misc.MyBase.NULL_VALUE;
posEnd = Misc.MyBase.NULL_VALUE;
}
}
}
///
/// 获取膜边界,当根本找不到膜时,返回IsVaild=false 的Range!!!
/// 膜的边界,只是斜波的中间而已。
///
///
///
public Range GetBorder(DIRECTION direction)
{
int b, e;
GetBorder(direction, out b, out e);
return new Range() { Begin = b, End = e };
}
///
/// 获取 膜边界 向内缩 N2 + SensorWidth / 2
///
///
///
///
public void GetBoltRange(DIRECTION direction, out int posBegin, out int posEnd)
{
GetBorder(direction, out posBegin, out posEnd);
if (Misc.MyBase.ISVALIDATA(posBegin))
{
posBegin += (SensorWidth / 2 + N2);
posEnd -= (SensorWidth / 2 + N2);
}
}
///
/// 膜边界 向内缩 N2 + SensorWidth / 2
///
///
///
public Range GetBoltRange(DIRECTION direction)
{
int b, e;
GetBoltRange(direction, out b, out e);
return new Range() { Begin = b, End = e };
}
///
/// 获取混合后的横向数据;没有把横向留白剔除;只把纵向留白剔除。
/// 数据单位 mm
///
///
///
[AsyncCb(typeof(BorderSearchGetViewReponse))]
public void GetView(AsyncCBHandler asyncDelegate, object asyncContext)
{
asyncDelegate?.Invoke(asyncContext, getViewReponse);
}
public bool Load()
{
return BorderSearchJsonDb.Load(this, param_path);
}
public bool Save()
{
return BorderSearchJsonDb.Save(this, param_path);
}
public void Apply()
{
Save();
}
#region INotifyPropertyChanged 成员
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyname)
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
}
#endregion
}
public class BorderSearchJsonDb
{
static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
{
c.CreateMap().ReverseMap();
}));
public static bool Load(BorderSearch src, string filePath)
{
try
{
if (File.Exists(filePath))
{
string json = File.ReadAllText(filePath);
var p = JsonConvert.DeserializeObject(json);
Mapper.Map(p, src);
return true;
}
}
catch
{
//异常,没有json 解码失败
}
return false;
}
public static bool Save(BorderSearch src, string filePath)
{
var p = Mapper.Map(src);
try
{
File.WriteAllText(filePath, JsonConvert.SerializeObject(p, Formatting.Indented));
return true;
}
catch
{
//异常,没有json 编码失败
}
return false;
}
public bool Enable;
public bool IsOneMaterial;
public bool IsBreakDetect = true;
public RangeStruct Valid = new RangeStruct(200,7000);
public bool TempADBySet;
public int TempAD = 50000;
public int TempRange = 500;
public bool IsTempRangeByPercent = true;
public double TempRangePercent = 0.02;
public int N=20;
public int SensorWidth=250;
public int N2=400;
public int N3=1000;
}
}