Hostlink读写寄存器报文分析

news2024/11/25 6:40:39

引言

  Hostlink是欧姆龙PLC默认的串口上行通信协议,使用默认的通信协议可免除PLC端的配置工作,易于实现分工协作。下面以CP1E-E的PLC为例进行说明,CP系列的PLC规则都是一样的。

读离散量

请求报文

字节流

40 33 31 46 41 30 30 30 30 30 30 30 30 30 30 31 30 31 30 32 30 30 34 30 30 30 30 30 30 41 30 41 2a 0d

字符流

@31FA000000000010102004000000A0A*\r

说明

@:起始字符;

31:节点号、站号、设备编号或其它类似的含义,用于识别目标PLC。相当于收件人地址。在Hostlink中,只有这两个字符是十进制的。固定长度2字符,取值范围00~31;

FA:使用FINS。对于上位机编程而言,这是一个常量;

0:超时,单位是厘秒,取值范围是0~F,一般写0,固定1位十六进制;

00000000:共有四个编号ICF、DA2、SA2、SID。对于请求报文,如果不想过多了解,这8个字符全为'0'就好;

0101:读指令,读离散量和读寄存器都是它;

02:读D系列寄存器的位

0040:读这个系列的第0个寄存器,4位十六进制字符串,这里是读D64;

00:位地址,2位16进制,虽然占两个字符,但只有右边那个有用,取值范围是00~0F,对这条指令,它代表读取的第一个离散量为D64.00;

000A:个数,这里是读取10个离散量;

0A:校验码,校验算法将在后面的章节展示;

*\r:结束符;

应答报文

字节流

40 33 31 46 41 30 30 34 30 30 30 30 30 30 30 30 31 30 31 30 30 30 30 30 31 30 31 30 31 30 31 30 31 30 31 30 31 30 31 30 31 30 31 34 31 2a 0d

字符流

@31FA0040000000010100000101010101010101010141*\r

说明

@31FA00400000000101:与上一节类似,不同之处在于超时这个位置,由1位16进制变成了2个'0',所以这一部分长度比之前的多一个字符;另外,ICF在响应时会变成40,所以中间那8个'0'就变成了"40000000";由于是PLC返回的报文,只关心上位机编程的话,就不要在意这里的ICF变成40了;

0000:这个是错误码,为0表示无错误,如果不为0,则需要查手册;

01010101010101010101:返回了10个离散量的值,对于离散量的读写,每2个字符代表一个离散量,00是断,01是通,其它数值表示这个PLC已经不正常了,要换一台新的;

41*\r:校验码和结束符;

读寄存器

请求报文

字节流

40 33 31 46 41 30 30 30 30 30 30 30 30 30 30 31 30 31 38 32 30 30 36 34 30 30 30 30 30 37 37 41 2a 0d

字符流

@31FA00000000001018200640000077A*\r

说明

红色部分见之前的章节。

82:与02相呼应,同是D系列寄存器,但是02是读它的位,82是读它的整体数值;

0064:读D100;

00:当前面用了82而不是02时,这里只能是"00";

0007:读7个寄存器;

响应报文

字节流

40 33 31 46 41 30 30 34 30 30 30 30 30 30 30 30 31 30 31 30 30 30 30 30 30 30 31 30 30 30 32 30 30 30 33 30 30 30 34 30 30 30 35 30 30 30 36 30 30 30 37 34 31 2a 0d

字符流

@31FA004000000001010000000100020003000400050006000741*\r

说明

红色部分见之前的章节。

0000:错误码;

0001000200030004000500060007:7个寄存器值,对于寄存器读写,每4个字符代表1个寄存器值,即4位16进制,至于类型是short、unsigned short还是wchar_t则是由上位机程序的缓存类型决定的;

写离散量

请求报文

字节流

40 30 30 46 41 30 30 30 30 30 30 30 30 30 30 31 30 32 30 32 30 33 45 38 30 38 30 30 30 36 30 31 30 30 30 30 30 30 30 31 30 31 30 37 2a 0d

字符流

@00FA00000000001020203E808000601000000010107*\r

说明

红色部分见之前的章节。

0102:写指令,写离散量和写寄存器是同一个;

02:D系列寄存器;

03E8:D1000;

08:D1000.08;

0006:后面有6个数据;

010000000101:离散量数据,两个字符代表一个离散量;

执行后,D1000的值变成了&12544。

响应报文

字节流

