using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.IO; using System.Xml.Serialization; using System.Collections.ObjectModel; using FObjBase; using FLY.FeedbackRenZiJia.OBJ_INTERFACE; using FLY.FeedbackRenZiJia.Common; using FLY.FeedbackRenZiJia.IService; using FLY.OBJComponents.Server; using FLY.OBJComponents.Common; using System.Net; using FLY.FeedbackRenZiJia.Server.Model; using PropertyChanged; namespace FLY.FeedbackRenZiJia.Server { public class FeedbackHeat : IFeedbackHeat,Misc.ISaveToXml { #region markno const int MARKNO_SAVE = 1; const int MARKNO_LOAD = 2; const int MARKNO_DELAY_ISCONNECTED = 3; #endregion #region IFeedbackHeat 成员 #region 参数 /// /// 作为客户端模式 /// public bool IsClient { get; set; } public IPEndPoint PLCep { get; set; } /// /// 自动控制使能 /// public bool IsAuto { get; set; } /// /// 加热后起效延时(s) /// public int Delay { get; set; } = 10; /// /// 检测功能使能 /// public bool HasCheck { get; set; } /// /// 手动加热步进 /// public int Step { get; set; } = 5; #endregion #region 动态数据 /// /// 通道数, 从测厚仪获取的!!!! /// public int ChannelCnt { get; protected set; } = 44; /// /// 分区数, 从测厚仪获取的!!!! /// public int NBolts { get; protected set; } = 88; /// /// 保存的加热策略名称 /// public string HeatsProductName { get; protected set; }="default"; /// 连接到PLC? /// public bool IsConnectedWithPLC { get; set; } /// /// 上一次加热修改时间 /// public DateTime LastChangedTime { get; set; } = DateTime.MinValue; /// /// 当前 /// //public int[] Currs { get; set; } #endregion #region 异常检测 /// /// 烧了的加热棒 /// [DoNotCheckEquality] public bool[] Bads { get; set; } //{ // get { return mHeatCheck.Bads; } //} /// /// 当前风机启动中 /// public bool HasFan { get; set; } /// /// 加热棒有电流 /// public bool HasElectricity { get; set; } /// /// 线速度 大于 3m/min /// public bool HasFilmVelocity { get; set; } /// /// 检测线速度 使能 /// public bool HasCheckFilmVelocity { get; set; } /// /// 当前正在检测的加热通道 /// public int CheckNo { get; set; } = -2; /// /// 正在检测中 /// public bool CheckEnable { get; set; } = false; #endregion #endregion private IPLCLink plc; private FLY.Thick.Blowing.IService.IBlowing renzijia; private FLY.Thick.Blowing.IService.IBlowingDetect mBDetect; private UInt16 CurrsUpdate=0; public HeatCell mHeatCell = new HeatCell(); HeatChanged mHeatChanged = new HeatChanged(); public HeatBuf mHeatBuf = new HeatBuf(); HeatCheck mHeatCheck = new HeatCheck(); public SnapShotBuf mSnapShotBuf = new SnapShotBuf(); HistoryDb mHistoryDb; /// /// 报警系统 /// WarningSystem mWarning; static FeedbackHeat() { Misc.SaveToXmlHepler.Regist(typeof(HeatCell)); Misc.SaveToXmlHepler.Regist(typeof(HeatBuf)); } public FeedbackHeat() { PLCep = Misc.StringConverter.ToIPEndPoint("192.168.50.60:502"); Load(); if (IsClient) plc = new PLCLink(PLCep); else plc = new HMI(); plc.PropertyChanged += new PropertyChangedEventHandler(hmi_PropertyChanged); HasCheckFilmVelocity = plc.HasElectricity; HasFan = plc.HasFan; InitBuf(); //------------------------------------------------------------------- PropertyChanged += new PropertyChangedEventHandler(FeedbackHeat_PropertyChanged); //------------------------------------------------------------------- //HeatBuf //UpdateTotalDelay(); //------------------------------------------------------------------- //HeatCell mHeatCell.Init(mHeatBuf); mHeatCell.PreHeatApplyEvent = (c) => { //有开风机才能加热 if ((HasCheck) && (!HasFan)) return false;//返回false,不能加热 //检测时,也是使用 HeatApply 确定加热的,要把下面注解掉。所以只能寄望于客户不要按HeatApply //检测中,不能改变加热 //if (mHeatCheck.Enable) // return false; return true; }; mHeatCell.AfterHeatApplyEvent += (c) => { //检测中,不保存加热 if (mHeatCheck.Enable) return; //保存最后一次加热 SaveHeatsLast(); //记录改变!!!! mHeatChanged.Add(mHeatCell.Heats); //复位undo模块 resetUndo(); //记录到数据库 if (lastLcThickHeat != null) { HeatChanged.ITEM heat = mHeatChanged.mItem.Last(); lastLcThickHeat.HTime = heat.Time; lastLcThickHeat.Heats = heat.Heats; setLcThickHeatIsStable(lastLcThickHeat); mHistoryDb.AddThickHeat(lastLcThickHeat); } }; mHeatCell.AfterClearOffsetsEvent += (c) => { //复位undo模块 resetUndo(); }; mHeatCell.PropertyChanged += new PropertyChangedEventHandler(mHeatCell_PropertyChanged);//当HeatCell.Heats 修改后,设置到HMI Misc.BindingOperations.SetBinding(mHeatCell, "Kp", mHeatBuf, "Kp", Misc.BindingOperations.BindingMode.TwoWay); if (!Directory.Exists("feedbackheats")) Directory.CreateDirectory("feedbackheats"); //------------------------------------------------------------------- //HeatChanged Misc.BindingOperations.SetBinding(this.mHeatChanged, "Last", this, "LastChangedTime"); //------------------------------------------------------------------- //HeatCheck mHeatCheck.Init(mHeatCell); mHeatCheck.PropertyChanged += new PropertyChangedEventHandler(mHeatCheck_PropertyChanged); Misc.BindingOperations.SetBinding(this, "HasElectricity", mHeatCheck, "HasElectricity"); Misc.BindingOperations.SetBinding(this, "HasCheck", mHeatCheck, "Has"); Misc.BindingOperations.SetBinding(mHeatCheck, "Enable", this, "CheckEnable", Misc.BindingOperations.BindingMode.TwoWay); Misc.BindingOperations.SetBinding(mHeatCheck, "CheckNo", this, "CheckNo"); Misc.BindingOperations.SetBinding(mHeatCheck, "Bads", this, "Bads"); //mHeatCheck.PropertyChanged += (s, e) => //{ // if (e.PropertyName == "Bads") // { // NotifyPropertyChanged("Bads"); // } //}; if (plc is PLCLink) { ((PLCLink)plc).Start(); } } void mHeatCheck_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Enable") { if (mHeatCheck.Enable) { if (!IsConnectedWithPLC) { //连接断开 mHeatCheck.Enable = false; } else if ((HasCheck) && (!HasFan)) { //风机没启动,要关闭加热 mHeatCheck.Enable = false; } else { //需要关闭自控 IsAuto = false; } } } } #region 异常 void InitError() { //-------------------------------------------------------------------------------- //连接断开事件 FObjBase.PollModule.Current.Poll_Config(PollModule.POLL_CONFIG.ADD, () => { Misc.BindingOperations.SetBinding(this, "IsConnectedWithPLC", () => { bool b = !IsConnectedWithPLC; ERR_STATE state = b ? ERR_STATE.ON : ERR_STATE.OFF; ERRNO errno = ERRNOs.ERRNO_PLC_DISCONNECTED; mWarning.Add(errno.Code, errno.Descrption, state); }); }, TimeSpan.FromSeconds(3), true, false, this, MARKNO_DELAY_ISCONNECTED, true); //------------------------------------------------------------------------ //检测功能的结果 this.PropertyChanged += (s, e) => { if (e.PropertyName == "HasCheck") { if (!HasCheck) { ERRNO[] eRRNOs = new ERRNO[] { ERRNOs.ERRNO_NO_FAN, ERRNOs.ERRNO_OPEN_CIRCUIT, ERRNOs.ERRNO_SHORT_CIRCUIT, ERRNOs.ERRNO_POWER_OFF }; foreach (ERRNO eRRNO in eRRNOs) { mWarning.Add(eRRNO.Code, eRRNO.Descrption, ERR_STATE.OFF); } } return; } else if (e.PropertyName == "HasFan") { if (HasCheck) { ERR_STATE state = (!HasFan) ? ERR_STATE.ON : ERR_STATE.OFF; ERRNO errno = ERRNOs.ERRNO_NO_FAN; mWarning.Add(errno.Code, errno.Descrption, state); } } }; //------------------------------------------------------------------------ //检测功能的结果 Dictionary error_property = new Dictionary(); error_property.Add("OpenCircuit", ERRNOs.ERRNO_OPEN_CIRCUIT); error_property.Add("ShortCircuit", ERRNOs.ERRNO_SHORT_CIRCUIT); error_property.Add("PowerOff", ERRNOs.ERRNO_POWER_OFF); mHeatCheck.PropertyChanged += (s, e) => { if (HasCheck) { if (error_property.ContainsKey(e.PropertyName)) { bool b = (bool)Misc.PropertiesManager.GetValue(mHeatCheck, e.PropertyName); ERR_STATE state = b ? ERR_STATE.ON : ERR_STATE.OFF; ERRNO errno = error_property[e.PropertyName]; mWarning.Add(errno.Code, errno.Descrption, state); } } }; //------------------------------------------------------------------------ //线速度异常 this.PropertyChanged += (s, e) => { if (e.PropertyName == "HasCheckFilmVelocity") { if (!HasCheckFilmVelocity) { ERRNO eRRNO = ERRNOs.ERRNO_NO_VELOCITY; mWarning.Add(eRRNO.Code, eRRNO.Descrption, ERR_STATE.OFF); } } else if (e.PropertyName == "HasFilmVelocity") { if (HasCheckFilmVelocity) { ERR_STATE b = (!HasFilmVelocity) ? ERR_STATE.ON : ERR_STATE.OFF; ERRNO eRRNO = ERRNOs.ERRNO_NO_VELOCITY; mWarning.Add(eRRNO.Code, eRRNO.Descrption, b); } } }; } #endregion void InitBuf() { plc.ChannelCnt = (UInt16)ChannelCnt; //Currs = new int[ChannelCnt]; mHeatChanged.Init(ChannelCnt); mHeatBuf.Init(NBolts, ChannelCnt); mHeatCell.Init2(NBolts, ChannelCnt); //复位undo模块 resetUndo(); mSnapShotBuf.Init(NBolts, ChannelCnt); } void hmi_PropertyChanged(object s, PropertyChangedEventArgs _e) { FObjBase.PollModule.Current.Dispatcher.Invoke( new PropertyChangedEventHandler( (sender, e)=> { if (e.PropertyName == "Errno") { if (plc.Errno == -1) { IsConnectedWithPLC = false; } else { IsConnectedWithPLC = true; //刚成功连接上 //重新复制数据到 PLC RecoverPLC(); } } else if (e.PropertyName == "HasElectricity") { HasElectricity = plc.HasElectricity; } else if (e.PropertyName == "HasFan") { HasFan = plc.HasFan; } }), s, _e); } List test_data = new List(); public int TestIndex { get; set; } = 0; bool TestMode = false; /// /// 从历史数据加载,模拟整个过程 /// public void Test() { TestMode = true; //if (TestIndex == 0) //{ // StreamReader sr = new StreamReader("test.csv", Encoding.GetEncoding("GB2312")); // string header = sr.ReadLine(); // while (!sr.EndOfStream) // { // string str = sr.ReadLine(); // FlyData_FeedbackHeat d = new FlyData_FeedbackHeat(); // d.TryParse(header, str); // test_data.Add(d); // } // sr.Close(); // NBolts = test_data[0].Thicks.Count(); // ChannelCnt = test_data[0].Heats.Count(); // Add( // test_data[0].Time, // test_data[0].Direction, // test_data[0].OrgBoltNo, // test_data[0].RotatePeriod, // test_data[0].Thicks); //} //if (TestIndex < test_data.Count()) //{ // int i = TestIndex; // FlyData_FeedbackHeat d = test_data[i]; // mHeatChanged.Test_SetHeats(d.Heats, d.HTime); // mHeatCell.ModifyPreHeats(d.Heats); // mHeatCell.HeatApply(); // Add(i + 1, d.Time, d.Direction, d.OrgBoltNo, d.Thicks); // TestIndex++; //} TestMode = false; } void mHeatCell_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "Heats") { Output(mHeatCell.Heats.ToArray()); } } void FeedbackHeat_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (GetSavePropertyNames().Contains(e.PropertyName)) { //PollModule.Current.Poll_JustOnce(new PollModule.PollHandler(delegate() //{ // //Save();//数据滞后保存!!!!! //}), this, MARKNO_SAVE); } if (e.PropertyName == "ChannelCnt") { //重新初始化!!!! InitBuf(); //滞后触发 PollModule.Current.Poll_JustOnce(new PollModule.PollHandler(delegate () { LoadHeatsLast(); }), this, MARKNO_LOAD); } else if (e.PropertyName == "NBolts") { //重新初始化!!!! InitBuf(); } else if (e.PropertyName == "Delay") { //重新计算总延时!!!! UpdateTotalDelay(); } else if (e.PropertyName == "HasFan") { if ((HasCheck) &&(!HasFan)) { //风机没开,停止一切动作。 StopAnyHeating(); } } else if (e.PropertyName == "IsAuto") { if (IsAuto) { //启动了自控,应该关闭检测 mHeatCheck.Enable = false; } } else if (e.PropertyName == "IsConnectedWithPLC") { if (!IsConnectedWithPLC) { mHeatCheck.Enable = false; IsAuto = false; } } } public void Init( FLY.Thick.Blowing.Client.BlowingServiceClient renzijia, FLY.Thick.Blowing.IService.IBlowingDetect bDetect, FLY.OBJComponents.Server.WarningSystem warning, HistoryDb historyDb ) { this.renzijia = renzijia; mBDetect = bDetect; mWarning = warning; mHistoryDb = historyDb; //新数据,与参数被修改时,都会收到事件,应该区别开 renzijia.DataEvent += (s, e) => { Add( e.Time, e.EndTime, e.IsBackw, e.RPeriod, e.RCnt, e.OrgBoltNo, e.RAngle, e.FilmLength, e.FilmVelocity, e.K, e.Thicks, e.Boltmap ); }; Misc.BindingOperations.SetBinding(renzijia, "NBolts", this, "NBolts"); Misc.BindingOperations.SetBinding(renzijia, "ChannelCnt", this, "ChannelCnt"); mBDetect.PropertyChanged += MBDetect_PropertyChanged; InitError(); } private void MBDetect_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "FilmLength") { UpdateTotalDelay(); } else if (e.PropertyName == "FilmVelocity") { UpdateTotalDelay(); HasFilmVelocity = (mBDetect.FilmVelocity > 3);//大于3m/min 正常生产 //当线速度 很小,肯定是没生产,停止加热,停止自控 if (HasCheckFilmVelocity && (!HasFilmVelocity)) { StopAnyHeating(); //停止自控 IsAuto = false; } } } /// /// 停止任何加热行为 /// void StopAnyHeating() { //停止自控 IsAuto = false; //备份 加热策略 int[] preheats = mHeatCell.PreHeats.ToArray(); bool needrestore = true; //停止检测加热棒 if (mHeatCheck.Enable) { mHeatCheck.Enable = false; needrestore = false; } //停止加热 mHeatCell.ClearPreHeats(); mHeatCell.HeatApply(); //恢复 加热策略 if(needrestore) mHeatCell.ModifyPreHeats(preheats); } void UpdateTotalDelay() { if (mBDetect == null) return; if (mBDetect.FilmVelocity < 10) return; int totaldelay = (int)(Delay + mBDetect.FilmLength / mBDetect.FilmVelocity * 60); int t = (int)(mHeatBuf.Delay.TotalSeconds); if (Math.Abs(totaldelay - t) > 5) { mHeatBuf.Delay = TimeSpan.FromSeconds(totaldelay); } } UInt16[] last_heat; UInt16 last_heatupdate; void Output(int[] heats) { List list = new List(); for (int i = 0; i < ChannelCnt; i++) { UInt16 heat = (UInt16)heats[i]; if (heat < 0) heat = 0; else if (heat > 100) heat = 100; list.Add(heat); } plc.SetHeat(list); last_heat = list.ToArray(); UInt16 heatupdate = (UInt16)(plc.HeatUpdate_R + 1); if (heatupdate >100) heatupdate = 1; plc.HeatUpdate = heatupdate; last_heatupdate = heatupdate; } void RecoverPLC() { plc.ChannelCnt = (UInt16)ChannelCnt; if (last_heat != null) { plc.SetHeat(last_heat); plc.HeatUpdate = last_heatupdate; } else { //设置全部0进去 plc.SetHeat(new UInt16[ChannelCnt]); plc.HeatUpdate = plc.HeatUpdate; } } void setLcThickHeatIsStable(Lc_ThickHeat lcThickHeat) { DateTime time = lcThickHeat.Time; DateTime htime = lcThickHeat.HTime; double filmLength = lcThickHeat.FilmLength; double filmVelocity = lcThickHeat.FilmVelocity; lcThickHeat.IsStable = time > (htime + TimeSpan.FromSeconds(Delay) + TimeSpan.FromMinutes(filmLength / filmVelocity)); } /// /// 最后一次保存的记录 /// Lc_ThickHeat lastLcThickHeat=null; void Add(DateTime time, DateTime endtime, bool isBackw, TimeSpan period, int rotateCnt, int orgboltno, double rAngle, double filmLength, double filmVelocity, double K, double[] frame, List boltMaps) { //1.求平均值 //2.每个螺丝,加热量的增量 = (Thick - Avg) * Factor; //3.求出输出量, 输出值 = 当前值 + 增量 if (frame.Count() != NBolts) return; //检测数据有效性 int invalid_cnt = frame.Count( (_d) => {return double.IsNaN(_d);} ); if (invalid_cnt > NBolts / 2)//有一半数据都是无效的 return; HeatChanged.ITEM heat = mHeatChanged.mItem.Last(); var lcThickHeat = new Lc_ThickHeat() { Time = time, EndTime = endtime, IsBackw = isBackw, RPeriod = period, RCnt = rotateCnt, OrgBoltNo = orgboltno, RAngle = rAngle, FilmLength = filmLength, FilmVelocity = filmVelocity, K = K, Thicks = frame, Boltmap = boltMaps, HTime = heat.Time, Heats = heat.Heats }; setLcThickHeatIsStable(lcThickHeat); mHistoryDb.AddThickHeat(lcThickHeat); lastLcThickHeat = lcThickHeat; mHeatBuf.IsIntoAutoONo = mHeatCell.IsInAutoONo; //添加到缓冲区, 计算分区平移 if (!mHeatBuf.Add( time, endtime, isBackw?Misc.DIRECTION.BACKWARD:Misc.DIRECTION.FORWARD, period, rotateCnt, orgboltno, rAngle, filmLength, RealThicksToFrames(frame), boltMaps, heat.Time, heat.Heats)) { //数据有问题,不需要在处理 return; } if (mHeatBuf.IsIntoAutoONo != mHeatCell.IsInAutoONo)//状态切换了, 只能是变为false { if (mHeatBuf.IsIntoAutoONo == false) { if (mHeatBuf.AutoONoResult == EAutoONoResult.OK) { if (renzijia.OrgBoltNo != mHeatBuf.BestOrgBoltNo) { renzijia.OrgBoltNo = mHeatBuf.BestOrgBoltNo; renzijia.Apply(); } } //对位完成 mHeatCell.IsAutoONo = false; } } FlyData_FeedbackHeat d = mHeatBuf.mData.Last(); mHeatCell.SetThickPercents(d.Thicks); if (IsAuto && lcThickHeat.IsStable)//如果自控中!!! { mHeatCell.Auto(); } } int[] RealThicksToFrames(IEnumerable realthicks) { return realthicks.Select(t => { if (double.IsNaN(t)) return Misc.MyBase.NULL_VALUE; else return (int)(t * 100); }).ToArray(); } bool Load() { return Misc.SaveToXmlHepler.Load("feedback.xml", this); } void Save() { Misc.SaveToXmlHepler.Save("feedback.xml", this); } #region 撤销 /// /// undo 的序号 /// public int UndoIdx { get; private set; } /// /// 在撤销中 /// public bool IsInUndo { get; private set; } /// /// 这次撤销动作的时间 /// public DateTime UndoTime { get; private set; } //读取 UndoIdx 的 加热 public void Undo() { updateUodo(UndoIdx - 1); } public void Redo() { updateUodo(UndoIdx + 1); } void updateUodo(int idx) { if (idx < 0) return; else if (idx >= mHeatChanged.mItem.Count()) return; mHeatCell.ModifyPreHeats(mHeatChanged.mItem[idx].Heats); UndoTime = mHeatChanged.mItem[idx].Time; IsInUndo = idx <= mHeatChanged.mItem.Count() - 1; UndoIdx = idx; } void resetUndo() { UndoIdx = mHeatChanged.mItem.Count() - 1; UndoTime = DateTime.MinValue; IsInUndo = false; } #endregion #region IFeedbackHeat 成员 public void Apply() { Save(); } private void SaveHeatsLast() { string path = @"feedbackheats\最后记录.xml"; FeedbackHeatsMark mark = new FeedbackHeatsMark(); mark.heats = mHeatCell.Heats.ToArray(); mark.Save(path); } private void LoadHeatsLast() { string path = @"feedbackheats\最后记录.xml"; FeedbackHeatsMark mark = FeedbackHeatsMark.Load(path); if ((mark != null) && (mark.heats != null)) { mHeatCell.ModifyPreHeats(mark.heats); } } #region 加热配置读写 public void SaveHeats(string productname) { string path = @"feedbackheats\" + productname + ".xml"; HeatsProductName = productname; FeedbackHeatsMark mark = new FeedbackHeatsMark(); mark.heats = mHeatCell.Heats.ToArray(); mark.Save(path); } /// /// 获取保存的加热量文件列表 返回类型为 List<string> /// /// /// public void GetHeatsFileList(AsyncCBHandler AsyncDelegate, object AsyncState) { DirectoryInfo dinfo = new DirectoryInfo(@"feedbackheats"); List filenames = new List(); if (dinfo.Exists) { foreach (FileInfo info in dinfo.GetFiles()) { if (Path.GetExtension(info.Name) == ".xml") { filenames.Add(Path.GetFileNameWithoutExtension(info.Name)); } } } AsyncDelegate(AsyncState, filenames); } /// /// 删除 加热量文件 /// /// public void DelHeatsFile(string productname) { string path = @"feedbackheats\" + productname + ".xml"; if (System.IO.File.Exists(path)) System.IO.File.Delete(path); } /// /// 加载 加热量文件 /// /// public void LoadHeatsFile(string productname) { string path = @"feedbackheats\" + productname + ".xml"; HeatsProductName = productname; FeedbackHeatsMark mark = FeedbackHeatsMark.Load(path); if ((mark != null) && (mark.heats != null)) { mHeatCell.ModifyPreHeats(mark.heats); } } #endregion #endregion #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(string propertyname) { if (PropertyChanged != null) { PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname)); } } #endregion #region ISaveToXml 成员 public string[] GetSavePropertyNames() { return new string[]{ "Step", "Delay", "HasCheck", "HeatsProductName", "HasCheckFilmVelocity", "IsClient", "PLCep" }; } #endregion } }