private volatile int sleepTimes = 0;//对休眠次数计数 /// <summary> /// 处理PLC打包指令,每SENDTOPLC_INTERVAL毫秒发一次, /// 同一PLC上次与本次间隔为PLC个数*SENDTOPLC_INTERVAL,此值过大,会造成灯指令覆盖 /// </summary> void HandlePackPLCComm() { while (true) { try { int[] strKey = null; lock (dicSendWindow) { int dicCount = dicSendWindow.Keys.Count; strKey = new int[dicCount]; dicSendWindow.Keys.CopyTo(strKey, 0); } int PLCCount = strKey.Length; for (int i = 0; i < PLCCount; i++) { int key = strKey[i]; PLCCommand command = null; lock (dicSendWindow) { command = dicSendWindow[strKey[i]]; } if (!command.Send) { Interlocked.Add(ref sleepTimes, 1);//加1 WinApi_PLC.SendDataAllOut((byte)key, command.ThirdFrame, command.FourthFrame); //bl_plc.SendDataAllOut((byte)key, command.ThirdFrame, command.FourthFrame); command.Send = true;//更新为已发送 lock (dicSendWindow) { dicSendWindow[key] = command; } //每个包之间都要间隔200毫秒,不然plc接收会粘包,而且plc同地址会中断 Thread.Sleep(SENDTOPLC_INTERVAL); } } //循环结束后,sleepTimes=0;休眠中断间隔,=1休眠剩余时间间隔 if (sleepTimes <= 1) { int sleepInterval = PLC_INTERRUPT_INTERVAL - sleepTimes * SENDTOPLC_INTERVAL; Thread.Sleep(sleepInterval < 0 ? 0 : sleepInterval); } Interlocked.Add(ref sleepTimes, -sleepTimes);//0 } catch (Exception ex) { Thread.Sleep(PLC_INTERRUPT_INTERVAL); InternalLogger.Log.Error("处理PLC打包指令出错:" + ex.Message); } } }
/// <summary> ///打包帧 /// </summary> /// <param name="command"></param> /// <param name="lightAddress">从0开始到15</param> /// <param name="lightStatus"></param> void SetFrame(PLCCommand command, int lightAddress, byte lightStatus) { if (lightAddress >= 0 && lightAddress <= 7) { command.ThirdFrame = SetBit(command.ThirdFrame, 8 - lightAddress, lightStatus == 1);//设置第三帧 } else { command.FourthFrame = SetBit(command.FourthFrame, 16 - lightAddress, lightStatus == 1);//设置第四帧 } }
/// <summary> /// 打包PLC指令 /// PLC首次,将plc地址保存,将<led地址,发送时刻>保存;指令打包并保存 /// PLC非首次,但LED首次,将指令打包保存 /// PLC非首次且LED非首次,判断间隔是否满足设定;(若满足,将指令打包保存;若不满足,则新线程等待设定时间后,打包保存) /// </summary> /// <param name="plcAddress">PLC地址号</param> /// <param name="lightAddress">PLC控制输出端口号(一般16个)</param> /// <param name="lightStatus">1开灯,0关灯</param> /// <param name="lightTime">灯开闭间隔</param> private void ControlPLCInterval(int plcAddress, int lightAddress, byte lightStatus, int lightTime) { try { double curTime = (DateTime.Now.ToUniversalTime() - baseDatatime).TotalMilliseconds; lock (dicSendWindow) { PLCCommand command = null; if (dicSendWindow.ContainsKey(plcAddress)) { dicSendWindow.TryGetValue(plcAddress, out command); if (command.DicLightSendTime.ContainsKey(lightAddress))//此灯已发送过 { double preTime = 0; command.DicLightSendTime.TryGetValue(lightAddress, out preTime); double totalTime = curTime - preTime; if (totalTime < lightTime)//时间间隔小于设定值 { int sleepReply = (int)Math.Ceiling(lightTime - totalTime); #region 在sleep前,需把本次的时间戳保存,防止后面还有指令进来 command.DicLightSendTime[lightAddress] = curTime + sleepReply; //更新发送时刻 dicSendWindow[plcAddress] = command; //更新 #endregion smartThreadPool.QueueWorkItem(() => { //新线程,先sleep(lightTime-totalTime)后,再lock放入dicSendWindow Thread.Sleep(sleepReply); lock (dicSendWindow) { SetFrame(command, lightAddress, lightStatus); command.DicLightSendTime[lightAddress] = curTime + sleepReply; //更新发送时刻 command.Send = false; dicSendWindow[plcAddress] = command; //更新 } }); return;//直接退出,不要删除 } else//大于设定间隔 { command.DicLightSendTime[lightAddress] = curTime;//更新发送时刻 } } else//此灯第一次发送 { command.DicLightSendTime.Add(lightAddress, curTime);//将plc下灯地址发送时刻保存 } SetFrame(command, lightAddress, lightStatus); command.Send = false; dicSendWindow[plcAddress] = command;//更新 } else { command = new PLCCommand(); //command.Send = false;默认 SetFrame(command, lightAddress, lightStatus); command.DicLightSendTime.Add(lightAddress, curTime); //将plc下灯地址发送时刻保存 dicSendWindow.Add(plcAddress, command); //新增 } } } catch (Exception ex) { InternalLogger.Log.Error("打包PLC指令出错:" + ex.Message); } }