解析西门子PLC的String和WString

西门子PLC有两种字符串类型,String与WString

String 用于存放英文数字标点符号等ASCII字符,每个字符占用一个字节

WString宽字符串用于存放中文、英文、数字等Unicode字符,每个字符占用两个字节

之前我搞过一篇解析String的

关于使用TCP-S7协议读写西门子PLC字符串的问题_s7 写入string-CSDN博客

对西门子PLC字符串的读写逻辑如下:

String[50]为例:


西门子PLC字符串类型 String[50],占用52个字节(偏移量),第一个字节是最大长度,就是50,第二个字节是实际长度24,第三个字节之后就是ASCII码,

PLC解析逻辑为找到第二个字节的长度length.,然后查找length个字符,就结束

WString[50]为例:

西门子PLC字符串类型 WString[50],占用104个字节(偏移量),第一第二个字节是最大长度,就是50,第三第四个字节是实际长度10,第五个字节之后就是Unicode码,

PLC解析逻辑为找到第三第四个字节的长度actualLength,然后查找actualLength * 2个字节,就结束。然后将字节数组转换为Unicode字符串即可 ,使用方法Encoding.Unicode.GetString(buffer, 4, actualLength * 2),因C#是低字节在前的,因此需要将相邻两个元素交换 buffer[0]与buffer[1]交换,buffer[2]与buffer[3]交换

相关解析函数如下【编程语言:C#】

​/// <summary>/// 写入西门子PLC的宽字符串,写入的字节长度为(length+2)*2,其中第一第二字节代表最大长度【PLC设定的】,第三第四字节代表实际长度/// 从第五个字节开始Unicode编码/// </summary>/// <param name="dbNumber"></param>/// <param name="offsetAddress"></param>/// <param name="maxLength"></param>/// <param name="writeString"></param>/// <returns></returns>static bool DB_WriteWString(short dbNumber, ushort offsetAddress, int maxLength, string writeString){byte[] byteArray = new byte[(maxLength + 2) * 2];byte[] stringByteArray = System.Text.Encoding.Unicode.GetBytes(writeString);//因C#是低字节在前的,因此需要交换相邻两个元素的位置。for (int i = 0; i < stringByteArray.Length / 2; i++){byte tempElement = stringByteArray[2 * i];stringByteArray[2 * i] = stringByteArray[2 * i + 1];stringByteArray[2 * i + 1] = tempElement;}Array.Copy(BitConverter.GetBytes((ushort)maxLength).Reverse().ToArray(), 0, byteArray, 0, 2);//最大长度Array.Copy(BitConverter.GetBytes((ushort)writeString.Length).Reverse().ToArray(), 0, byteArray, 2, 2);//实际长度Array.Copy(stringByteArray, 0, byteArray, 4, stringByteArray.Length);//第三个字节开始写入字符串的ASCII码return WriteSerialByteArray(dbNumber, offsetAddress, byteArray);}static bool WriteSerialByteArray(short dbNumber, ushort offsetAddress, byte[] byteArray){Console.WriteLine($"这里写入DB{dbNumber}.{offsetAddress}的连续字节数组【{string.Join(",", byteArray.Select(x => x.ToString("X2")))}】");return true;}static bool ReadSerialByteArray(short dbNumber, ushort offsetAddress, out byte[] byteArray){byteArray = new byte[] { 12, 6, 48, 49, 97, 98, 65, 66, 0, 0, 0, 0 };//假设读取12个字节Console.WriteLine($"这里读取DB{dbNumber}.{offsetAddress}的连续字节数组【{string.Join(",", byteArray.Select(x => x.ToString("X2")))}】");return true;}/// <summary>/// 读取宽字符串Unicode:从第五个字节开始,读取实际长度个字符  actualLength =  byteArray[2] byteArray[3]/// </summary>/// <param name="dbNumber"></param>/// <param name="offsetAddress"></param>/// <param name="readString"></param>/// <returns></returns>static bool DB_ReadWString(short dbNumber, ushort offsetAddress, out string readString){ReadSerialByteArray(dbNumber, offsetAddress, out byte[] byteArray);if (byteArray.Length < 4){throw new Exception($"读取宽字符串时,原字节数组长度不能低于4");}int maxLength = BitConverter.ToUInt16(new byte[] { byteArray[1], byteArray[0] }, 0);//最大长度,限制长度int actualLength = BitConverter.ToUInt16(new byte[] { byteArray[3], byteArray[2] }, 0);//实际长度if (byteArray.Length / 2 - 2 < actualLength){throw new Exception($"读取到的字节长度 小于 实际长度,非法的读取,PLC设定的字符串最大长度为【{maxLength}】");}//Unicode需要相邻两个字节交换位置,C#是低字节在前的for (int exchangeIndex = 0; exchangeIndex < actualLength; exchangeIndex++){byte tempExchange = byteArray[4 + exchangeIndex * 2];byteArray[4 + exchangeIndex * 2] = byteArray[4 + exchangeIndex * 2 + 1];byteArray[4 + exchangeIndex * 2 + 1] = tempExchange;}//从第五个元素开始,读取【actualLength * 2】个字节,一个Unicode字符对应一个ushort数字,占用两个字节readString = System.Text.Encoding.Unicode.GetString(byteArray, 4, actualLength * 2);return true;}​

