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

这篇具有很好参考价值的文章主要介绍了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();

        // 初始化socket
        bool initSocket(string ip, int port, int milSecond);
        // 关闭socket
        bool 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)
    {
        // create
        int 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());

        // connect
        int res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
        if (res == -1)
        {
            cout << "connect 链接失败:" << endl;
            return false;
        }
        cout << "connect 链接成功:" << endl;

        // 设置timeout
        setsockopt(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,十进制就是100
        cmd[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个int
        cmd[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;表示后面的报文内容的长度是12
        cmd[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,十进制就是100
        cmd[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个int
        cmd[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;
}

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

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

 协议参考:

https://www.jianshu.com/p/ca7f1609c8c1文章来源地址https://www.toymoban.com/news/detail-696984.html

到了这里,关于linux 下 C++ 与三菱PLC 通过MC Qna3E 二进制 协议进行交互的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 三菱PLC通过三菱FX1N-232BD模块与威纶通触摸屏设置

    1.PLC编程设置,通过GX Developer编程软件设置PLC参数如图1 所示 注意红色框设置选择的参数设置,绿色框参数设置配置要和威纶通EasyBuilder Pro的参数一直即可。 注意:PLC参数设置完成后,下载到PLC中,然后PLC需要断电,在启动设置的参数才能生效。 2.维纶触摸屏编程软件EasyBu

    2024年02月12日
    浏览(122)
  • python实现MC协议(SLMP 3E帧)的TCP服务端

    python实现MC协议(SLMP 3E帧)的TCP服务端是一件稍微麻烦点的事情。它不像modbusTCP那样,可以使用现成的pymodbus模块去实现。但是,我们可以根据协议帧进行组包,自己去实现帧的格式,而这一切可以基于socket模块。本文为第一篇。 参考文档:三菱PLC之SLMP协议报文说明 - 知乎

    2024年02月06日
    浏览(41)
  • 三菱PLC 流水灯控制

    方式一思路:每一秒让计数器 C0 的值加一,然后将 C0 对应的值赋值给输出灯 Y10 - Y15。 方式二思路:每一秒让寄存器 D0 的值向左移1位,然后使用组合位元件将 D0 的低八位值传送给输出灯 Y10 - Y17。    后面有更好的思路再来增加。

    2024年02月16日
    浏览(81)
  • 上位机开发之三菱PLC通信

    三菱MC协议是开发三菱系列PLC必须掌握的一种通信协议,其目的是让外部设备,可以通过串行通信模块C24或以太网接口模块E71,实现对可编程控制器CPU的数据访问。 MC协议是Melsec协议的简称。 外部设备通过C24/E71访问可编程控制器CPU时,可以选择不同的通信帧及数据格式来实现

    2023年04月08日
    浏览(44)
  • 三菱plc数据寄存器D

    在输入和输出处理、模拟控制和位置控制期间,需要许多数据寄存器来存储数据和参数。数据寄存器为16位,最高位为符号位。两个数据寄存器可以组合存储32位数据,最高位仍然是符号位。   数据寄存器分为以下几类: 通用数据寄存器D0 ~ D199有200个点。 断电/锁存寄存器D20

    2024年02月08日
    浏览(45)
  • 【2023】JAVA和PLC实现通讯读取写入数据,以三菱PLC举例

    进行PLC连接需要了解两个概念: 长连接和短连接 。为了更好的说明当前的通信情况,我把所有的通信拆分为四个部分,连接,发,收,断开。 短连接:连接,发,收,断开,连接,发,收,断开,连接,发,收,断开,连接,发,收,断开…无限循环 长连接:连接,发,收

    2024年02月12日
    浏览(40)
  • 【工控老马】三菱Q系列PLC调试及三菱触摸屏报警使用说明详解

    一 程序段注解 第1步 编辑菜单→文档创建→声明编辑 第2步 在左侧空白处双击→弹出行间声明输入弹窗→输入申明内容 第3步 显示如图,转换+全部编译 二 插入比较指令 第1步 编辑菜单→梯形图编辑模式→写入模式 第2步 在需要插入指令的地方双击→输入指令内容 第3步 显示

    2024年02月05日
    浏览(64)
  • C# 三菱PLC上位机开发环境搭建

    一、安装软件 用到两个三菱的软件: 1. MX Component(下载地址 也可以直接在官网上搜索,注意MX后面有空格) 用于连接PLC 2. GX Works2(下载地址 用GX Works3也行) 这个软件主要是电气做PLC编程,我们用来作为仿真,省去开发时摆弄真实PLC 序列号:117-570766844 二、配置软件 MX

    2023年04月18日
    浏览(51)
  • 三菱plc学习入门(创建属于自己的FB模块)

            在现实生活中,往往会需要修改一些属于方便自己的库,1,自己创建的库方便自己使用与查看2,提高自己编程能力,3,保护自己的程序不被外人修改!!!下面就让我来操作一下 导入需要库    选择需要的库 FB设置块口令         当我们需要完成一个功能,并且

    2024年01月19日
    浏览(36)
  • C++:通过ofstream写入二进制文件内容

    C++:通过ifstream读取二进制文件内容_c++ ifstream 二进制读取-CSDN博客 介绍了读取二进制文件的方法。 本文介绍一下写入二进制数据到文件的方法: 1.通过write

    2024年01月19日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包