public static Results Calcu(List <PValue>[] inputs, CalcuInfo calcuinfo) { //公用变量 bool _errorFlag = false; string _errorInfo = ""; bool _warningFlag = false; string _warningInfo = ""; bool _fatalFlag = false; string _fatalInfo = ""; int i; //0输出初始化:该算法如果没有有效输入值(inputs为null)或者输入值得有效值为null,给出的计算结果。值为0,计算标志位为StatusConst.InputIsNull List <PValue>[] results = new List <PValue> [4]; for (i = 0; i < results.Length; i++) { results[i] = new List <PValue>(); results[i].Add(new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, (long)StatusConst.InputIsNull)); } try { //0、输入 //0.1、输入处理:输入长度。当输入为空时,给出标志位为StatusConst.InputIsNull的计算结果. if (inputs == null || inputs.Length < 2 || inputs[0] == null || inputs[1] == null) //一些情况下,进来就有null,这样下面的的取出状态为会出错 { _errorFlag = true; _errorInfo = "输入数据数量不足。一维偏差及得分计算要求必须有且仅有两个有效输入。"; //正常情况下不应该出现数量不足,出现这种问题 return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //0.2、输入处理:截止时刻值。该算法,截止时刻点不需要参与计算,要删除。 //本算法做特别处理 //if (input.Count > 1) input.RemoveAt(input.Count - 1); //0.3、输入处理:标志位。该算法考虑标志位不为0的情况,先过滤这些点。 //********如果计算偏差前,参考量和被考核量,在计算的分钟周期内,分钟值为状态位无效的值。 //********这里会读入这个值,并在下面过滤。如果被过滤,则下面的参考量和被考核量长度判断会报错。并返回不正常状态的计算结果。 for (i = 0; i < inputs.Length; i++) { for (int j = inputs[i].Count - 1; j >= 0; j--) { if (inputs[i][j].Status != 0) { inputs[i].RemoveAt(j); } } } //0.4、输入处理:过滤后结果。 //本算法做特别处理 //——如果去除了截止时刻点,过滤后长度小于1(计算要求至少有一个有效数据),则直接返回全0 //——如果没取除截止时刻点,过滤后长度小于2(计算要求至少有一个有效数据和一个截止时刻值) //if (input.Count < 1) return results; //1、检查并整理数据 //——这里是计算每分钟的偏差。 //——输入量是被评变量每分钟滤波值、参考变量每分钟滤波值 if (inputs[0] == null || inputs[0].Count < 2) { _warningFlag = true; _warningInfo = "一维偏差及得分计算,经状态位过滤后,被测评量为空"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[1] == null || inputs[1].Count < 2) { _warningFlag = true; _warningInfo = "一维偏差及得分计算,经状态位过滤后,参考量为空"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //1.2 直接取各变量第一个数值计算,不需要处理截止时刻值 PValue pvdata; PValue refdata; pvdata = inputs[0][0]; //不适用截止值 refdata = inputs[1][0]; //不适用截止值 StatusConst invalidflag = StatusConst.Normal; //参考指标值是否超越偏差曲线x范围,当次指标考核计算是否有效 //2、根据参数获取期望曲线、记分曲线 string[] para = calcuinfo.fparas.Split(new char[] { ';' }); string readMethod = para[0]; //第一个参数是获取曲线的方式,D为动态,S为静态。动态方式,每次计算均读取CSV。静态方式,只读取一次CSV。 int opxid = int.Parse(para[1]); //第二个参数是期望曲线序号 int scoreid = int.Parse(para[2]); //第三个参数是得分曲线序号 Curve1D opxCurve; Curve1D scoreCurve; string strTemp = CurveConfig.Instance.OPXCurvesReadStatus; //这行这条语句是为了下面在使用CurveConfig.GetXXXCurve时保证Instance已被初始化。20181109调试发现该问题。 try { if (readMethod.ToUpper() == "S") { //第一个参数为S时,静态读取期望曲线和积分曲线表 opxCurve = CurveConfig.GetOPXCurve(opxid, calcuinfo.fstarttime); scoreCurve = CurveConfig.GetScoreCurve(scoreid, calcuinfo.fstarttime); } else { //——20181031,因动态方式读取曲线,速度太慢,不在采用。 //——转而采用在静态方式下读取带生效时间的配置曲线。这样在增加配置曲线时,仅需要暂停计算引擎后重启,即可载入新的配置曲线,并继续进行计算。 //第一个参数为D时,动态读取期望曲线和积分曲线表 //opxCurve = CurveConfig.ReadOPXCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == opxid; }); //scoreCurve = CurveConfig.ReadScoreCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == scoreid; }); _errorFlag = true; _errorInfo = "参数配置为D,动态读取配置曲线功能已经取消,请使用静态读取方式。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { _errorFlag = true; _errorInfo = String.Format("读取期望和得分曲线时错误!错误信息:{0}", ex.ToString()); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //判读所需要的曲线是否正确读取,如果没有正确读取一定是返回null,此时要给出错误log if (opxCurve == null) { _errorFlag = true; _errorInfo = String.Format("期望曲线为空,对应的期望曲线:{0}-{1}获取错误!", opxid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (scoreCurve == null) { _errorFlag = true; _errorInfo = String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //3、计算偏差 PValue opx, opxe, opxerate, opxw; //期望值、偏差、偏差比、得分 opx = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //期望值 opxe = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //偏差 opxerate = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //偏差比 opxw = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //得分 //Debug.WriteLine("----opxCurve.Xvalue[0] " + opxCurve.Xvalue[0].ToString() + "~opxCurve.Xvalue[1]" + opxCurve.Xvalue[opxCurve.Xvalue.Length - 1].ToString()); //期望值:查期望曲线 //处理方式一:参考点超限时,不做处理,对应时间段没有任何计算结果 //——该方法的主要问题是,整分钟的偏差和得分计算接口,会有空。在那些超过偏差曲线范围外的时间段,没有分钟计算值。 //——该方法导致的另外一个问题就是,由于缺少分钟值,并发计算在缺少分钟值的位置要去插值,这个插值行为是错误的。 //——在缺少分钟值的情况下,实时计算,由于取数据时存在起始时间和截止时间的插值算法,如果恰好这些时间点没有值,也会进行插值,这个插值会造成错误。 /* * if (refdata.Value < opxCurve.Xvalue[0] || refdata.Value > opxCurve.Xvalue[opxCurve.Xvalue.Length - 1]) * { * return null; * } * else * { * opx.Value = opxCurve.Fx(refdata.Value); * } */ //处理方式二:参考点超限时,按边界点处理。正常计算。仅在计算结果中置标志位为11. //——四个计算结果均按边界值计算,置计算结果标志位为11。 //——但是,凡是对偏差和得分这些结果进行进一步计算的,这些算法需要考虑标志位。 //——由于不会缺少分钟值,无论是并发计算和实时计算都不会出现问题。 //期望值 if (refdata.Value < opxCurve.XLowerLimit) { //如果参考标签的值,在期望曲线参考值有效范围外,则期望返回边界值。 opx.Value = opxCurve.Fx(opxCurve.XLowerLimit); //期望值返回边界值 invalidflag = StatusConst.OutOfValid4Bias; //至过程值超限标志 } else if (refdata.Value > opxCurve.XUpperLimit) { opx.Value = opxCurve.Fx(opxCurve.XUpperLimit); invalidflag = StatusConst.OutOfValid4Bias; } else { opx.Value = opxCurve.Fx(refdata.Value); //期望值按正常计算 } //偏差=实际值-期望值 opxe.Value = pvdata.Value - opx.Value; //偏差比=偏差/期望值 if (opx.Value == 0) { opxerate.Value = 0; } else { opxerate.Value = opxe.Value / opx.Value * 100; } //得分:查记分曲线 //——偏差超过限度时,直接返回得分边界值。而得分不像偏差曲线的x越界,要置invalidflag opxw.Value = scoreCurve.Fx(opxe.Value); //4、组织输出 for (i = 0; i < 4; i++) { results[i] = new List <PValue>(); } results[0].Add(opx); results[1].Add(opxe); results[2].Add(opxerate); results[3].Add(opxw); //5、如果越界标识不为0,还需要给每个结果的状态添加越界的标志。 if (invalidflag != 0) { for (i = 0; i < 4; i++) { results[i][0].Status = (long)invalidflag; } } return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } catch (Exception ex) { //计算中出任何错误,则需要记录log //LogHelper.Write(LogType.Error, "计算模块错误!"); //记录计算模块的名称、当前标签、起始时间、结束时间 //string moduleInfo = string.Format("——计算模块的名称是:{0},当前计算源标签是:{1},计算起始时间是:{2},计算结束时间是:{3}。", calcuInfo.fmodulename, calcuInfo.sourcetagname, calcuinfo.fstarttime.ToString(), calcuinfo.fendtime.ToString()); //LogHelper.Write(LogType.Error, moduleInfo); //计算引擎报错具体信息 //string errInfo = string.Format("——具体报错信息:{0}。", ex.ToString()); //LogHelper.Write(LogType.Error, errInfo); //返回null供计算引擎处理 _fatalFlag = true; _fatalInfo = ex.ToString(); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } }
public static Results Calcu(List <PValue>[] inputs, CalcuInfo calcuinfo) { //公用变量 bool _errorFlag = false; string _errorInfo = ""; bool _warningFlag = false; string _warningInfo = ""; bool _fatalFlag = false; string _fatalInfo = ""; int i; //0输出初始化:该算法如果没有有效输入值(inputs为null)或者输入值得有效值为null,给出的计算结果。值为0,计算标志位为StatusConst.InputIsNull List <PValue>[] results = new List <PValue> [4]; for (i = 0; i < results.Length; i++) { results[i] = new List <PValue>(); results[i].Add(new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, (long)StatusConst.InputIsNull)); } try { //0、输入 //时间段偏差算法与单点偏差算法,唯一的不同点在于,单点偏差算法一次仅算一个偏差值。时间段偏差算法,一次要写多个偏差值。 //0.1、输入处理:输入长度。输入必须还有一个被考核量,一个参考量。当输入为空或者输入数量不足,给出标志位为StatusConst.InputIsNull的计算结果. //——在该算法下,正常情况不应该出现inputs整体为空的情况,出现整体为空,说明前置的求每分钟均值的算法有误。这种情况必须给出错误 if (inputs == null || inputs.Length < 2 || inputs[0] == null || inputs[0].Count == 0 || inputs[1] == null || inputs[1].Count == 0) //一些情况下,进来就有null,这样下面的的取出状态为会出错 { _errorFlag = true; _errorInfo = "时间段一维偏差算法输入错误。输入为空或输入数量不足,或者被考核量、参考量数据长度为0。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //0.2、输入处理:输入的是要参与考核的被考核量与参考量的分钟数据,长度必须相同 if (inputs[0].Count != inputs[1].Count) { _errorFlag = true; _errorInfo = "时间段一维偏差算法输入错误。被考核量和参考量数据长度不相同。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //准备结果,只要输入数据没有整体错误,则必须有计算结果 int spanNumber = inputs[0].Count - 1; //不算截止数据 double intervalseconds = (double)(calcuinfo.fendtime.Subtract(calcuinfo.fstarttime).TotalSeconds / spanNumber); List <PValue>[] tempresults = new List <PValue> [4]; tempresults[0] = new List <PValue>(); //期望 tempresults[1] = new List <PValue>(); //偏差 tempresults[2] = new List <PValue>(); //偏差比 tempresults[3] = new List <PValue>(); //得分 //在输入数量正确,且分钟数量的数量相同的情况下,循环计算每分钟的偏差值 for (int j = 0; j < inputs[0].Count - 1; j++) //不计算截止数据 { //0.3 初始化当前分钟的计算结果 for (i = 0; i < results.Length; i++) { tempresults[i].Add(new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), (long)StatusConst.InputIsNull)); } //0.4、输入处理:检查标志位。 //——这里是计算每分钟的偏差。 //——输入量是被评变量每分钟滤波值、参考变量每分钟滤波值 if (inputs[0][j] == null || inputs[1][j] == null) { //正常状态下,不应该出现分钟过滤值在某个点没有值得情况,这种情况说明分钟过滤算法有误,应该检查 //在计算引擎中,即使计算错误标志为true,但仍会写计算结果 string nullobject = ""; if (inputs[0][j] == null) { nullobject += "被考核量"; } if (inputs[1][j] == null) { nullobject += "、参考量"; } _errorFlag = true; _errorInfo += String.Format("时间段一维偏差及得分计算,{0}分钟数据为空。请检查!" + Environment.NewLine, nullobject); //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); //这里不直接结束,而是继续循环 continue; } if (inputs[0][j].Status != 0 || inputs[1][j].Status != 0) { string statusnotzero = ""; if (inputs[0][j].Status != 0) { statusnotzero += "被考核量"; } if (inputs[1][j].Status != 0) { statusnotzero += "、参考量"; } //正常状态下,有可能出现分钟过滤在某个时刻点状态值非0,记录报警,并计算下一个点 _warningFlag = true; _warningInfo += String.Format("时间段一维偏差及得分计算,{0}分钟数据状态位异常。{1}到{2}无有效数据!" + Environment.NewLine, statusnotzero, inputs[0][j].Timestamp.ToString("yyyy-MM-dd HH:mm:ss"), inputs[0][j].Timestamp.ToString("yyyy-MM-dd HH:mm:ss") ); continue; } //1.2 直接取各变量分钟值 PValue pvdata; PValue refdata; pvdata = inputs[0][j]; // refdata = inputs[1][j]; // StatusConst invalidflag = StatusConst.Normal; //参考指标值是否超越偏差曲线x范围,当次指标考核计算是否有效 //2、根据参数获取期望曲线、记分曲线 string[] para = calcuinfo.fparas.Split(new char[] { ';' }); string readMethod = para[0]; //第一个参数是获取曲线的方式,D为动态,S为静态。动态方式,每次计算均读取CSV。静态方式,只读取一次CSV。 int opxid = int.Parse(para[1]); //第二个参数是期望曲线序号 int scoreid = int.Parse(para[2]); //第三个参数是得分曲线序号 Curve1D opxCurve; Curve1D scoreCurve; string strTemp = CurveConfig.Instance.OPXCurvesReadStatus; //这行这条语句是为了下面在使用CurveConfig.GetXXXCurve时保证Instance已被初始化。20181109调试发现该问题。 try { if (readMethod.ToUpper() == "S") { //第一个参数为S时,静态读取期望曲线和积分曲线表 opxCurve = CurveConfig.GetOPXCurve(opxid, calcuinfo.fstarttime); scoreCurve = CurveConfig.GetScoreCurve(scoreid, calcuinfo.fstarttime); } else { //——20181031,因动态方式读取曲线,速度太慢,不在采用。 //——转而采用在静态方式下读取带生效时间的配置曲线。这样在增加配置曲线时,仅需要暂停计算引擎后重启,即可载入新的配置曲线,并继续进行计算。 //第一个参数为D时,动态读取期望曲线和积分曲线表 //opxCurve = CurveConfig.ReadOPXCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == opxid; }); //scoreCurve = CurveConfig.ReadScoreCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == scoreid; }); _errorFlag = true; _errorInfo = "参数配置为D,动态读取配置曲线功能已经取消,请使用静态读取方式。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { _errorFlag = true; _errorInfo += String.Format("读取期望和得分曲线时错误!错误信息:{0}", ex.ToString()) + Environment.NewLine; //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } //判读所需要的曲线是否正确读取,如果没有正确读取一定是返回null,此时要给出错误log if (opxCurve == null) { _errorFlag = true; _errorInfo += String.Format("期望曲线为空,对应的期望曲线:{0}-{1}获取错误!", opxid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")) + Environment.NewLine; //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } if (scoreCurve == null) { _errorFlag = true; _errorInfo += String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")) + Environment.NewLine; //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } //3、计算偏差 PValue sp, err, errrate, score; //期望值、偏差、偏差比、得分 sp = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); //期望值 err = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); //偏差 errrate = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); //偏差比 score = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); //得分 //Debug.WriteLine("----opxCurve.Xvalue[0] " + opxCurve.Xvalue[0].ToString() + "~opxCurve.Xvalue[1]" + opxCurve.Xvalue[opxCurve.Xvalue.Length - 1].ToString()); //期望值:查期望曲线 //处理方式一:参考点超限时,不做处理,对应时间段没有任何计算结果 //——该方法的主要问题是,整分钟的偏差和得分计算接口,会有空。在那些超过偏差曲线范围外的时间段,没有分钟计算值。 //——该方法导致的另外一个问题就是,由于缺少分钟值,并发计算在缺少分钟值的位置要去插值,这个插值行为是错误的。 //——在缺少分钟值的情况下,实时计算,由于取数据时存在起始时间和截止时间的插值算法,如果恰好这些时间点没有值,也会进行插值,这个插值会造成错误。 /* * if (refdata.Value < opxCurve.Xvalue[0] || refdata.Value > opxCurve.Xvalue[opxCurve.Xvalue.Length - 1]) * { * return null; * } * else * { * opx.Value = opxCurve.Fx(refdata.Value); * } */ //处理方式二:参考点超限时,按边界点计算偏差。同时在计算结果中置标志位为StatusConst.OutOfValid4Bias. //——四个计算结果均按边界值计算,置计算结果标志位为StatusConst.OutOfValid4Bias。 //——但是,凡是对偏差和得分这些结果进行进一步计算的,这些算法需要考虑标志位。 //——由于不会缺少分钟值,无论是并发计算和实时计算都不会出现问题。 //要注意,防止偏差曲线的x不是从小到大。(但是,目前硬性要求期望曲线和得分曲线配置,范围要从低到高。因此老版本上,没有这个判断) double XMin = opxCurve.Xvalue[0] < opxCurve.Xvalue[opxCurve.Xvalue.Length - 1] ? opxCurve.Xvalue[0] : opxCurve.Xvalue[opxCurve.Xvalue.Length - 1]; double XMax = opxCurve.Xvalue[0] < opxCurve.Xvalue[opxCurve.Xvalue.Length - 1] ? opxCurve.Xvalue[opxCurve.Xvalue.Length - 1] : opxCurve.Xvalue[0]; if (refdata.Value < XMin) { //如果参考标签的值,在期望曲线参考值有效范围外,则偏差返回边界值。 sp.Value = opxCurve.Fx(XMin); invalidflag = StatusConst.OutOfValid4Bias; } else if (refdata.Value > XMax) { sp.Value = opxCurve.Fx(XMax); invalidflag = StatusConst.OutOfValid4Bias; } else { //期望值 sp.Value = opxCurve.Fx(refdata.Value); //虽然一律要算期望值,但是超限后按边界算期望,所有计算结果标志位都会被置位 } //偏差=实际值-期望值 err.Value = pvdata.Value - sp.Value; //偏差比=偏差/期望值 if (sp.Value == 0) { errrate.Value = 0; } else { errrate.Value = err.Value / sp.Value * 100; } //得分:查记分曲线 //——偏差超过限度时,直接返回得分边界值。但是得分不像偏差曲线的x越界,要置invalidflag score.Value = scoreCurve.Fx(err.Value); //4、组织输出 for (i = 0; i < 4; i++) { results[i] = new List <PValue>(); } tempresults[0][j] = sp; tempresults[1][j] = err; tempresults[2][j] = errrate; tempresults[3][j] = score; //如果越界标识不为0,还需要给每个结果的状态添加越界的标志。 if (invalidflag != 0) { for (i = 0; i < 4; i++) { tempresults[i][j].Status = (long)invalidflag; } } }//end for return(new Results(tempresults, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } catch (Exception ex) { //计算中出任何错误,则需要记录log //LogHelper.Write(LogType.Error, "计算模块错误!"); //记录计算模块的名称、当前标签、起始时间、结束时间 //string moduleInfo = string.Format("——计算模块的名称是:{0},当前计算源标签是:{1},计算起始时间是:{2},计算结束时间是:{3}。", calcuInfo.fmodulename, calcuInfo.sourcetagname, calcuinfo.fstarttime.ToString(), calcuinfo.fendtime.ToString()); //LogHelper.Write(LogType.Error, moduleInfo); //计算引擎报错具体信息 //string errInfo = string.Format("——具体报错信息:{0}。", ex.ToString()); //LogHelper.Write(LogType.Error, errInfo); //返回null供计算引擎处理 _fatalFlag = true; _fatalInfo = ex.ToString(); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } }
public static Results Calcu(List <PValue>[] inputs, CalcuInfo calcuinfo) { //公用变量 bool _errorFlag = false; string _errorInfo = ""; bool _warningFlag = false; string _warningInfo = ""; bool _fatalFlag = false; string _fatalInfo = ""; int i; //0输出初始化:该算法如果没有有效输入值(inputs为null)或者输入值得有效值为null,给出的计算结果。值为0,计算标志位为StatusConst.InputIsNull List <PValue>[] results = new List <PValue> [5]; for (i = 0; i < results.Length; i++) { results[i] = new List <PValue>(); results[i].Add(new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, (long)StatusConst.InputIsNull)); } try { //0、输入 //0.1、输入处理:输入长度。当输入为空时,给出标志位为StatusConst.InputIsNull的计算结果. if (inputs == null || inputs.Length < 2 || inputs[0] == null || inputs[1] == null) //一些情况下,进来就有null,这样下面的的取出状态为会出错 { _errorFlag = true; _errorInfo = "输入有效数据数量不足。一维偏差及得分计算要求必须有且仅有两个有效输入。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //0.2、输入处理:截止时刻值。该算法,截止时刻点不需要参与计算,要删除。 //本算法做特别处理 //if (input.Count > 1) input.RemoveAt(input.Count - 1); //0.3、输入处理:标志位。该算法考虑标志位不为0的情况,先过滤这些点。 //********如果计算偏差前,参考量和被考核量,在计算的分钟周期内,分钟值为状态位无效的值。 //********这里会读入这个值,并在下面过滤。如果被过滤,则下面的参考量和被考核量长度判断会报错。并返回不正常状态的计算结果。 for (i = 0; i < inputs.Length; i++) { for (int j = inputs[i].Count - 1; j >= 0; j--) { if (inputs[i][j].Status != 0) { inputs[i].RemoveAt(j); } } } //0.4、输入处理:过滤后结果。 //本算法做特别处理 //——如果去除了截止时刻点,过滤后长度小于1(计算要求至少有一个有效数据),则直接返回全0 //——如果没取除截止时刻点,过滤后长度小于2(计算要求至少有一个有效数据和一个截止时刻值) //if (input.Count < 1) return results; //1、检查并整理数据 //——这里是计算每分钟的偏差。 //——输入量是被评变量每分钟滤波值、参考变量每分钟滤波值 if (inputs[0] == null || inputs[0].Count < 2) { _warningFlag = true; _warningInfo = "一维偏差及得分计算,被测评量经状态位过滤后数据为空!"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[1] == null || inputs[1].Count < 2) { _warningFlag = true; _warningInfo = "一维偏差及得分计算,参考量经状态位过滤后数据为空!"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //1.2 直接取各变量第一个数值计算,不需要处理截止时刻值 PValue pvdata; PValue refdata; pvdata = inputs[0][0]; //不使用截止值 refdata = inputs[1][0]; //不使用截止值 StatusConst invalidflag = StatusConst.Normal; //参考指标值是否超越偏差曲线x范围,当次指标考核计算是否有效 //2、根据参数获取期望曲线、记分曲线 string calcuMode; //计算方式选择 string[] para = calcuinfo.fparas.Split(new char[] { ';' }); string readMethod = para[0]; //第一个参数是获取曲线的方式,D为动态,S为静态。动态方式,每次计算均读取CSV。静态方式,只读取一次CSV。 int opxid = int.Parse(para[1]); //第二个参数是期望曲线序号 int scoreid = int.Parse(para[2]); //第三个参数是得分曲线序号 double k = double.Parse(para[3]); double b = double.Parse(para[4]); if (para.Length == 6) { calcuMode = para[5]; //如果设定了第5个参数,计算模式用二维计分。D表示二维计分,s表示一维计分 } else { calcuMode = "S"; //如果没设定第5个参数,计算模式为一维计分 } Curve1D opxCurve; //期望值 Curve1D scoreCurve; //计分值 Curve2D scoreCurve2D; string strTemp = CurveConfig.Instance.OPXCurvesReadStatus; //这行这条语句是为了下面在使用CurveConfig.GetXXXCurve时保证Instance已被初始化。20181109调试发现该问题。 string strTemp2D = CurveConfig.Instance.ScoreCurves2DReadStatus; try { if (readMethod.ToUpper() == "S") { //第一个参数为S时,静态读取期望曲线和积分曲线表 opxCurve = CurveConfig.GetOPXCurve(opxid, calcuinfo.fstarttime); scoreCurve2D = CurveConfig.GetScoreCurve2D(scoreid, calcuinfo.fstarttime); scoreCurve = CurveConfig.GetScoreCurve(scoreid, calcuinfo.fstarttime); } else { //——20181031,因动态方式读取曲线,速度太慢,不在采用。 //——转而采用在静态方式下读取带生效时间的配置曲线。这样在增加配置曲线时,仅需要暂停计算引擎后重启,即可载入新的配置曲线,并继续进行计算。 //第一个参数为D时,动态读取期望曲线和积分曲线表 //opxCurve = CurveConfig.ReadOPXCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == opxid; }); //scoreCurve = CurveConfig.ReadScoreCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == scoreid; }); _errorFlag = true; _errorInfo = "参数配置为D,动态读取配置曲线功能已经取消,请使用静态读取方式。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { _errorFlag = true; _errorInfo = String.Format("读取期望和得分曲线时错误!错误信息:{0}", ex.ToString()); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //判读所需要的曲线是否正确读取,如果没有正确读取一定是返回null,此时要给出错误log if (opxCurve == null) { _errorFlag = true; _errorInfo = String.Format("期望曲线为空,对应的期望曲线:{0}-{1}获取错误!", opxid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (calcuMode == "D") { if (scoreCurve2D == null) { _errorFlag = true; _errorInfo = String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } if (calcuMode == "S") { if (scoreCurve == null) { _errorFlag = true; _errorInfo = String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } //3、计算偏差 PValue sp, err, errrate, score, wscore; //期望值、偏差、偏差比、得分 sp = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //期望值 err = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //偏差 errrate = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //偏差比 score = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //得分 wscore = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //加权得分 //Debug.WriteLine("----opxCurve.Xvalue[0] " + opxCurve.Xvalue[0].ToString() + "~opxCurve.Xvalue[1]" + opxCurve.Xvalue[opxCurve.Xvalue.Length - 1].ToString()); //期望值:查期望曲线 //处理方式一:参考点超限时,不做处理,对应时间段没有任何计算结果 //——该方法的主要问题是,整分钟的偏差和得分计算接口,会有空。在那些超过偏差曲线范围外的时间段,没有分钟计算值。 //——该方法导致的另外一个问题就是,由于缺少分钟值,并发计算在缺少分钟值的位置要去插值,这个插值行为是错误的。 //——在缺少分钟值的情况下,实时计算,由于取数据时存在起始时间和截止时间的插值算法,如果恰好这些时间点没有值,也会进行插值,这个插值会造成错误。 /* * if (refdata.Value < opxCurve.Xvalue[0] || refdata.Value > opxCurve.Xvalue[opxCurve.Xvalue.Length - 1]) * { * return null; * } * else * { * opx.Value = opxCurve.Fx(refdata.Value); * } */ //处理方式二:参考点超限时,按边界点计算偏差。同时在计算结果中置标志位为StatusConst.OutOfValid4Bias. //——四个计算结果均按边界值计算,置计算结果标志位为StatusConst.OutOfValid4Bias。 //——但是,凡是对偏差和得分这些结果进行进一步计算的,这些算法需要考虑标志位。 //——由于不会缺少分钟值,无论是并发计算和实时计算都不会出现问题。 if (refdata.Value < opxCurve.XLowerLimit) //小于下限 { //如果参考标签的值,在期望曲线参考值有效范围外,则期望返回边界值。 sp.Value = opxCurve.Fx(opxCurve.XLowerLimit); //期望值返回边界值 invalidflag = StatusConst.OutOfValid4Bias; //至过程值超限标志 } else if (refdata.Value > opxCurve.XUpperLimit) //大于上限 { sp.Value = opxCurve.Fx(opxCurve.XUpperLimit); invalidflag = StatusConst.OutOfValid4Bias; } else { sp.Value = opxCurve.Fx(refdata.Value); //期望值按正常计算 } //偏差=实际值-期望值 err.Value = pvdata.Value - sp.Value; //偏差比=偏差/期望值 if (sp.Value == 0) { errrate.Value = 0; } else { errrate.Value = err.Value / sp.Value * 100; } //得分:查记分曲线 //——偏差超过限度时,直接返回得分边界值。但是得分不像偏差曲线的x越界,要置invalidflag if (calcuMode == "S") { score.Value = scoreCurve.Fx(err.Value); } else { //参考值Y(对应表的第一列配置) if (refdata.Value < scoreCurve2D.YLowerLimit) { refdata.Value = scoreCurve2D.YLowerLimit; invalidflag = StatusConst.OutOfValid4Bias; } else if (refdata.Value > scoreCurve2D.YUpperLimit) { refdata.Value = scoreCurve2D.YUpperLimit; invalidflag = StatusConst.OutOfValid4Bias; } score.Value = scoreCurve2D.Fx(err.Value, refdata.Value); //虽然一律要算期望值,但是超限后按边界算期望,所有计算结果标志位都会被置位 } wscore.Value = k * score.Value + b; //4、录入数据库 MDeviationSOutClass MessageIN = new MDeviationSOutClass(); MessageIN.sp = sp.Value; MessageIN.err = err.Value; MessageIN.errrate = errrate.Value; MessageIN.score = score.Value; MessageIN.wscore = wscore.Value; string year = string.Empty; string month = string.Empty; string day = string.Empty; string hour = string.Empty; year = calcuinfo.fstarttime.Year.ToString(); month = calcuinfo.fstarttime.Month.ToString(); day = calcuinfo.fstarttime.Day.ToString(); hour = calcuinfo.fstarttime.Hour.ToString(); bool isok = BLL.AlgorithmBLL.insertMDeviationS(MessageIN, calcuinfo.fsourtagids[0].ToString(), year, month, day, hour, Convert.ToInt32(invalidflag)); if (isok) { return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } else { _fatalFlag = true; _fatalInfo = "MDeviationS数据录入数据库是失败"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { //计算中出任何错误,则需要记录log //LogHelper.Write(LogType.Error, "计算模块错误!"); //记录计算模块的名称、当前标签、起始时间、结束时间 //string moduleInfo = string.Format("——计算模块的名称是:{0},当前计算源标签是:{1},计算起始时间是:{2},计算结束时间是:{3}。", calcuInfo.fmodulename, calcuInfo.sourcetagname, calcuinfo.fstarttime.ToString(), calcuinfo.fendtime.ToString()); //LogHelper.Write(LogType.Error, moduleInfo); //计算引擎报错具体信息 //string errInfo = string.Format("——具体报错信息:{0}。", ex.ToString()); //LogHelper.Write(LogType.Error, errInfo); //返回null供计算引擎处理 _fatalFlag = true; _fatalInfo = ex.ToString(); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } }
public static Results Calcu(List <PValue>[] inputs, CalcuInfo calcuinfo) { //公用变量 bool _errorFlag = false; string _errorInfo = ""; bool _warningFlag = false; string _warningInfo = ""; bool _fatalFlag = false; string _fatalInfo = ""; int i; //0输出初始化:该算法如果没有有效输入值(inputs为null)或者输入值得有效值为null,给出的计算结果。值为0,计算标志位为StatusConst.InputIsNull List <PValue>[] results = new List <PValue> [4]; for (i = 0; i < results.Length; i++) { results[i] = new List <PValue>(); results[i].Add(new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, (long)StatusConst.InputIsNull)); } try { //0、输入 //0.1、输入处理:输入长度。当输入为空时,给出标志位为StatusConst.InputIsNull的计算结果. if (inputs == null || inputs.Length < 3) { _errorFlag = true; _errorInfo = "时间段二维偏差及得分算法输入错误。计算要求必须有且仅有三个有效输入。当前输入整体为空或输入数量不足。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[0] == null || inputs[0].Count == 0) { _errorFlag = true; _errorInfo = "时间段二维偏差及得分计算输入错误。被考核量输入数据为空。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[1] == null || inputs[1].Count == 0) { _errorFlag = true; _errorInfo = "时间段二维偏差及得分计算输入错误。参考量1输入数据为空。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[2] == null || inputs[2].Count == 0) { _errorFlag = true; _errorInfo = "时间段二维偏差及得分计算输入错误。参考量2输入数据为空。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //0.2、在判断各个数据长度是否相等之前,对常数标签按照被考核量进行扩充。因为常数标签仅返回一个值。 for (i = 1; i < inputs.Length; i++) //第0个量是被考核指标,不可能是常数标签 { if (calcuinfo.fsourtagids[i] < APPConfig.rdbtable_constmaxnumber) { double constValue = inputs[i][0].Value; //常数标签仅返回一个值 List <PValue> constList = new List <PValue>(); //以被考核量为标准,重新构建常数标签 for (int j = 0; j < inputs[0].Count; j++) { constList.Add(new PValue(constValue, inputs[0][j].Timestamp, inputs[0][j].Endtime, inputs[0][j].Status)); } inputs[i] = constList; } } //0.2、输入处理:输入的是要参与考核的被考核量与参考量的分钟数据,长度必须相同。 //——因为被考核量和考核量,都是经过filter计算的,无论实时数据有什么问题,filter计算后都应该产生计算结果(只不过状态位不同)。 //——因此正常状态下,被考核量与考核量的长度应该相同,如果不相同,则说明filter计算一定有问题,需要检查。因此这里应该报错误。而不是报警告。 if (inputs[0].Count != inputs[1].Count || inputs[0].Count != inputs[2].Count || inputs[1].Count != inputs[2].Count) { _errorFlag = true; _errorInfo = "时间段一维偏差算法输入错误。被考核量和参考量数据长度不相同。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //准备结果,只要输入数据没有整体错误,则必须有计算结果 int spanNumber = inputs[0].Count - 1; //不算截止数据 double intervalseconds = (double)(calcuinfo.fendtime.Subtract(calcuinfo.fstarttime).TotalSeconds / spanNumber); List <PValue>[] tempresults = new List <PValue> [4]; tempresults[0] = new List <PValue>(); //期望 tempresults[1] = new List <PValue>(); //偏差 tempresults[2] = new List <PValue>(); //偏差比 tempresults[3] = new List <PValue>(); //得分 //在输入数量正确,且分钟数量的数量相同的情况下,循环计算每分钟的偏差值 for (int j = 0; j < inputs[0].Count - 1; j++) //不计算截止数据 { //0.3 初始化当前分钟的计算结果 for (i = 0; i < results.Length; i++) { tempresults[i].Add(new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), (long)StatusConst.InputIsNull)); } //0.4、输入处理:检查标志位。 //——这里是计算每分钟的偏差。 //——输入量是被评变量每分钟滤波值、参考变量每分钟滤波值 if (inputs[0][j] == null || inputs[1][j] == null || inputs[2][j] == null) { //正常状态下,不应该出现分钟过滤值在某个点没有值得情况,这种情况说明分钟过滤算法有误,应该检查 //在计算引擎中,即使计算错误标志为true,但仍会写计算结果 string nullobject = ""; if (inputs[0][j] == null) { nullobject += "|被考核量|"; } if (inputs[1][j] == null) { nullobject += "|参考量1|"; } if (inputs[2][j] == null) { nullobject += "|参考量2|"; } _errorFlag = true; _errorInfo += String.Format("时间段二维偏差及得分计算,{0}从{1}到{2}的分钟数据为空。请检查!" + Environment.NewLine, nullobject, inputs[0][j].Timestamp.ToString("yyyy-MM-dd HH:mm:ss"), inputs[0][j].Endtime.ToString("yyyy-MM-dd HH:mm:ss") ); //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); //这里不直接结束,而是继续循环,计算下一分钟的偏差值 continue; } else if (inputs[0][j].Status != 0 || inputs[1][j].Status != 0 || inputs[2][j].Status != 0) { string statusnotzero = ""; if (inputs[0][j].Status != 0) { statusnotzero += "|被考核量|"; } if (inputs[1][j].Status != 0) { statusnotzero += "|参考量1|"; } if (inputs[2][j].Status != 0) { statusnotzero += "|参考量2|"; } //正常状态下,有可能出现分钟过滤在某个时刻点状态值非0,记录报警,并计算下一个点 _warningFlag = true; _warningInfo += String.Format("时间段二维偏差及得分计算,{0}的分钟数据状态位异常。从{1}到{2}无有效数据。请检查!" + Environment.NewLine, statusnotzero, inputs[0][j].Timestamp.ToString("yyyy-MM-dd HH:mm:ss"), inputs[0][j].Endtime.ToString("yyyy-MM-dd HH:mm:ss") ); continue; } //1.2、直接取各变量分钟值 PValue pvdata; PValue refdataX; PValue refdataY; pvdata = inputs[0][j]; //不使用截止值,被测评量 refdataX = inputs[1][j]; //不使用截止值,参考量1 refdataY = inputs[2][j]; //不使用截止值,参考量2 StatusConst invalidflag = StatusConst.Normal; //参考指标值是否超越偏差曲线x范围,当次指标考核计算是否有效 //2、根据参数获取期望曲线、记分曲线 string[] para = calcuinfo.fparas.Split(new char[] { ';' }); string readMethod = para[0]; //第一个参数是获取曲线的方式,D为动态,S为静态。动态方式,每次计算均读取CSV。静态方式,只读取一次CSV。 int opxid = int.Parse(para[1]); //第二个参数是期望曲线序号 int scoreid = int.Parse(para[2]); //第三个参数是得分曲线序号 Curve2D opxCurve; //偏差曲线是二维 Curve1D scoreCurve; //得分曲线是一维 string strTemp = CurveConfig.Instance.OPXCurvesReadStatus; //这行这条语句是为了下面在使用CurveConfig.GetXXXCurve时保证Instance已被初始化。20181109调试发现该问题。 try { if (readMethod.ToUpper() == "S") { //静态读取,对于分钟计算来说,只有第一次才读取偏差曲线和得分曲线 opxCurve = CurveConfig.GetOPXCurve2D(opxid, calcuinfo.fstarttime); scoreCurve = CurveConfig.GetScoreCurve(scoreid, calcuinfo.fstarttime); } else { //——20181031,因动态方式读取曲线,速度太慢,不在采用。 //——转而采用在静态方式下读取带生效时间的配置曲线。这样在增加配置曲线时,仅需要暂停计算引擎后重启,即可载入新的配置曲线,并继续进行计算。 //动态读取,对于分钟计算来说,每次计算都要读取偏差曲线和得分曲线 //opxCurve = CurveConfig.ReadOPXCurves2D(calcuinfo.fstarttime).Find(delegate(Curve2D a) { return a.Index == opxid; }); //scoreCurve = CurveConfig.ReadScoreCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == scoreid; }); _errorFlag = true; _errorInfo = "参数配置为D,动态读取配置曲线功能已经取消,请使用静态读取方式。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { _errorFlag = true; _errorInfo += String.Format("读取期望和得分曲线时错误,请检查曲线配置文件!错误信息:{0}", ex.ToString()); //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } //判读所需要的曲线是否正确读取,如果没有正确读取一定是返回null,此时要给出错误log if (opxCurve == null) { _errorFlag = true; _errorInfo += String.Format("期望曲线为空,对应的期望曲线:{0}-{1}获取错误!", opxid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")) + Environment.NewLine; //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } if (scoreCurve == null) { _errorFlag = true; _errorInfo += String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")) + Environment.NewLine; //return new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo); continue; } //3、计算偏差 PValue sp, err, errrate, score; //期望值、偏差、偏差比、得分 sp = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); err = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); errrate = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); score = new PValue(0, calcuinfo.fstarttime.AddSeconds(j * intervalseconds), calcuinfo.fstarttime.AddSeconds((j + 1) * intervalseconds), 0); //计算期望值,查期望曲线。当参考量超限时,有两种处理方法 //处理方式一:不做处理,返回null,对应时间段没有任何计算结果 /* * if (refdataX.Value < opxCurve.Xvalue[0] || * refdataX.Value > opxCurve.Xvalue[opxCurve.Xvalue.Length - 1] || * refdataY.Value < opxCurve.Yvalue[0] || * refdataY.Value > opxCurve.Yvalue[opxCurve.Yvalue.Length - 1] * ) * { * //如果参考标签的值,在期望曲线参考值有效范围外,则当前时刻点,考核无效,不返回任何考核值 * return null; * } */ //处理方式二:任意一个参考值超限时,按边界值计算偏差,同时所有计算结果状态位置为StatusConst.OutOfValid4Bias //要注意,防止偏差曲线的x不是从小到大。 //参考值X(对应表的第一行配置) if (refdataX.Value < opxCurve.XLowerLimit) { refdataX.Value = opxCurve.XLowerLimit; invalidflag = StatusConst.OutOfValid4Bias; } else if (refdataX.Value > opxCurve.XUpperLimit) { refdataX.Value = opxCurve.XUpperLimit; invalidflag = StatusConst.OutOfValid4Bias; } //参考值Y(对应表的第一列配置) if (refdataY.Value < opxCurve.YLowerLimit) { refdataY.Value = opxCurve.YLowerLimit; invalidflag = StatusConst.OutOfValid4Bias; } else if (refdataY.Value > opxCurve.YUpperLimit) { refdataY.Value = opxCurve.YUpperLimit; invalidflag = StatusConst.OutOfValid4Bias; } //期望值:查期望曲线 - 2d表格 sp.Value = opxCurve.Fx(refdataX.Value, refdataY.Value); //虽然一律要算期望值,但是超限后按边界算期望,所有计算结果标志位都会被置位 //偏差=实际值-期望值 err.Value = pvdata.Value - sp.Value; //偏差比=偏差/期望值 if (sp.Value == 0) { errrate.Value = 0; } else { errrate.Value = err.Value / sp.Value * 100; } //得分:查记分曲线 //——偏差超过限度时,直接返回得分边界值。但是得分不像偏差曲线的x越界,要置invalidflag score.Value = scoreCurve.Fx(err.Value); //4、组织输出 results = new List <PValue> [4]; for (i = 0; i < 4; i++) { results[i] = new List <PValue>(); } tempresults[0][j] = sp; tempresults[1][j] = err; tempresults[2][j] = errrate; tempresults[3][j] = score; //如果越界标识不为0,还需要给每个结果的状态添加越界的标志。 if (invalidflag != 0) { for (i = 0; i < 4; i++) { tempresults[i][j].Status = (long)invalidflag; } } } return(new Results(tempresults, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } catch (Exception ex) { //计算中出任何错误,则需要记录log //LogHelper.Write(LogType.Error, "计算模块错误!"); //记录计算模块的名称、当前标签、起始时间、结束时间 //string moduleInfo = string.Format("——计算模块的名称是:{0},当前计算源标签是:{1},计算起始时间是:{2},计算结束时间是:{3}。", calcuInfo.fmodulename, calcuInfo.sourcetagname, calcuinfo.fstarttime.ToString(), calcuinfo.fendtime.ToString()); //LogHelper.Write(LogType.Error, moduleInfo); //计算引擎报错具体信息 //string errInfo = string.Format("——具体报错信息:{0}。", ex.ToString()); //LogHelper.Write(LogType.Error, errInfo); //返回null供计算引擎处理 _fatalFlag = true; _fatalInfo = ex.ToString(); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } }
public static Results Calcu(List <PValue>[] inputs, CalcuInfo calcuinfo) { //公用变量 bool _errorFlag = false; string _errorInfo = ""; bool _warningFlag = false; string _warningInfo = ""; bool _fatalFlag = false; string _fatalInfo = ""; int i; //0输出初始化:该算法如果没有有效输入值(inputs为null)或者输入值得有效值为null,给出的计算结果。值为0,计算标志位为StatusConst.InputIsNull List <PValue>[] results = new List <PValue> [5]; for (i = 0; i < results.Length; i++) { results[i] = new List <PValue>(); results[i].Add(new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, (long)StatusConst.InputIsNull)); } try { //0、输入 //0.1、输入处理:输入长度。当输入为空时,给出标志位为StatusConst.InputIsNull的计算结果. if (inputs == null || inputs.Length < 3) { _errorFlag = true; _errorInfo = "二维偏差及得分计算要求必须有且仅有三个有效输入。当前输入为空或输入数量不足。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[0] == null || inputs[1] == null || inputs[2] == null) { _errorFlag = true; _errorInfo = "二维偏差及得分计算要求三个输入均不为空。当前输入有空数据。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //0.2、输入处理:截止时刻值。该算法,截止时刻点不需要参与计算,要删除。 //本算法做特别处理 //if (input.Count > 1) input.RemoveAt(input.Count - 1); //0.3、输入处理:标志位。该算法考虑标志位不为0的情况,先过滤这些点。 //********如果计算偏差前,参考量和被考核量,在计算的分钟周期内,分钟值为状态位无效的值。 //********这里会读入这个值,并在下面过滤。如果被过滤,则下面的参考量和被考核量长度判断会报错。并返回不正常状态的计算结果。 for (i = 0; i < inputs.Length; i++) { for (int j = inputs[i].Count - 1; j >= 0; j--) { if (inputs[i][j].Status != 0) { inputs[i].RemoveAt(j); } } } //0.4、输入处理:过滤后结果。 //本算法做特别处理 //——如果去除了截止时刻点,过滤后长度小于1(计算要求至少有一个有效数据),则直接返回全0 //——如果没取除截止时刻点,过滤后长度小于2(计算要求至少有一个有效数据和一个截止时刻值) //if (input.Count < 1) return results; //1、检查并整理数据 //——这里是计算每分钟的偏差。 //——输入量是被评变量每分钟滤波值、参考变量每分钟滤波值 if (inputs[0] == null || inputs[0].Count < 2) { _warningFlag = true; _warningInfo = "二维偏差及得分计算,被测评量正常状态的数据为空"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[1] == null || inputs[1].Count < 2) { _warningFlag = true; _warningInfo = "二维偏差及得分计算,第一个参考量正常状态的数据为空"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (inputs[2] == null || inputs[2].Count < 2) { _warningFlag = true; _warningInfo = "二维偏差及得分计算,第二个参考量正常状态的数据为空"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //1.2、直接读取各变量第一个数据,不需要处理截止时刻值 PValue pvdata; PValue refdataX; PValue refdataY; pvdata = inputs[0][0]; //不使用截止值,被测评量 refdataX = inputs[1][0]; //不使用截止值,参考量1 refdataY = inputs[2][0]; //不使用截止值,参考量2 StatusConst invalidflag = StatusConst.Normal; //参考指标值是否超越偏差曲线x范围,当次指标考核计算是否有效 //2、根据参数获取期望曲线、记分曲线 string[] para = calcuinfo.fparas.Split(new char[] { ';' }); string readMethod = para[0]; //第一个参数是获取曲线的方式,D为动态,S为静态。动态方式,每次计算均读取CSV。静态方式,只读取一次CSV。 int opxid = int.Parse(para[1]); //第二个参数是期望曲线序号 int scoreid = int.Parse(para[2]); //第三个参数是得分曲线序号 double k = double.Parse(para[3]); double b = double.Parse(para[4]); Curve2D opxCurve; //偏差曲线是二维 Curve1D scoreCurve; //得分曲线是一维 string strTemp = CurveConfig.Instance.OPXCurvesReadStatus; //这行这条语句是为了下面在使用CurveConfig.GetXXXCurve时保证Instance已被初始化。20181109调试发现该问题。 try { if (readMethod.ToUpper() == "S") { //静态读取,对于分钟计算来说,只有第一次才读取偏差曲线和得分曲线 opxCurve = CurveConfig.GetOPXCurve2D(opxid, calcuinfo.fstarttime); scoreCurve = CurveConfig.GetScoreCurve(scoreid, calcuinfo.fstarttime); } else { //——20181031,因动态方式读取曲线,速度太慢,不在采用。 //——转而采用在静态方式下读取带生效时间的配置曲线。这样在增加配置曲线时,仅需要暂停计算引擎后重启,即可载入新的配置曲线,并继续进行计算。 //动态读取,对于分钟计算来说,每次计算都要读取偏差曲线和得分曲线 //opxCurve = CurveConfig.ReadOPXCurves2D(calcuinfo.fstarttime).Find(delegate(Curve2D a) { return a.Index == opxid; }); //scoreCurve = CurveConfig.ReadScoreCurves(calcuinfo.fstarttime).Find(delegate(Curve1D a) { return a.Index == scoreid; }); _errorFlag = true; _errorInfo = "参数配置为D,动态读取配置曲线功能已经取消,请使用静态读取方式。"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { _errorFlag = true; _errorInfo = String.Format("读取期望和得分曲线时错误!错误信息:{0}", ex.ToString()); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //判读所需要的曲线是否正确读取,如果没有正确读取一定是返回null,此时要给出错误log if (opxCurve == null) { _errorFlag = true; _errorInfo = String.Format("期望曲线为空,对应的期望曲线:{0}-{1}获取错误!", opxid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } if (scoreCurve == null) { _errorFlag = true; _errorInfo = String.Format("得分曲线为空,对应的得分曲线:{0}-{1}获取错误!", scoreid, calcuinfo.fstarttime.ToString("yyyy-MM-dd HH:mm:ss")); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } //3、计算偏差 PValue sp, err, errrate, score, wscore; //期望值、偏差、偏差比、得分 sp = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); err = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); errrate = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); score = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); wscore = new PValue(0, calcuinfo.fstarttime, calcuinfo.fendtime, 0); //加权得分 //计算期望值,查期望曲线。当参考量超限时,有两种处理方法 //处理方式一:不做处理,返回null,对应时间段没有任何计算结果 /* * if (refdataX.Value < opxCurve.Xvalue[0] || * refdataX.Value > opxCurve.Xvalue[opxCurve.Xvalue.Length - 1] || * refdataY.Value < opxCurve.Yvalue[0] || * refdataY.Value > opxCurve.Yvalue[opxCurve.Yvalue.Length - 1] * ) * { * //如果参考标签的值,在期望曲线参考值有效范围外,则当前时刻点,考核无效,不返回任何考核值 * return null; * } */ //处理方式二:任意一个参考值超限时,按边界值计算偏差,同时所有计算结果状态位置为StatusConst.OutOfValid4Bias //要注意,防止偏差曲线的x不是从小到大。 //参考值X(对应表的第一行配置) if (refdataX.Value < opxCurve.XLowerLimit) { refdataX.Value = opxCurve.XLowerLimit; invalidflag = StatusConst.OutOfValid4Bias; } else if (refdataX.Value > opxCurve.XUpperLimit) { refdataX.Value = opxCurve.XUpperLimit; invalidflag = StatusConst.OutOfValid4Bias; } //参考值Y(对应表的第一列配置) if (refdataY.Value < opxCurve.YLowerLimit) { refdataY.Value = opxCurve.YLowerLimit; invalidflag = StatusConst.OutOfValid4Bias; } else if (refdataY.Value > opxCurve.YUpperLimit) { refdataY.Value = opxCurve.YUpperLimit; invalidflag = StatusConst.OutOfValid4Bias; } //期望值:查期望曲线 - 2d表格 sp.Value = opxCurve.Fx(refdataX.Value, refdataY.Value); //虽然一律要算期望值,但是超限后按边界算期望,所有计算结果标志位都会被置位 //偏差=实际值-期望值 err.Value = pvdata.Value - sp.Value; //偏差比=偏差/期望值 if (sp.Value == 0) { errrate.Value = 0; } else { errrate.Value = err.Value / sp.Value * 100; } //得分:查记分曲线 //——偏差超过限度时,直接返回得分边界值。但是得分不像偏差曲线的x越界,要置invalidflag score.Value = scoreCurve.Fx(err.Value); wscore.Value = k * score.Value + b; //4、录入数据库 MDeviationSOutClass MessageIN = new MDeviationSOutClass(); MessageIN.sp = sp.Value; MessageIN.err = err.Value; MessageIN.errrate = errrate.Value; MessageIN.score = score.Value; MessageIN.wscore = wscore.Value; string year = string.Empty; string month = string.Empty; string day = string.Empty; string hour = string.Empty; year = calcuinfo.fstarttime.Year.ToString(); month = calcuinfo.fstarttime.Month.ToString(); day = calcuinfo.fstarttime.Day.ToString(); hour = calcuinfo.fstarttime.Hour.ToString(); bool isok = BLL.AlgorithmBLL.insertMDeviation2DS(MessageIN, calcuinfo.fsourtagids[0].ToString(), year, month, day, hour, Convert.ToInt32(invalidflag)); if (isok) { return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } else { _fatalFlag = true; _fatalInfo = "MDeviationS数据录入数据库是失败"; return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } } catch (Exception ex) { //计算中出任何错误,则需要记录log //LogHelper.Write(LogType.Error, "计算模块错误!"); //记录计算模块的名称、当前标签、起始时间、结束时间 //string moduleInfo = string.Format("——计算模块的名称是:{0},当前计算源标签是:{1},计算起始时间是:{2},计算结束时间是:{3}。", calcuInfo.fmodulename, calcuInfo.sourcetagname, calcuinfo.fstarttime.ToString(), calcuinfo.fendtime.ToString()); //LogHelper.Write(LogType.Error, moduleInfo); //计算引擎报错具体信息 //string errInfo = string.Format("——具体报错信息:{0}。", ex.ToString()); //LogHelper.Write(LogType.Error, errInfo); //返回null供计算引擎处理 _fatalFlag = true; _fatalInfo = ex.ToString(); return(new Results(results, _errorFlag, _errorInfo, _warningFlag, _warningInfo, _fatalFlag, _fatalInfo)); } }