假设PLC定义一个WString[50],我们读取到的字节数组为:

byte[] byteArray = new byte[] { 0, 50, 0, 16, 83, 228, 82, 81, 89, 71, 140, 45, 0, 65, 0, 66, 0, 67, 0, 49, 0, 50, 0, 51, 0, 52, 0, 53, 0, 54, 0, 97, 0, 98, 0, 99, 0, 0, 0, 0};

解析为:古剑奇谭ABC123456abc

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kotlin 的优势:现代编程语言的卓越选择

文章目录 简洁与优雅的语法空安全特性函数式编程&#xff0c;支持高阶函数、lambdaKotlin 内联函数与 Java 的互操作性强大的类型推断协程支持lazy 委托object 单例模式区间表达式现代的开发工具支持 本文首发地址 https://h89.cn/archives/301.html 最新更新地址 https://gite…

包装类和泛型

&#x1f389;欢迎大家收看&#xff0c;请多多支持&#x1f339; &#x1f970;关注小哇&#xff0c;和我一起成长&#x1f680;个人主页&#x1f680; 包装类&#x1f319; Java中每个基本数据类型都对应了一个包装类&#xff0c; 除了int的包装类是Integer&#xff0c;char…

微信小程序开发 快速学习 这篇就够了

目录 一、配置篇 &#xff08;1&#xff09;官网链接&#xff1a; &#xff08;2&#xff09;项目分析 &#xff08;3&#xff09;调试器 &#xff08;4&#xff09;预览体验 &#xff08;5&#xff09;配置文件 &#xff08;6&#xff09;配置pages &#xff08;7&…

【开发问题记录】启动某个微服务时无法连接到seata(seata启动或配置异常)

问题记录 一、问题描述1.1 问题复现1.1.1 将Linux中的部分微服务启动1.1.2 在本地启动当时出错的服务 1.2 解决思路1.2.1 Nacos中seata相关的信息1.2.2 Linux中seata相关的信息 二、问题解决2.1 seata的配置错误2.1.1 Nacos中seata的配置问题2.1.2 命名空间问题的发现 2.2 网络…

Matlab编程资源库(10)离散傅立叶变换

一、离散傅立叶变换算法简要 给定一个N点的离散信号序列x(n)&#xff0c;其中n表示时刻&#xff0c;n 0, 1, 2, ..., N-1。 定义离散傅立叶变换的频域序列X(k)&#xff0c;其中k表示频率&#xff0c;k 0, 1, 2, ..., N-1。 通过以下公式计算每个频率对应的复数值&#xff…

生鲜云订单零售系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;订单评价管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;商品信息&#…

[Unity] ShaderGraph实现镜头加速线/残血效果 URP

效果如下所示&#xff1a;残血状态时&#xff0c;画面会压暗角&#xff0c;并出现速度线营造紧迫感。 使用到的素材如下&#xff0c;换别的当然也可以。[这是张白色的png放射图&#xff0c;并非皇帝的新图hhh] 这个效果的实现逻辑&#xff0c;其实就是利用time向圆心做透明度的…

2024经济师考试报名『注册流程』图解!

⏰报名时间&#xff1a;8月12日—9月11日 ☑️报名注册流程 1、经济师考试报名注册网站&#xff1a;中国人事考试网. 2、点击考生登录栏目中的【新用户注册】按钮&#xff0c;进行注册。 3、进入用户注册界面&#xff0c;填写注册信息。 4、填写完毕确认无误后点击【提交】&…

