linux 下 C++ 与三菱PLC 通过MC Qna3E 二进制 协议进行交互

西门子plc 有snap7库 进行交互,并且支持c++ 而且跨平台。但是三菱系列PLC并没有现成的开源项目,没办法只能自己拼接,我这里实现了MC 协议 Qna3E 帧,并使用二进制进行交互。

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>#include <mutex>
#include <string>
using namespace std;namespace MelsecMC
{class PlcSocket{private:bool is_open;int global_socket_fd; // 用于发送/接受数据mutex m;public:PlcSocket();~PlcSocket();// 初始化socketbool initSocket(string ip, int port, int milSecond);// 关闭socketbool closeSocket();// 发送数据bool write(unsigned char *buffer, int len);// 接收数据bool read(unsigned char *buffer, int len);};
}
#include "socket.h"
#include <chrono>
#include <thread>namespace MelsecMC
{PlcSocket::PlcSocket(){global_socket_fd = -1;}PlcSocket::~PlcSocket(){}bool PlcSocket::initSocket(string ip, int port, int milSecond){// createint socket_fd = socket(AF_INET, SOCK_STREAM, 0);if (socket_fd == -1){cout << "socket 创建失败:" << endl;return false;}struct sockaddr_in addr;addr.sin_family = PF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr(ip.c_str());// connectint res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));if (res == -1){cout << "connect 链接失败:" << endl;return false;}cout << "connect 链接成功:" << endl;// 设置timeoutsetsockopt(socket_fd, SOL_SOCKET, SO_SNDTIMEO, (char *)20, sizeof(int));setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)20, sizeof(int));cout << "setsockopt 成功:" << endl;global_socket_fd = socket_fd;return true;}bool PlcSocket::write(unsigned char *buffer, int len){m.lock();long result = send(global_socket_fd, buffer, len, 0);m.unlock();if (result < 0){return false;}return true;}bool PlcSocket::read(unsigned char *buffer, int len){m.lock();long result = recv(global_socket_fd, buffer, len, 0);m.unlock();if (result < 0){cout << "recv失败:" << endl;return false;}return true;}bool PlcSocket::closeSocket(){close(global_socket_fd);return true;}
}
/***
MC协议的通讯方式有很多种:4C、3C、2C、1C、4E、3E、1E帧格式 数据格式分为二进制格式和ASCII码格式
本代码采用 3E + 二进制格式
https://www.jianshu.com/p/ca7f1609c8c1
***/
#pragma once
#include <sys/sem.h>
#include <sys/shm.h>
#include <iostream>
#include <memory>
#include "socket.h"namespace MelsecMC
{class MelsecMcClient : public std::enable_shared_from_this<MelsecMcClient>{public:using Ptr = std::shared_ptr<MelsecMcClient>;explicit MelsecMcClient();~MelsecMcClient();bool connectTo(const string & ip, int port);bool disconnect();bool readInt32(std::string area,int start,int &value);bool writeInt32(std::string area,int start,int value);bool readShort(std::string area,int start,short &value);bool writeShort(std::string area,int start,short value);private:PlcSocket socket;unsigned char head[7] = {0x50,0x00,0x00,0xFF,0xFF,0x03,0x00};private:unsigned char decodeArea(std::string area);};
}
#include "client.h"
#include <sstream>
#include <iomanip>
namespace MelsecMC
{MelsecMcClient::MelsecMcClient(){}MelsecMcClient::~MelsecMcClient(){disconnect();}bool MelsecMcClient::connectTo(const string &ip, int port){return socket.initSocket(ip, port, 2000);}bool MelsecMcClient::disconnect(){return socket.closeSocket();}bool MelsecMcClient::writeInt32(std::string area,int start,int value){unsigned char cmd[25] = {0};// 头memcpy(cmd, head, 7);//50 00 00 FF FF 03 00   10 00   0A 00   01 14  00 00    64 00 00   A8  02 00  01 00 00 00 写1//请求数据物理长度cmd[7] = 0x10;cmd[8] = 0x00;// CPU监视定时器 表示等待PLC响应的timeout时间cmd[9] = 0x0A;cmd[10] = 0x00;//写命令 跟读的差别是:读是0104,写是0114 ;就是04和14的差别cmd[11] = 0x01;cmd[12] = 0x14;//(子命令) : 值是0表示按字读写入1个字=16位),如果值是1就按位写入cmd[13] = 0x00;cmd[14] = 0x00;//(首地址):地址因为跨度比较大,所以用了3个字节;值640000  返过来是000064,十进制就是100cmd[17] = start / 255 / 255 % 255;cmd[16] = start / 255 % 255;cmd[15] = start % 255;//(软元件 读取的区域) : 表示读取PLC寄存器的类型: 这里的A8表示D点;其他常见的有: 90-M点;9C-X点;9D-Y点;B0-ZR外部存储卡unsigned char areaHex = decodeArea(area);if (areaHex == 0x00){std::cout << "不存在的地址 " << area << std::endl;return false;}cmd[18] = areaHex;//写入长度 00 02  =10进制2个字 =32位 = 4个字节 =1个intcmd[19] = 0x02;cmd[20] = 0x00;//写入int值cmd[24] = (value >> 24) & 0xFF;cmd[23] = (value >> 16) & 0xFF;  cmd[22] = (value >> 8) & 0xFF;cmd[21] = value & 0xFF;if (!socket.write(cmd, sizeof(cmd))){return false;}// 读取数据unsigned char recv[512] = {0};if (!socket.read(recv, sizeof(recv))){return false;}if (recv[0] != 0xD0 && recv[1] != 0x00){std::cout << "数据格式不正确" << std::endl;return false;}return true;}bool MelsecMcClient::readInt32(std::string area, int start, int &value){unsigned char cmd[21] = {0};// 头memcpy(cmd, head, 7);//请求数据长度 也要反过来,值是000C,也就是12;表示后面的报文内容的长度是12cmd[7] = 0x0C;cmd[8] = 0x00;// CPU监视定时器 表示等待PLC响应的timeout时间cmd[9] = 0x0A;cmd[10] = 0x00;// 批量读命令 值是0401(所有值都要反过来看);表示批量读取;如果是1401就是随机写取;cmd[11] = 0x01;cmd[12] = 0x04;//(子命令) : 值是0表示按字读取(1个字=16位),如果值是1就按位读取cmd[13] = 0x00;cmd[14] = 0x00;//(首地址):地址因为跨度比较大,所以用了3个字节;值640000  返过来是000064,十进制就是100cmd[17] = start / 255 / 255 % 255;cmd[16] = start / 255 % 255;cmd[15] = start % 255;//(软元件 读取的区域) : 表示读取PLC寄存器的类型: 这里的A8表示D点;其他常见的有: 90-M点;9C-X点;9D-Y点;B0-ZR外部存储卡unsigned char areaHex = decodeArea(area);if (areaHex == 0x00){std::cout << "不存在的地址 " << area << std::endl;return false;}cmd[18] = areaHex;// 读取长度 00 02  =10进制2个字 =32位 = 4个字节 =1个intcmd[19] = 0x02;cmd[20] = 0x00;// 发送数据if (!socket.write(cmd, sizeof(cmd))){return false;}// 读取数据unsigned char recv[512] = {0};if (!socket.read(recv, sizeof(recv))){return false;}// 解析数据// D0 00 00 FF FF 03 00 06 00 00 00 BB 02 96 49// D0 00 (响应) :表示反馈信息,固定D0 00// 00 (网络编号 ): 与上同// FF (PLC编号) : 与上同// FF 03 (请求目标模块IO编号) : 与上同// 00 (请求目标模块站编号): 与上同// 06 00  应答数据物理长度if (recv[0] != 0xD0 && recv[7] != 0x06){std::cout << "数据格式不正确" << std::endl;return false;}value = recv[14] << 24 | recv[13] << 16 | recv[12] << 8 | recv[11];std::cout << "value " << value << std::endl;return true;}bool MelsecMcClient::readShort(std::string area,int start,short &value){unsigned char cmd[21] = {0};memcpy(cmd, head, 7);cmd[7] = 0x0C;cmd[8] = 0x00;cmd[9] = 0x0A;cmd[10] = 0x00;cmd[11] = 0x01;cmd[12] = 0x04;cmd[13] = 0x00;cmd[14] = 0x00;cmd[17] = start / 255 / 255 % 255;cmd[16] = start / 255 % 255;cmd[15] = start % 255;unsigned char areaHex = decodeArea(area);if (areaHex == 0x00){std::cout << "不存在的地址 " << area << std::endl;return false;}cmd[18] = areaHex;cmd[19] = 0x01;cmd[20] = 0x00;if (!socket.write(cmd, sizeof(cmd))){return false;}unsigned char recv[512] = {0};if (!socket.read(recv, sizeof(recv))){return false;}if (recv[0] != 0xD0 && recv[7] != 0x04){std::cout << "数据格式不正确" << std::endl;return false;}value = recv[12] << 8 | recv[11];std::cout << "value " << value << std::endl;return true;}bool MelsecMcClient::writeShort(std::string area,int start,short value){unsigned char cmd[23] = {0};memcpy(cmd, head, 7);cmd[7] = 0x0E;cmd[8] = 0x00;cmd[9] = 0x0A;cmd[10] = 0x00;cmd[11] = 0x01;cmd[12] = 0x14;cmd[13] = 0x00;cmd[14] = 0x00;cmd[17] = start / 255 / 255 % 255;cmd[16] = start / 255 % 255;cmd[15] = start % 255;unsigned char areaHex = decodeArea(area);if (areaHex == 0x00){std::cout << "不存在的地址 " << area << std::endl;return false;}cmd[18] = areaHex;cmd[19] = 0x01;cmd[20] = 0x00;//写入short值 cmd[22] = (value >> 8) & 0xFF;cmd[21] = value & 0xFF;if (!socket.write(cmd, sizeof(cmd))){return false;}unsigned char recv[512] = {0};if (!socket.read(recv, sizeof(recv))){return false;}if (recv[0] != 0xD0 && recv[1] != 0x00){std::cout << "数据格式不正确" << std::endl;return false;}return true;}unsigned char MelsecMcClient::decodeArea(std::string area){if (area == "D"){return 0xA8;}else if (area == "M"){return 0x90;}else if (area == "X"){return 0x9C;}else if (area == "Y"){return 0x9D;}else if (area == "ZR"){return 0xB0;}else{return 0x00;}}
}
#include "client.h"using namespace MelsecMC;int main(int argc, char **argv)
{MelsecMcClient::Ptr client = std::make_shared<MelsecMcClient>();client->connectTo("10.10.14.60",6000);//int value;//melsecMcNet->readInt32("D",100,value);//client->writeInt32("D",101,122234);//client->writeShort("D",102,223);short value;client->readShort("D",102,value);return 0;
}

可利用 这个工具进行测试:

 协议参考:

https://www.jianshu.com/p/ca7f1609c8c1

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

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

相关文章

zTasker—简洁易用强大的定时热键一体自动化工具,效率倍增器

软件名称 zTasker 应用平台 PC Windows7及以上 一句简介 市面上定时类软件很多&#xff0c;但无一例外功能都很单一&#xff0c;要完成不同的任务&#xff0c;需要不同的软件 市面上的热键软件&#xff0c;要么功能少&#xff0c;要么像是AutoHotKey这样对于一般用户太专业…

分享篇:初识Canvas

目录 什么是Canvas&#xff1f; canvas的坐标 使用canvas的基本步骤​编辑 步骤&#xff1a; 1.需要一个canvas标签 代码演示&#xff08;方法1&#xff09; 代码演示&#xff08;方法2&#xff09; 2.需要获取 画笔 对象 &#xff08;获取 元素的 2D 上下文对象&#…

通过RSYNC在linux和windows间同步文件

通过RSYNC在linux和windows间同步文件 下载windows版本rsync下载后是一个zip的压缩包&#xff0c;直接解压就可使用配置windows到linux的秘钥拷贝公钥文件到linux服务器&#xff0c;实现免密配置同步命令结合windows计划任务实现定时同步文件 下载windows版本rsync 下载链接 h…

[kingbase运维之奇怪的现象]

#[kingbase运维之奇怪的现象] ##奇怪的现象 某银行数据中心应用反馈&#xff0c;业务接口日志记录了很多执行慢的SQL&#xff0c;出现的时间是随机的&#xff0c;单独在数据库客户端工具执行会很快返回结果。根据之前的经验推断是业务代码传入的参数类型与数据库表结构字段定义…

Kubernetes Configmap + Secret

Secret是什么&#xff1f; 在Kubernetes中&#xff0c;Secret是一种用于存储敏感信息的资源对象。它主要用于保存密码、API令牌、密钥和其他敏感数据&#xff0c;以供容器、Pod或集群中的其他资源使用。 Secret有以下特点&#xff1a; 安全存储&#xff1a;Secret对象被用于安全…

【面试必刷TOP101】合并k个已排序的链表 判断链表中是否有环

目录 题目&#xff1a;合并k个已排序的链表_牛客题霸_牛客网 (nowcoder.com) 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 题目&#xff1a;判断链表中是否有环_牛客题霸_牛客网 (nowcoder.com) 题目的接口…

公司电脑文件加密防泄密软件系统——「天锐绿盾」

天锐绿盾是一款功能强大的公司电脑文件加密防泄密软件系统&#xff0c;旨在保护企业的知识产权和商业机密。 PC访问地址&#xff1a;http://985.so/2y2n9 它具有以下几个主要特点&#xff1a; 文件加密&#xff1a;天锐绿盾会对存储在公司电脑上的所有敏感文件进行自动加密&am…

JavaScript学习笔记02

JavaScript笔记02 数据类型详解 字符串 在 JavaScript 中正常的字符串都使用单引号 或者双引号" "包裹&#xff1a;例&#xff1a; 转义字符 在 JavaScript 字符串中也可用使用转义字符&#xff08;参考&#xff1a;详解转义字符&#xff09;&#xff1a;例&…

百度收录和权重怎么提升-网站如何获得百度权重

你是否一直苦恼于网站权重的低迷&#xff1f;不知道如何开始提升网站权重&#xff0c;缺乏优质内容更新网站。不清楚如何进行关键词优化来提升网站排名和权重。SEO是一个需要持续投入时间和资源的过程。每个网站的情况都会有所不同&#xff0c;因此所花费的时间也会有所差异。然…

android studio 找不到设备

问题描述&#xff1a; 当android studio 没有打开&#xff0c; 执行adb devices 可以查看到设备&#xff0c; 当android studio 打开&#xff0c; 执行adb devices 可以查看不到设备&#xff0c; android studio 设备管理器中也没有设备 解决方法&#xff1a; 关闭android s…

数据库_之常用API的使用

数据库_之电商API MySQL C API 使用&#xff08;基本函数&#xff09; Mysql C API函数详解 MySQL的常用API 一个常用的程序调用MySQL数据库的时候通常都会调用以下API,下面来逐个分析. mysql_init() //函数原型 MYSQL *STDCALL mysql_init(MYSQL *mysql);这个API主要是用来分…

竞赛 基于机器视觉的二维码识别检测 - opencv 二维码 识别检测 机器视觉

文章目录 0 简介1 二维码检测2 算法实现流程3 特征提取4 特征分类5 后处理6 代码实现5 最后 0 简介 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器学习的二维码识别检测 - opencv 二维码 识别检测 机器视觉 该项目较为新颖&#xff0c;适合作为竞赛课…

数据分析三剑客之Pandas

1.引入 前面一篇文章我们介绍了numpy&#xff0c;但numpy的特长并不是在于数据处理&#xff0c;而是在它能非常方便地实现科学计算&#xff0c;所以我们日常对数据进行处理时用的numpy情况并不是很多&#xff0c;我们需要处理的数据一般都是带有列标签和index索引的&#xff0…

uni-app:单页面的页面切换

效果 代码 <template><view><view class"tab-bar"><text class"tab" :class"{ active: activeTab 0 }" click"changeTab(0)">页面1</text><text class"tab" :class"{ active: acti…

解锁网页开发的力量:深入探讨 JavaScript 编程

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 JavaScript 是现代网页开…

SpringBoot 整合 Websocket 通信demo (附浏览器聊天窗口)

1. 依赖 <!-- SpringBoot WebSocket --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dependency>2. 自动注册配置类 import org.springframework.context…

巨人互动|Google海外户Google排名算法机制

谷歌是目前最流行的搜索引擎之一&#xff0c;它的排名算法机制是谷歌搜索引擎的核心。谷歌的排名算法机制可以分为两个部分&#xff1a;计算网页相关度和排序结果。 一、计算网页相关度 谷歌通过计算每个页面的相关度来确定搜索结果的优先级。相关度是根据页面上的关键词、图…

【Java从入门到精通】这也许就是Java火热的原因吧!

前言&#xff1a;Java是一种高级的、面向对象的、可跨平台的程序设计语言。Java根据技术类别可划分为以下几类&#xff1a;JavaSE&#xff08;Standard Edition&#xff0c;标准版&#xff09;&#xff1a;支持面向桌面、嵌入式和移动设备的应用程序开发&#xff1b;JavaEE&…

input子系统框架、外设驱动开发

一、input子系统基本框架 Linux内核为了两个目的&#xff1a; 简化纯输入类外设&#xff08;如&#xff1a;键盘、鼠标、游戏杆、轨迹球、触摸屏。。。等等&#xff09;的驱动开发统一输入类外设产生的数据格式&#xff08;struct input_event&#xff09;&#xff0c;更加方…

功率放大器的作用有哪些

功率放大器是电子设备中常见的一个组件&#xff0c;其作用是将输入信号的能量放大到更高的功率级别&#xff0c;以用于驱动高功率负载或者提供足够的功率来满足特定需求。功率放大器在各种应用领域中发挥着重要作用&#xff0c;下面将详细介绍功率放大器的作用与应用。 图&…