40 30 30 46 41 30 30 34 30 30 30 30 30 30 30 30 31 30 32 30 30 30 30 34 30 2a 0d

字符流

@00FA00400000000102000040*\r

说明

红色部分见之前的章节。

0000:错误码;

写寄存器

请求报文

字节流

40 30 30 46 41 30 30 30 30 30 30 30 30 30 30 31 30 32 38 32 30 33 45 38 30 30 30 30 30 32 66 66 66 66 66 66 66 66 30 32 2a 0d

字符流

@00FA00000000001028203E8000002ffffffff02*\r

说明

红色部分见之前的章节。

0102:写指令,写离散量和写寄存器是同一个;

82:D系列寄存器;

03E8:D1000;

00:不起作用,设为00即可;

0002:后面有2个数据;

ffffffff:寄存器数据,4个字符代表一个寄存器;

执行后,D1000的值变成了&65535。

响应报文

字节流

40 30 30 46 41 30 30 34 30 30 30 30 30 30 30 30 31 30 32 30 30 30 30 34 30 2a 0d

字符流

@00FA00400000000102000040*\r

说明

红色部分见之前的章节。

0000:错误码;

强制置位与取消

请求报文

字节流

40 30 30 46 41 30 30 30 30 30 30 30 30 30 32 33 30 31 30 30 30 31 30 30 30 30 33 30 30 30 36 34 30 30 37 37 2a 0d

字符流

@00FA0000000002301000100003000640077*\r

说明

红色部分见之前的章节。

2301:强制线圈状态命令,只能对CIO生效,对输入也有效果;

0000:操作代号,文档中只提及0000、0001、8000、8001、FFFF这五种状态,实测得:0000代表强制断、0001代表强制通、8000|8001|FFFF都代表取消这个点的强制状态;

30:CIO区;

0064:100,也就是第一个输出线圈的那个寄存器;

00:位地址为0,与上下文结合,表达了100.00,即第一个输出线圈;

执行后,Q:100.00强制为断;

响应报文

字节流

40 30 30 46 41 30 30 34 30 30 30 30 30 30 30 32 33 30 31 30 30 30 30 34 33 2a 0d

字符流

@00FA00400000002301000043*\r

说明

红色部分见之前的章节。

0000:错误码;

校验码

static final String fcs(CharSequence string, int length) {
	int xor = 0;//初始化校验码变量
	for (int i = 0; i < length; ++i) {
		char c = string.charAt(i);//取每一个需要校验的字符的ASCII码
		xor ^= 0xff & c;//进行8位校验
	}
	return toHex(xor, 2);//返回16进制的字符串
}

校验码函数中的length就是校验码第0个字符的位置。

@Override
public String getString() {
	try {
		StringBuilder body = new StringBuilder();//报文的缓存
		if (!m_message.getHeader().isResponse()) {//这个标志存在于ICF中,第6个Bit
			m_message.encodeRequest(body);//编码请求包
		} else {
			m_message.encodeResponse(body);//编码响应包
		}
		if (m_message.getHeader().isNet()) {//这个标志存在于ICF中,第7个Bit
			int length = body.length() - 16;
			body.replace(8, 16, toHex(length / 2, 8));//FINS/TCP中的内容,与本文无关
		} else {
			String fcs = StaticOmronPLCHostlinkUtils.fcs(body, body.length());//对之前缓存的所有字符进行校验
			body.append(fcs).append("*\r");//将校验码附加到缓存尾部,最后加上结束符
		}
		return body.toString();//完成报文的编码
	} catch (Throwable e) {
	}
	return null;
}

双重角色编码解码器结构

OmronPLCHostlink:Hostlink协议包装者,具备主、从机的双重能力;

OmronPLCHostlinkMessage:Hostlink协议的具体报文生成器的基类,需要被OmronPLCHostlink类包装;

需要3个实现类,如图,从上到下依次是强制线圈状态报文编码解码器、读数据编码解码器、写数据编码解码器;

 Hostlink的两种模式类型:

public enum OmronPLCHostlinkMode {
    CS_CJ, CV,;
}

CP系列的PLC使用的是CS_CJ,而那个CV应该是大型机,本文只考虑小型机,上文所用的报文都是CP系列的报文,对大型机可能无效;

OmronPLCHostlinkMessageHeader:公共部分的编码解码器,(即上文中开头到命令的那一段),被OmronPLCHostlinkMessage包装;

关于强制线圈状态的枚举

package pers.laserpen.util.protocol.omron;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import pers.laserpen.util.java.StaticJavaUtils;