Unity UGUI 之 Mask

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 本文在发布时间选用unity 2022.3.8稳定版本&#xff0c;请注意分别 1.什么是遮罩 遮罩是一…

深度解读大语言模型中的Transformer架构

一、Transformer的诞生背景 传统的循环神经网络&#xff08;RNN&#xff09;和长短期记忆网络&#xff08;LSTM&#xff09;在处理自然语言时存在诸多局限性。RNN 由于其递归的结构&#xff0c;在处理长序列时容易出现梯度消失和梯度爆炸的问题。这导致模型难以捕捉长距离的依…

学习react-登录状态验证

1.创建三个页面LoginPage, HomePage,NotFoundPage用于Router 创建LoginPage.tsx用于做登录页面 // LoginPage.tsx const LoginPage (props:LoginProp) > {const navigate useNavigate();return( <h1 onClick{ ()>{navigate("/");}}>Hello Login, {pr…

02 Go语言操作MySQL基础教程_20240729 课程笔记

概述 如果您没有Golang的基础&#xff0c;应该学习如下前置课程。 Golang零基础入门Golang面向对象编程Go Web 基础Go语言开发REST API接口_20240728 基础不好的同学每节课的代码最好配合视频进行阅读和学习&#xff0c;如果基础比较扎实&#xff0c;则阅读本教程巩固一下相…

微信小游戏之 三消(一)

首先设定一下 单个 方块 cell 类&#xff1a; 类定义和属性 init 方法 用于初始化方块&#xff0c;接收游戏实例、数据、宽度、道具类型和位置。 onWarning 方法 设置警告精灵的帧&#xff0c;并播放闪烁动作&#xff0c;用于显示方块的警告状态。 grow 方法 根据传入的方向…

21.发布确认模式-高级

问题 生产环境中由于一些不明原因&#xff0c;导致rabbitmq重启&#xff0c;在重启的期间生产者消息投递失败&#xff0c;导致消息丢失&#xff0c;需要手动处理恢复。那么如何才能进行rabbitmq的消息可靠性投递&#xff1f;特别是在极端的情况&#xff0c;rabbitmq集群不可用…

文件操作相关的精讲

目录&#xff1a; 思维导图 一. 文件定义 二. 文件的打开和关闭 三. 文件的顺序读写操作 四. 文件的随机读写操作 五. 文本文件和二进制文件 六. 文件读取结束的判断 七.文件缓冲区 思维导图&#xff1a; 一. 文件定义 1.文件定义 C语言中&#xff0c;文件是指一组相…

Vue3可媲美Element Plus Tree组件实战之移除节点

Element Plus Tree自定义节点内容示例中介绍了移除节点的用法&#xff0c;个人觉得作为提供给用户API&#xff0c;应该遵循迪米特法则&#xff0c;把功能实现的细节封装在组件内部&#xff0c;而提供给用户最简单的操作方式&#xff0c;同时在此基础上支持用户的扩展。 因此&a…

接口测试支持IDEA插件一键同步API、新增思维导图快速评审测试用例,MeterSphere开源持续测试工具v3.1.0版本发布

2024年7月29日&#xff0c;MeterSphere开源持续测试工具正式发布v3.1.0版本。 在这一版本中&#xff0c;接口测试方面&#xff0c;支持通过IDEA插件一键同步API至MeterSphere&#xff1b;测试管理方面&#xff0c;“测试用例”模块新增通过思维导图模式快捷评审测试用例。在“…

挑战房市预测领头羊:KNN vs. 决策树 vs. 线性回归

挑战房市预测领头羊&#xff08;KNN&#xff0c;决策树&#xff0c;线性回归&#xff09; 1. 介绍1.1 K最近邻&#xff08;KNN&#xff09;&#xff1a;与邻居的友谊1.1.1 KNN的基础1.1.2 KNN的运作机制1.1.3 KNN的优缺点 1.2 决策树&#xff1a;解码房价的逻辑树1.2.1 决策树的…

CSS实现文本溢出处理

1.单行文本溢出 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-wid…

总结——TI_音频信号分析仪

一、简介 设备&#xff1a;MSPM0G3507 库&#xff1a;CMSIS-DSP TI 数据分析&#xff1a;FFT 软件&#xff1a;CCS CLion MATLAB 目的&#xff1a;对音频信号进行采样&#xff08;滤波偏置处理&#xff09;&#xff0c;通过FFT获取信号的频率成分&am…