公司网站设计规划,飞鸽crm电销系统,成免费的crm无需下载,在小说网站做编辑怎么找一、源码结构 二、运行效果 三、源码解析
PLC批量读写点对点更新数据类型处理
优点#xff1a;根据数据类型#xff0c;判定监听的地址范围#xff08;40120_int 监听两个word#xff1a;40120 40121#xff1b;40130_long 监听四个word#xff1a;40130 40131 40132 4…一、源码结构 二、运行效果 三、源码解析
PLC批量读写点对点更新数据类型处理
优点根据数据类型判定监听的地址范围40120_int 监听两个word40120 4012140130_long 监听四个word40130 40131 40132 40133添加到UI字典中PLC批量读取判定数据变化查找控件集合点对点更新效率高
实现流程 1. 读取配置文件及创建变量信息点位名称地址数据类型bool/short/int/float/long/double 2. 自定义控件绑定参数用UI字典存储通过属性get方式如果是bool类型直接取Bool字典的点位数据如果是Word类型根据数据类型拼装Word字典中的word数据得到对应数据类型的点位数据通过set方式加入到写队列。 using PLCBind.CustomControls;
using PLCBind.Service;
using PLCBind.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;namespace PLCBind
{public partial class MainForm : Form{ public MainForm(){InitializeComponent();}private void MainForm_Load(object sender, EventArgs e){CommonMethods.LoadVar();// 读取配置文件及创建变量信息点位名称地址类型PLCService.Init(); // 读任务写任务,数据有变化时事件广播通知自定义控件预先绑定事件BindControlParamModel();// 为按钮绑定参数}/// summary/// 为控件绑定参数/// /summaryprivate void BindControlParamModel(){InitControlTag(); SetControlParamModel();}/// summary/// 绑定控件变量/// /summaryvoid InitControlTag(){// 左皮带lblYX1.Tag ucBeltLeft1.Tag 00101;// 启动lblZS1.Tag 40101;// 转速lblDY1.Tag 40120;// 电压lblDL1.Tag 40130;// 电流 }/// summary/// 赋值控件参数/// /summaryvoid SetControlParamModel(){foreach (Control item in this.pnlMain.Controls){if (item is ITransferUI objItem){var address item.Tag.ToString();var common CommonMethods.HomeVariables.Where(obj obj.PLCAddress address).FirstOrDefault();if (common ! null){objItem.ParamModel common;Liststring lstAddress null;switch (common.DataType){case DataType.Bool:lstAddress PLCService.RangeAddress(common.PLCAddress, 0);break;case DataType.Short:lstAddress PLCService.RangeAddress(common.PLCAddress, 0);break;case DataType.Int:case DataType.Float:lstAddress PLCService.RangeAddress(common.PLCAddress, 2);// 40120 监听两个word40120 40121break;case DataType.Long:case DataType.Double:lstAddress PLCService.RangeAddress(common.PLCAddress, 4);// 40130 监听四个word40130 40131 40132 40133break;} foreach (var range in lstAddress){ CommonMethods.AddControl(CommonMethods.DicHomeControl, range, item);} }}}}private void tabControl1_SelectedIndexChanged(object sender, EventArgs e){var index tabControl1.SelectedIndex;switch (index){case 0:this.ucParameter1.RemoveParams();break;case 1:// 参数设置this.pnlSet.Controls.Clear();this.pnlSet.Controls.Add(ucParameter1);this.ucParameter1.ListParams CommonMethods.SetVariables.Where(s s.Group 顺序启动参数).ToList();break;}} }
} using PLCBind.Service;namespace PLCBind.UIForm
{public class BaseParams{/// summary/// 描述/// /summarypublic string Description { get; set; }/// summary/// PLC地址, 多个输入时用;分隔开/// /summarypublic string PLCAddress { get; set; } /// summary/// 数据类型/// /summarypublic DataType DataType { get; set; }/// summary/// 数据分组/// /summarypublic string Group { get; set; }/// summary/// 单位/// /summarypublic string Unit { get; set; }/// summary/// 设置与获取PLC值/// /summarypublic object PLCValue{get{object obj null; switch (DataType){case DataType.Bool:obj PLCService.GetBool(PLCAddress);break;case DataType.Short:obj PLCService.GetShort(PLCAddress);break;case DataType.Int:obj PLCService.GetInt(PLCAddress);break;case DataType.Float:obj PLCService.GetFloat(PLCAddress);break;case DataType.Long:obj PLCService.GetLong(PLCAddress);break;case DataType.Double:obj PLCService.GetDouble(PLCAddress);break;} return obj;}set{PLCService.AddWriteVariable(PLCAddress, value, DataType);}} }
} 3. 异步任务处理读任务写任务,将读到的数据存到Data字典中判断数据是否有发生变化如果数据有变化通过UI字典获取控件集合调用更新方法 using HslCommunication;
using HslCommunication.Core;
using HslCommunication.ModBus;
using PLCBind.CustomControls;
using PLCBind.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using UtilHelper;namespace PLCBind.Service
{public class PLCService{ public static ConcurrentDictionarystring, bool DicBoolData new ConcurrentDictionarystring, bool();public static ConcurrentDictionarystring, Word DicWordData new ConcurrentDictionarystring, Word();public static ConcurrentDictionarystring, Word DicWordChange new ConcurrentDictionarystring, Word();//static ModbusTcpNet client null;static IByteTransform byteTransform;static ConcurrentQueuePLCModel queueWrite new ConcurrentQueuePLCModel();// UI通知static void NoticeUI(string address, ConcurrentDictionarystring, ListControl dicControl){dicControl.TryGetValue(address, out ListControl lstControl);if (null ! lstControl){foreach (var item in lstControl){if (item is ITransferUI objItem){objItem.NoticeChange();}}}}/// summary/// 事件触发/// /summary public static void DataChange(string address){Task.Run(() NoticeUI(address, CommonMethods.DicHomeControl));Task.Run(() NoticeUI(address, CommonMethods.DicSetControl));}/// summary/// 自定义控件内接收到数据变化事件根据传入address以及DataType查询监听地址所需要的监听范围40120_int 监听两个word40120 4012140130_long 监听四个word40130 40131 40132 40133判断是否属于本控件监听/// /summarypublic static Liststring RangeAddress(string address, int length){Liststring lstaddress new Liststring();if (0 length){lstaddress.Add(address);}else{for (int i 0; i length; i){lstaddress.Add(FillAddress((DataHelper.Obj2Int(address) i).ToString()));}}return lstaddress;}/// summary/// 读取时按位补充0/// /summarypublic static string FillAddress(string val, int length 5){return val.PadLeft(length, 0);}/// summary/// 写入时格式化地址如40101 - 101/// /summary public static string FormatAddress(string val){if (val.Length 5) return val;return val.Substring(1, val.Length - 1);}/// summary/// 初始化plc通信开启读写任务/// /summarypublic static void Init(){client new ModbusTcpNet(CommonMethods.PLCConfig.HostAddress, CommonMethods.PLCConfig.PortNumber);client.AddressStartWithZero false;client.DataFormat DataFormat.CDAB;byteTransform client.ByteTransform;TskPlcRead();TskPlcWrite();}/// summary/// 获取boolbool类型/// /summary/// param nameaddress/param/// returns/returnspublic static bool GetBool(string address){ try{bool exist DicBoolData.TryGetValue(address, out var value);// 字典存储if (!exist){Logger.Info($[Error] PLCService,GetBool,errmsg:查无点位数据({address}));}return value;}catch (Exception ex){Logger.Info([Error] PLCService,GetBool,errmsg: ex.Message);}return false;}/// summary/// 获取word1个word2个字节/// /summary static Word GetAddressWord(string address, int add){address FillAddress((Convert.ToInt32(address) add).ToString());bool exist DicWordData.TryGetValue(address, out var value);if (!exist){Logger.Info($[Error] PLCService,GetAddressWord,errmsg:查无点位数据({address}));}return value;}/// summary/// 拼接字节多个word/// /summary static byte[] JoinAddressWord(string address, DataType datatype){byte[] ret null;switch (datatype){ case DataType.Short:{ var buff GetAddressWord(address, 0);ret new byte[2] { buff.Byte1, buff.Byte2 };}break;case DataType.Int:case DataType.Float:{var buff1 GetAddressWord(address, 0);var buff2 GetAddressWord(address, 1);ret new byte[4] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2 };} break;case DataType.Long: case DataType.Double:{var buff1 GetAddressWord(address, 0);var buff2 GetAddressWord(address, 1);var buff3 GetAddressWord(address, 2);var buff4 GetAddressWord(address, 3);ret new byte[8] { buff1.Byte1, buff1.Byte2, buff2.Byte1, buff2.Byte2, buff3.Byte1, buff3.Byte2, buff4.Byte1, buff4.Byte2 };} break;}return ret;}public static ushort GetShort(string address){try{ var buff JoinAddressWord(address, DataType.Short);return byteTransform.TransUInt16(buff, 0); }catch (Exception ex){Logger.Info([Error] PLCService,GetShort,errmsg: ex.Message);}return 0;}public static uint GetInt(string address){try{var buff JoinAddressWord(address, DataType.Int);return byteTransform.TransUInt32(buff, 0);}catch (Exception ex){Logger.Info([Error] PLCService,GetInt,errmsg: ex.Message);}return 0;}public static float GetFloat(string address){try{var buff JoinAddressWord(address, DataType.Float);return byteTransform.TransSingle(buff, 0);}catch (Exception ex){Logger.Info([Error] PLCService,GetFloat,errmsg: ex.Message);}return 0;}public static ulong GetLong(string address){try{var buff JoinAddressWord(address, DataType.Long);return byteTransform.TransUInt64(buff, 0);}catch (Exception ex){Logger.Info([Error] PLCService,GetLong,errmsg: ex.Message);}return 0;}public static double GetDouble(string address){try{var buff JoinAddressWord(address, DataType.Double);return byteTransform.TransDouble(buff, 0);}catch (Exception ex){Logger.Info([Error] PLCService,GetDouble,errmsg: ex.Message);}return 0;}/// summary/// 定时读取/// /summarystatic void TskPlcRead(){Task.Factory.StartNew(async () {var start_c CommonMethods.PLCConfig.ReadStart_Coil;var start_h CommonMethods.PLCConfig.ReadStart_Holding;bool[] temp_c null; bool init_c false;byte[] temp_h null; bool init_h false;while (!CommonMethods.CTS.IsCancellationRequested){try{DicWordChange.Clear();var array_c (await client.ReadBoolAsync(start_c, (ushort)CommonMethods.PLCConfig.ReadCount_Coil)).Content;var array_h (await client.ReadAsync(start_h, (ushort)(CommonMethods.PLCConfig.ReadCount_Holding * 2))).Content;// ushort占两个字节if (null ! array_c){// bool类型只占1位数据有变化直接通知if (null temp_c){init_c true;temp_c new bool[array_c.Length];}CheckBoolChange(0, start_c, temp_c, array_c, init_c); init_c false;Array.Copy(array_c, temp_c, array_c.Length);}if (null ! array_h){// word类型数据位(2,4,8)所以要先读取全部的数据再通知变化if (null temp_h){init_h true;temp_h new byte[array_h.Length];}CheckWordChange(4, start_h, temp_h, array_h, init_h); init_h false;Array.Copy(array_h, temp_h, array_h.Length);if (DicWordChange.Count 0){foreach (var item in DicWordChange){DataChange(item.Key);}}}}catch (Exception ex){Logger.Info([Error] PLCMgr,TskPlcRead,errmsg ex.Message);}await Task.Delay(100);}}, TaskCreationOptions.LongRunning);}/// summary/// 检查数据是否有变化bool类型/// /summary public static void CheckBoolChange(string flg, string start, bool[] oldbuffer, bool[] newbuffer, bool init){for (int i 0; i newbuffer.Length; i){// 00101string address flg FillAddress((i Convert.ToInt32(start)).ToString(), 4); bool value newbuffer[i];DicBoolData.AddOrUpdate1(address, value); if (init || oldbuffer[i] ! value){ DataChange(address);}}}/// summary/// 检查数据是否有变化word类型/// /summary public static void CheckWordChange(string flg, string start, byte[] oldbuffer, byte[] newbuffer, bool init){int index 0;for (int i 0; i newbuffer.Length; i i 2){// 40101string address flg FillAddress((index Convert.ToInt32(start)).ToString(), 4); index; byte byte1 newbuffer[i];byte byte2 newbuffer[i 1];Word buff new Word() { Byte1 byte1, Byte2 byte2 };DicWordData.AddOrUpdate1(address, buff);if (init || (oldbuffer[i] ! byte1 || oldbuffer[i 1] ! byte2)){DicWordChange.AddOrUpdate1(address, buff);}}}/// summary/// 添加写入值/// /summary public static void AddWriteVariable(string address, object value, DataType datatype){queueWrite.Enqueue(new PLCModel() { Address address, Value value, PLCDataType datatype });//加载值进队列} /// summary/// 定时写入/// /summarystatic void TskPlcWrite(){ Task.Factory.StartNew(async () {while (!CommonMethods.CTS.IsCancellationRequested){try{if (!queueWrite.IsEmpty){PLCModel model null; OperateResult result null;queueWrite.TryDequeue(out model);var dataype model.PLCDataType;switch (dataype){case DataType.Bool:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToBoolean(model.Value));break;case DataType.Short:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt16(model.Value)); break;case DataType.Int:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt32(model.Value));break;case DataType.Float:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToSingle(model.Value));break;case DataType.Long:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToUInt64(model.Value));break;case DataType.Double:result await client.WriteAsync(FormatAddress(model.Address), Convert.ToDouble(model.Value));break;}if (!result.IsSuccess){Logger.Info([Error] PLCMgr,TskPlcWrite,errmsg:写入失败, result.Message);}}}catch (Exception ex){ Logger.Info([Error] PLCMgr,TskPlcWrite,errmsg: ex.Message);} await Task.Delay(100);}}, TaskCreationOptions.LongRunning);}}
} 4. 主界面控件都是静态加载参数设置的控件是动态加载(点击进入动态加载变量并监听离开移除不监听) using PLCBind.Util;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;namespace PLCBind.UIForm
{public partial class ucParameter : UserControl{public ucParameter(){InitializeComponent();} /// summary/// 参数集合/// /summarypublic object ListParams{set{ RemoveParams();if (value is ListBaseParams Parameters){AddParams(Parameters);}}}/// summary/// 移除参数/// /summarypublic void RemoveParams(){foreach (Control item in this.tableLayoutPanel1.Controls){if (item is ucTextSetting ctrText){CommonMethods.RemoveControl(CommonMethods.DicSetControl, ctrText.Address);// 移除集合}}this.tableLayoutPanel1.Controls.Clear(); // 移除控件}/// summary/// 添加参数/// /summary void AddParams(ListBaseParams objParams){ var pamramCount objParams.Count;var pamrammIndex 0;for (int columnIndex 0; columnIndex tableLayoutPanel1.ColumnCount; columnIndex){for (int rowIndex 0; rowIndex tableLayoutPanel1.RowCount; rowIndex){if (pamramCount pamrammIndex){var common objParams[pamrammIndex];var address common.PLCAddress;ucTextSetting ucLbText new ucTextSetting();ucLbText.Anchor ((((AnchorStyles.Top | AnchorStyles.Bottom) | AnchorStyles.Left)));ucLbText.ParamModel common;ucLbText.Address address;CommonMethods.AddControl(CommonMethods.DicSetControl, address, ucLbText);// 添加集合tableLayoutPanel1.Controls.Add(ucLbText, columnIndex, rowIndex);// 添加控件 pamrammIndex;}}}}private void TableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e){if (e.Row % 2 1)e.Graphics.FillRectangle(Brushes.White, e.CellBounds);elsee.Graphics.FillRectangle(new SolidBrush(Color.FromArgb(192, 224, 248)), e.CellBounds);}}
} 注意事项 1. 字典类型 Data字典ConcurrentDictionarystring, bool DicBoolDataConcurrentDictionarystring, Word DicWordDataWordbyte1byte2 UI字典ConcurrentDictionarystring, ListControl DicHomeControlConcurrentDictionarystring, ListControl DicSetControl 2. bool类型只占1位数据有变化直接通知 3. word类型数据位(short:2,int/float:4,long/double:8)所以要先读取全部的数据再通知变化 4. 自定义控件继承ITransferUI类属性ParamModel方法NoticeChange赋值属性ParamModel其中PLCValue get:通过不同的数据类型获取字典中的word数据并拼接合成相应的数据类型;set:传入地址(写入时格式化地址如:40101-101)及类型