public enum OmronPLCHostlinkForceOperation {
	OFF(0), ON(1), CANCEL_OFF(0x8000), CANCEL_ON(0x8001), CANCEL_ALL(0xffff),;

	private static Map<Integer, OmronPLCHostlinkForceOperation> s_dict;
	private final int m_code;

	private OmronPLCHostlinkForceOperation(int code) {
		m_code = code;
	}

	public final int getCode() {
		return m_code;
	}

	public static OmronPLCHostlinkForceOperation valueOf(int code) {
		if (s_dict == null) {
			synchronized (OmronPLCHostlinkCommand.class) {
				if (s_dict == null) {
					s_dict = new ConcurrentHashMap<>();
					OmronPLCHostlinkForceOperation[] values = values();
					for (OmronPLCHostlinkForceOperation v : values) {
						OmronPLCHostlinkForceOperation ok = s_dict.putIfAbsent(v.getCode(), v);
						if (ok != null) {
							StaticJavaUtils.exit("初始化错误,出现了重复定义的编码:" + ok);
						}
					}
				}
			}
		}
		return s_dict.get(code);
	}

}

寄存器前缀枚举

package pers.laserpen.util.protocol.omron;

import static pers.laserpen.util.protocol.omron.OmronPLCHostlinkMemoryType.*;

import pers.laserpen.util.data.collection.Tree;

public enum OmronPLCHostlinkMemoryArea {
	CS_CJ_CIO_BIT(0x30, Bit, 6),
	CV_CIO_BIT(0x00, Bit, 6),
	CS_CJ_WORK_BIT(0x31, Bit, 6),
	CS_CJ_HOLDING_BIT(0x32, Bit, 6),
	CS_CJ_AUXILIARY_BIT(0x33, Bit, 6),

	CS_CJ_CIO_BIT_FORCED(0x70, Bit, 6),
	CV_CIO_BIT_FORCED(0x40, Bit, 6),
	CS_CJ_WORK_BIT_FORCED(0x71, Bit, 6),
	CS_CJ_HOLDING_BIT_FORCED(0x72, Bit, 6),

	CS_CJ_CIO_WORD(0xB0, Word, 6),
	CV_CIO_WORD(0x80, Word, 6),
	CS_CJ_WORK_WORD(0xB1, Word, 6),
	CS_CJ_HOLDING_WORD(0xB2, Word, 6),
	CS_CJ_AUXILIARY_WORD(0xB3, Word, 6),

	CS_CJ_CIO_WORD_FORCED(0xF0, Word, 6),
	CV_CIO_WORD_FORCED(0xC0, Word, 6),
	CS_CJ_WORK_WORD_FORCED(0xF1, Word, 6),
	CS_CJ_HOLDING_WORD_FORCED(0xF2, Word, 6),

	CS_CJ_DM_BIT(0x02, Bit, 6),
	CS_CJ_DM_WORD(0x82, Word, 6),
	CV_DM_WORD(0x82, Word, 6),

	CS_CJ_EM0_BIT(0x20, Bit, 6),
	CS_CJ_EM0_WORD(0xA0, Word, 6),
	CV_EM0_WORD(0x90, Word, 6),
	CS_CJ_EM_CURRENT_WORD(0x98, Word, 6),
	CV_EM_CURRENT_WORD(0x98, Word, 6),
	CS_CJ_EM_CURRENT_NO(0xBC, Undefined, 6),
	CV_EM_CURRENT_NO(0x9C, Undefined, 6),

	;

	private static Tree<Object, OmronPLCHostlinkMemoryArea> m_dict;
	private final int m_area;
	private final OmronPLCHostlinkMemoryType m_type;
	private final int m_addressBytes;

	private OmronPLCHostlinkMemoryArea(int cmd, OmronPLCHostlinkMemoryType type, int addressBytes) {
		m_area = cmd;
		m_type = type;
		m_addressBytes = addressBytes;
	}

	public final int getCode() {
		return m_area;
	}

	public final OmronPLCHostlinkMemoryType getType() {
		return m_type;
	}

