Open chzhm159 opened 4 years ago
首先提供一个实现品参考 NuGet 搜索 "Infrastructure.Plc.Keyence" 其次说一下我目前的实现思路 我是基于 DotNetty ,这样我就只专注于 协议的 解析上. 设计思路 变量为核心对象,从哪里采集,采集到的如何处理,处理完之后的结果输送到哪里去 从而达到将协议细节封装,而只关注业务逻辑上. 已经支持了 OMRON Fins/TCP 协议 现在打算支持 基恩士的上位链路 其次 设计一个 Tag 类. 主要包含 三部分 信息
顺带贴上我目前正在测试的代码,如果需要我可以将全部源码发给你.
public enum TagOperator { /// <summary> /// 只读 /// </summary> R, /// <summary> /// 只写 /// </summary> W, /// <summary> /// 读+写 /// </summary> RW } public enum TagOperatorUnit { /// <summary> /// 位 ,一位只有两种状态: 0 , 1 /// </summary> BIT, /// <summary> /// 字,1字=2字节. 1 word=2 byte /// </summary> WORD } public enum ValueType { /// <summary> /// 8 位 无符号整数,取值范围 16# 00~FF /// </summary> IByte, /// <summary> /// 16 位,整型, 取值范围: -32768~32767 /// </summary> IInt16, /// <summary> /// 16位,无符号整数,取值范围 0~65535 /// </summary> UInt16, /// <summary> /// 32位,无符号整数, /// </summary> ULong32, /// <summary> /// 32位,有符号整数, /// </summary> ILong32, /// <summary> /// 64 位,有符号整数类型 /// </summary> ILong64, /// <summary> /// 自定义的处理 /// </summary> ICustome } class Tag : EventArgs { /// <summary> /// tag的业务友好的名称 /// </summary> public String TagName { get; set; } /// <summary> /// 全局唯一性的变量名称.其组成结果默认为 [DeviceName]:[TagName] /// 目前是已此名称作为 redis 中的 key. /// </summary> public String UniqueName { get; set; } /// <summary> /// 寄存器类型,例如Omron的 DM 区 /// </summary> public String registerType { get; set; } /// <summary> /// 寄存器地址编号 /// </summary> public int begin { get; set; } /// <summary> /// 自起始位置往后的偏移量 /// </summary> public int offset { get; set; } /// <summary> /// 读取单位 /// </summary> public TagOperatorUnit unit { get; set; } /// <summary> /// 一次读取多少个单位 /// </summary> public int count { get; set; } /// <summary> /// tag 的读写控制,r=read-only,w=write-only,rw=read-write /// </summary> public TagOperator opt { get; set; } /// <summary> /// 读取变量的处理函数委托声明, /// </summary> /// <param name="tag"></param> /// <param name="buf"></param> /// <returns></returns> public delegate object valueProcess(Tag tag, IByteBuffer buf); /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public event EventHandler<Tag> OnValueReadyEvent; private bool _loopRead = true; /// <summary> /// 是否为循环读取模式,默认为 true /// </summary> public bool loopRead { get { return this._loopRead; } set { this._loopRead = value; } } private ValueType _valueType ; public void setValueType(ValueType vt) { // TODO 这里可以根据最终数值类型,自动计算 读取长度. this._valueType = vt; } private ValueType getValueType() { return this._valueType; } /* * 暂时不支持变量级别的采集周期设置 private int _rInterval = 1000; /// <summary> /// 周期性读取间隔,单位为毫秒 /// </summary> public void setReadInterval (int interval){ this._rInterval = interval; } /// <summary> /// 获取读取周期,单位为毫秒 /// </summary> /// <returns></returns> public int getReadInterval() { return this._rInterval ; } */ /// <summary> /// 保留原始数据,便于更灵活的处理,但是需要注意手动释放调用 .Release(); /// </summary> private IByteBuffer ValueBuf { get; set; } /// <summary> /// 默认的返回值解析. 根据最初设定的 ValueType 自动做数值转换 /// </summary> /// <param name="buf"></param> public virtual void setValueBuffer(IByteBuffer buf) { // @Warning,后续会替换掉这个buf的缓存,根据这个buffer的byte数值和ValueType计算得到最终数值后,释放这个缓存 this.ValueBuf = buf.Duplicate(); EventHandler<Tag> raiseEvent = OnValueReadyEvent; // Event will be null if there are no subscribers if (raiseEvent != null) { Delegate[] delegAry = raiseEvent.GetInvocationList(); foreach (EventHandler<Tag> deleg in delegAry) { deleg.BeginInvoke(this,this,null,null); } } } /// <summary> /// 默认的变量值处理函数. 根据ValueType来自动处理,目前支持的有 IInt16, /// </summary> /// <param name="tag"></param> /// <param name="buf"></param> /// <returns></returns> public virtual object defaultValueProcess(Tag tag,IByteBuffer buf) { object v; switch (tag.getValueType()) { case ValueType.IInt16: // Gets a 16-bit short integer at the specified absolute index in this buffer. v = buf.GetShort(0); break; case ValueType.ULong32: //Gets an unsigned 32-bit integer at the specified absolute index in this buffer. v = buf.GetUnsignedInt(0) ; break; case ValueType.UInt16: // Gets an unsigned 16-bit short integer at the specified absolute index in this buffer. v = buf.GetUnsignedShort(0); break; case ValueType.ICustome: //这里支持自定义处理,将byte转换为字符串形式,以便自行拆分 v = ByteBufferUtil.HexDump(buf); break; default: v = ByteBufferUtil.HexDump(buf); break; } return v; } public object getValue(valueProcess proc) { return proc(this,this.ValueBuf); } /// <summary> /// 获取该变量的数值.为默认处理机制. /// </summary> public object value { get { // TODO,这里可以做性能优化,否则每次获取数值的时候,都需要重新计算一遍 // 而且还有个问题就是,读取到的是旧值....因为 IByteBuffer 是不断被更新的 valueProcess proc = defaultValueProcess; return this.getValue(proc); } } }
你好,看到你的这个设计,觉得很好,不知道现在进行到什么程序了,在基恩士的上位链路上,他的协议封装和楼主的设计类似,也是将数据分为不同的操作类型,数据大小,通过特定的指令去读取数据,如果设计一个支持的tag类,我觉得可以参考楼主的这种设计思想,不好意思,过了这么长时间才看到。
我的架构设计 做了几个项目,也参考了不少产品,例如 HslCommunication ,NI opc server,亚控ioserver,等还是决定自己打造一款产品. 希望有兴趣一起
首先提供一个实现品参考 NuGet 搜索 "Infrastructure.Plc.Keyence" 其次说一下我目前的实现思路 我是基于 DotNetty ,这样我就只专注于 协议的 解析上.
设计思路 变量为核心对象,从哪里采集,采集到的如何处理,处理完之后的结果输送到哪里去 从而达到将协议细节封装,而只关注业务逻辑上. 已经支持了 OMRON Fins/TCP 协议 现在打算支持 基恩士的上位链路 其次 设计一个 Tag 类. 主要包含 三部分 信息
从哪里读取
读取到如何处理
处理完之后存到哪里
顺带贴上我目前正在测试的代码,如果需要我可以将全部源码发给你.