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
相关文章:

linux 下 C++ 与三菱PLC 通过MC Qna3E 二进制 协议进行交互
西门子plc 有snap7库 进行交互,并且支持c 而且跨平台。但是三菱系列PLC并没有现成的开源项目,没办法只能自己拼接,我这里实现了MC 协议 Qna3E 帧,并使用二进制进行交互。 #pragma once#include <stdio.h> #include <std…...

Spring基础(2w字---学习总结版)
目录 一、Spirng概括 1、什么是Spring 2、什么是容器 3、什么是IoC 4、模拟实现IoC 4.1、传统的对象创建开发 5、理解IoC容器 6、DI概括 二、创建Spring项目 1、创建spring项目 2、Bean对象 2.1、创建Bean对象 2.2、存储Bean对象(将Bean对象注册到容器…...

07 目标检测-YOLO的基本原理详解
一、YOLO的背景及分类模型 1、YOLO的背景 上图中是手机中的一个app,在任何场景下(工业场景,生活场景等等)都可以试试这个app和这个算法,这个app中间还有一个button,来调节app使用的模型的大小,更大的模型实时性差但精…...
每日一题 78子集(模板)
题目 78 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 示例 1: 输入:nums [1,2,3] 输出:[[],[1],[2]…...

OpenCV之形态学操作
形态学操作包含以下操作: 腐蚀 (Erosion)膨胀 (Dilation)开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 其中腐蚀和膨胀操作是最基本的操作,其他操作由这两个操作变换而来。 腐蚀 用一个结构元素…...

设计模式:享元模式
设计模式:享元模式 什么是享元模式 首先我们需要简单了解一下什么是享元模式。享元模式(Flyweight Pattern):主要用于减少创建对象的数量,以减少内存占用和提高性能。享元模式的重点就在这个享字,通过一些共享技术来减少对象的创建ÿ…...

汉诺塔问题(包含了三台柱和四台柱)——C语言版本
目录 1. 什么是汉诺塔 2. 三座台柱的汉诺塔 2.1 思路 2.2 三座台柱的汉诺塔代码 3. 四座台柱的汉诺塔 3.1 思路 3.2 四座台柱的汉诺塔代码 1. 什么是汉诺塔 汉诺塔代码的功能:计算盘子的移动次数,由数学公式知,汉诺塔的盘子移动次数与…...

【实训项目】滴滴电竞APP
1.设计摘要 2013年国家体育总局决定成立一支由17人组成的电子竞技国家队,第四届亚室会中国电竞代表队 出战第四届亚洲室内和武道运动会。 2014年1月13日CCTV5《体育人间》播放英雄联盟皇族战队的纪录片。 在2015到2019年间,我国电竞战队取得的无数值得…...

C++核心编程--类篇
C核心编程 1.内存分区模型 C程序在执行时,将内存大方向分为4个区域 意义:不同区域存放数据,赋予不同的生命周期,更能灵活编程 代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放…...
java中用feign远程调用注解FeignClient的时候不重写Encoder和Decoder怎么格式不对呢?
如果在使用 Feign 进行远程调用时,没有重写 Encoder 和 Decoder,但仍然遇到格式不对的问题,可能是由于以下原因之一: 服务端返回的数据格式与客户端期望的格式不匹配:Feign 默认使用基于 Jackson 的 Encoder 和 Decode…...
记录使用Docker Compose 部署《XAPI项目》遇道的问题及解决方案
《XAPI项目》:GitHub仓库(勿打🚫小破站一个) 这篇文档,主要内容是记录使用Docker Compose 部署《XAPI项目》遇道的问题及解决方案 目录 📚 本地MySQL数据如何导入到容器内的MySQL中❎ 解决报错:…...

腾讯云OCR实践 - 降低客服财务运营成本
一、 前言: 随着图片时代的飞速发展,大量的文字内容为了优化排版和表现效果,都采用了图片的形式发布和存储,这为内容的传播和安全性带来了很大的便利,需要做重复性劳动。 OCR文字扫描工具也逐渐的应运而生,…...
springboot+vue上传图片
这里是一个简单的示例,演示了如何在Spring Boot中从Vue.js上传图像: 1.前端Vue.js代码: <template><div><input type"file" change"handleFileUpload"><button click"uploadImage">…...

高压电缆护层接地环流及温度在线监测系统
高压电缆的金属护层是电缆的重要组成部分,当缆芯通过电流时,会在金属护层上产生环流,外护套的绝缘状态差、接地不良、金属护层接地方式不正确等等都会引起护套环流异常现象,严重威胁电缆运行安全。 当电缆金属护层环流出现异常时…...

无涯教程-JavaScript - IPMT函数
描述 IPMT函数根据定期,固定的还款额和固定的利率返回给定投资期限内的利息支付。 语法 IPMT (rate, per, nper, pv, [fv], [type])争论 Argument描述Required/OptionalRateThe interest rate per period.RequiredPerThe period for which you want to find the interest a…...

【EI会议征稿】第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023)
第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023) 第三届机械自动化与电子信息工程国际学术会议(MAEIE 2023)将于2023年12月15-17日在江苏南京举行。本会议通过与业内众多平台、社会各团体协力,聚集机械自动…...
手写实现LRN局部响应归一化算子
1、重写算子的需求 芯片推理过程中遇到很多算子计算结果不对的情况,原因是封装的算子会在某些特殊情况下计算超限,比如输入shape特别大或者数值特别大时,LRN算子计算会出现NAN值,所以需要重写算子。先对输入数据做一个预处理&…...

朗思科技数字员工通过统信桌面操作系统兼容性互认认证
近日,朗思科技数字员工与统信桌面操作系统V20进行了兼容互认,针对上述产品的功能、兼容性方面,通过共同严格测试表明——朗思科技数字员工在统信桌面操作系统 V20上整体运行稳定,满足功能及兼容性测试要求。 北京朗思智能科技有限…...

十六、Webpack常见的插件和模式
一、认识插件Plugin Webpack的另一个核心是Plugin,官方有这样一段对Plugin的描述: While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset m…...

ChatGPT新增超强插件:文本直接生成视频、海报,支持自定义修改!
全球著名在线设计平台Canva,在ChatGPT Plus(GPT-4)上推出了插件功能,用户通过文本提示,几秒钟就能生成演示文稿、PPT插图、电子书封面、宴会邀请函等各种精美设计海报,同时支持生成视频。 该插件最强大的功…...
vscode里如何用git
打开vs终端执行如下: 1 初始化 Git 仓库(如果尚未初始化) git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...

云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...