	public static OmronPLCHostlinkMemoryArea valueOf(OmronPLCHostlinkMode mode, int code) {
		if (m_dict == null) {
			synchronized (OmronPLCHostlinkMemoryArea.class) {
				if (m_dict == null) {
					m_dict = new Tree<>();
					OmronPLCHostlinkMemoryArea[] values = values();
					for (OmronPLCHostlinkMemoryArea v : values) {
						if (v.name().startsWith("CS_CJ_")) {
							m_dict.path(true, OmronPLCHostlinkMode.CS_CJ, v.getCode()).set(v);
						} else if (v.name().startsWith("CV_")) {
							m_dict.path(true, OmronPLCHostlinkMode.CV, v.getCode()).set(v);
						}
					}
				}
			}
		}
		Tree<Object, OmronPLCHostlinkMemoryArea> node = m_dict.path(false, mode, code);
		return node == null ? null : node.get();
	}

	public int getAddressBytes() {
		return m_addressBytes;
	}

}

  之前被一些博文骗了,其实所有的读写地址都是6个字符的,就算后面两个字符无效也必须写上。

  代码仅供参考,它是配合通信程序使用的。将编码解码器与通信接口分开设计可以消除通信方面的重复代码,并且可以对编码解码器进行单独调试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/47091.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[Jetson]在nvidia orin/xavier上快速配置深度学习环境(Tensorflow,Pytorch都可以参考)

本文章将介绍如何通过docker在边缘计算设备nvidia orin/xavier上快速配置深度学习环境.该方法适用于Tensorflow,Pytorch,但是本文以介绍Tensorflow的安装为主. 文章目录第一步:安装docker第二步:安装nvidia-docker2第三步:拉取tensorflow镜像3.1 确定容器版本3.2 拉取镜像3.3 测…

awk命令应用

记录&#xff1a;353 场景&#xff1a;在CentOS 7.9操作系统上&#xff0c;使用awk文本处理工具处理文本&#xff1b;使用awk、cat和grep搭配使用处理文本&#xff1b;使用awk直接处理文本&#xff1b;使用shell脚本调用awk脚本处理文本。 版本&#xff1a; 操作系统&#x…

基于神经网络彩色图像插值研究-附Matlab程序

⭕⭕ 目 录 ⭕⭕✳️ 一、引言✳️ 二、色彩过滤阵列CFA✳️ 三、BP网络结构✳️ 四、神经网络彩色图像插值实验验证✳️ 五、参考文献✳️ 六、Matlab程序获取与验证✳️ 一、引言 彩色图像插值是通过估算相邻像素来估计缺失的颜色分量的过程&#xff0c;数字相机通过色彩过滤…

若依对SpringSecurity框架的运用

引言&#xff1a;借助ruoyi-vue框架学习其对SpringSecurity框架的运用。若依的前后端分离版本基于SpringSecurity和JWT配合Redis来做用户状态记录. 1 SpringSecurity 1.1 入口 后台接收登录数据&#xff0c;基于用户名和密码封装一个(UsernamePasswordAuthenticationToken)认…

线程安全和synchronized关键字

一&#xff0c;线程安全的引入 1.示例 多线程在多进程的基础上更好解决了并发问题&#xff0c;但由于一个进程内的多个线程是资源共享的&#xff0c;就会出现多个线程在并发执行的时候造成内存中数据的混乱。 举一个例子&#xff1a; class Counter {public int count;publi…

hypervision理解的记录

目录 一、hypervision介绍 Type 1 Hypervisor Type 2 Hypervisor 二、QNX hypervision是TYPE1的虚拟机 三、QNX hypervision架构 1、VMM (虚拟机管理器) 2、virtual-net 3、qnx官网 network 九、其他 一、hypervision介绍 首先&#xff0c;hypervision分为Type1和Type2…

SpringBoot添加外部jar包及打包(亲测有效) - 第452篇

历史文章&#xff08;文章累计450&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 深入Feign源码吃透Spring扩展点「扩展点…

亿级异构任务调度框架设计与实践

背景 阿里云日志服务作为云原生可观测与分析平台。提供了一站式的数据采集、加工、查询分析、可视化、告警、消费与投递等功能。全面提升用户的研发、运维、运营、安全场景的数字化能力。 日志服务平台作为可观测性平台提供了数据导入、数据加工、聚集加工、告警、智能巡检、…

“200万天价床垫”引发的思考:普通床垫越卖越贵是推测还是事实

定制床垫价格高达200万元&#xff1f;近段时间&#xff0c;一场娱乐圈的闹剧让大家把目光转向了床垫市场。在天价床垫的话题下&#xff0c;除了大部分猜测床垫品牌的讨论以外&#xff0c;也有不少人认为指出了“社会现状”&#xff1a;健康品质化的消费追求正在让市面上的床垫价…

Python_数据容器_字典

