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.hqwc.cn/news/108508.html

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

相关文章

第21章_瑞萨MCU零基础入门系列教程之事件链接控制器ELC

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

TableConvert-免费在线表格转工具 让表格转换变得更容易

在线表格转工具TableConvert TableConvert 是一个基于web的免费且强大在线表格转换工具&#xff0c;它可以在 Excel、CSV、LaTeX 表格、HTML、JSON 数组、insert SQL、Markdown 表格 和 MediaWiki 表格等之间进行互相转换&#xff0c;也可以通过在线表格编辑器轻松的创建和生成…

A股风格因子看板 (2023.09 第03期)

该因子看板跟踪A股风格因子&#xff0c;该因子主要解释沪深两市的市场收益、刻画市场风格趋势的系列风格因子&#xff0c;用以分析市场风格切换、组合风格暴露等。 今日为该因子跟踪第03期&#xff0c;指数组合数据截止日2023-08-31&#xff0c;要点如下 近1年A股风格因子检验统…

第四范式通过聆讯,AI上市公司将再添薪火

四次递表之后&#xff0c;估值超过200亿元的AI独角兽第四范式近日如愿通过港交所聆讯&#xff0c;即将成为资本市场AI皇冠上的又一颗明珠。 第四范式成立于2014年&#xff0c;历经11轮融资&#xff0c;不仅股东阵容豪华&#xff0c;也在商业化层面进展可观。今年4月底&#xf…

华为云云耀云服务器L实例评测|老用户回归的初印象

华为云云耀云服务器L实例评测&#xff5c;老用户回归的初印象 前言一、新面孔1. 云耀云服务器2. 服务器特色 二、上手感官体验1. 性价比感受2. 推荐宝塔面板3. CloudShell登录4. 安全性 总结 前言 其实笔者接触华为云已经很久了&#xff0c;第一次使用的云服务器就是华为云。当…

【计算机网络】什么是WebSocket?

目录 WebSocket简介协议优点使用场景 WebSocket WebSocket是一种网络传输协议,可在单个TCP连接上进行全双工通信&#xff0c;位于OSI模型的应用层。 WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务器主动向客户端推送数据。在WebSocket API中&a…

电脑死机的时候,CPU到底在做什么?

电脑死机&#xff0c;应该每个接触计算机的小伙伴都经历过吧。 尤其是早些年&#xff0c;电脑配置还没现在这么高的时候&#xff0c;多开几个重量级应用程序&#xff0c;死机就能如约而至&#xff0c;就算你把键盘上的CTRLALTDELETE按烂了&#xff0c;任务管理器也出不来&…

[移动通讯]【Carrier Aggregation-4】【LTE-4】

前言&#xff1a; 推荐两个在线工具 UECapabilityInformation Decoder: https://www.sqimway.com/ue_capa.php FGI Decoder: http://howltestuffworks.blogspot.com/2016/11/lte-feature-group-indicators-decoder.html 参考&#xff1a; 载波聚合&#xff08;CA&#xff09;…

PyTorch实战-实现神经网络图像分类基础Tensor最全操作详解(二)

前言 PyTorch可以说是三大主流框架中最适合初学者学习的了&#xff0c;相较于其他主流框架&#xff0c;PyTorch的简单易用性使其成为初学者们的首选。这样我想要强调的一点是&#xff0c;框架可以类比为编程语言&#xff0c;仅为我们实现项目效果的工具&#xff0c;也就是我们…

SeaArt.ai: 海艺AI绘画艺术图片模型创作平台

【产品介绍】 • 名称 SeaArt.ai • 具体描述 SeaArt.ai是一个基于人工智能技术的AI绘画工具&#xff0c;它可以根据你的描述或者关键词来生成符合你想象的图片。你可以选择不同的模式来创建不同类型的图片&#xff0c;比如人物、风景、建筑、神话、自…

【Spring】手动实现Spring底层机制-问题的引出

&#x1f384;欢迎来到边境矢梦的csdn博文&#x1f384; &#x1f384;本文主要梳理手动实现Spring底层机制-问题的引出 &#x1f384; &#x1f308;我是边境矢梦&#xff0c;一个正在为秋招和算法竞赛做准备的学生&#x1f308; &#x1f386;喜欢的朋友可以关注一下&#x1…

TypeScript类型兼容:结构化类型

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 1. 鸭子类型&#xff1a;定义和示例 2. 鸭子类型的优点 2.1 代码的灵活性 2.2 代码的复用 2.3 与 JavaScript 的…