一、字典&#xff08;映射&#xff09;的定义 生活中的字典&#xff1a; 【字】&#xff1a;【含义】 可以按【字】找出对应的【含义】 Python中的字典&#xff1a; key : value 可以按照[key]找出对应的[value] 1、Python字典使用场景&#xff1a; 通过使用字典&#…

uni-app 之 web-view 与h5 通讯

官网文档&#xff1a;https://uniapp.dcloud.net.cn/component/web-view.html#getenv web-view 是一个 web 浏览器组件&#xff0c;可以用来承载网页的容器&#xff0c;会自动铺满整个页面&#xff08;nvue 使用需要手动指定宽高&#xff09;。 各小程序平台&#xff0c;web-v…

图像下采样再上采样维度不匹配

图像在下采样后再上采样&#xff0c;维度会发生不匹配&#xff0c;假设一幅图像的维度为(b,c,h,w)&#xff0c;那么当h和w是偶数的时候&#xff0c;下采样和上采样是匹配的&#xff0c;当且仅当他是偶数的时候才匹配&#xff0c;然而图像的h和w往往不一定是偶数。当然有许多种方…

【Shell 脚本速成】06、Shell 数组详解

目录 一、数组介绍 二、数组定义 三、数组赋值方式 四、数组取值 案例演示 五、关联数组 5.1 定义管理数组 5.2 关联数组赋值 5.3 管理数组取值 5.4 综合案例 有这样一个现实问题&#xff1a;一个班级学员信息系统&#xff0c;要求存储学员ID、NAME、SCORE、AGE、GE…

关于订单功能的处理和分析

这两天看了一下RABC的权限管理处理&#xff0c;梳理了一下订单功能的表创建&#xff0c;界面&#xff0c;功能分析。 目录 RABC RBAC0模型 那么对于RABC模型我们怎么创建数据库表&#xff1f; 订单模块的梳理 RABC RABC说的是在用户和权限之间多一个角色&#xff0c;用户与…

软件测试基础

⭐️前言⭐️ &#x1f349;博客主页&#xff1a; &#x1f341;【如风暖阳】&#x1f341; &#x1f349;精品Java专栏【JavaSE】、【备战蓝桥】、【JavaEE初阶】、【MySQL】、【数据结构】 &#x1f349;欢迎点赞 &#x1f44d; 收藏 ⭐留言评论 &#x1f4dd;私信必回哟&…

Spring Cloud OpenFeign - - - >拦截器

源码地址&#xff1a;https://download.csdn.net/download/weixin_42950079/87209379 SpringMVC拦截器 和 OpenFeign拦截器 的区别 初学者很容易将 Spring MVC 拦截器 和 Spring Cloud OpenFeign 拦截器搞混&#xff0c;误以为OpenFeign拦截器就是SpringMVC拦截器&#xff1a; …

虹科分享 | 麦氏比浊仪在药敏试验中的应用

细菌是重要的病原微生物&#xff0c;人类针对不同的病原菌研发了各类抗菌药&#xff0c;这些药物对细菌性疾病的治疗与控制起到了关键作用。然而随着新型致病菌的不断出现&#xff0c;加上细菌在药物使用过程中逐渐产生了耐药性&#xff0c;抗菌药的防治效果越来越差。病原菌对…

Python 中的类与继承

类的定义以及实例的建立 Python中&#xff0c;类通过 class 关键字定义。 例如最简单的一个类定义可以为&#xff1a; class Person(object):pass Python 的编程习惯&#xff0c;类名以大写字母开头&#xff0c;紧接着是(object)&#xff0c;表示该类是从哪个类继承下来的。…

解决单文件组件里的跨域请求数据问题(使用vue单文件组件请求数据必会遇到的问题!!!)

为什么要解决跨域问题&#xff1a; 因为浏览器有限制&#xff0c;只有同域名同端口号下的数据才能拿来用&#xff1b;那如果想拿到不同域名不同端口号下的数据就不行了&#xff1b; 在单文件组件中如何去解决跨域问题&#xff1a; 因为服务器没有跨域限制&#xff0c;只有浏览…

PyTorch(四)Torchvision 与 Transforms

文章目录Log一、Torchvision1. CIFAR10① 介绍② 使用2. 与 Transforms 结合使用总结Log 2022.11.28接着开启新的一章2022.11.29继续学习 一、Torchvision 视频教程中 Torchvision v0.9.0文档Torchvision 官方文档Torchvision Datasets API 文档 1. CIFAR10 ① 介绍 CIFAR…