西門子plc 有snap7庫 進(jìn)行交互,并且支持c++ 而且跨平臺(tái)。但是三菱系列PLC并沒有現(xiàn)成的開源項(xiàng)目,沒辦法只能自己拼接,我這里實(shí)現(xiàn)了MC 協(xié)議 Qna3E 幀,并使用二進(jìn)制進(jìn)行交互。
#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; // 用于發(fā)送/接受數(shù)據(jù)
mutex m;
public:
PlcSocket();
~PlcSocket();
// 初始化socket
bool initSocket(string ip, int port, int milSecond);
// 關(guān)閉socket
bool closeSocket();
// 發(fā)送數(shù)據(jù)
bool write(unsigned char *buffer, int len);
// 接收數(shù)據(jù)
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 創(chuàng)建失敗:" << 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;
// 設(shè)置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協(xié)議的通訊方式有很多種:4C、3C、2C、1C、4E、3E、1E幀格式 數(shù)據(jù)格式分為二進(jìn)制格式和ASCII碼格式
本代碼采用 3E + 二進(jìn)制格式
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
//請求數(shù)據(jù)物理長度
cmd[7] = 0x10;
cmd[8] = 0x00;
// CPU監(jiān)視定時(shí)器 表示等待PLC響應(yīng)的timeout時(shí)間
cmd[9] = 0x0A;
cmd[10] = 0x00;
//寫命令 跟讀的差別是:讀是0104,寫是0114 ;就是04和14的差別
cmd[11] = 0x01;
cmd[12] = 0x14;
//(子命令) : 值是0表示按字讀寫入1個(gè)字=16位),如果值是1就按位寫入
cmd[13] = 0x00;
cmd[14] = 0x00;
//(首地址):地址因?yàn)榭缍缺容^大,所以用了3個(gè)字節(jié);值640000 返過來是000064,十進(jìn)制就是100
cmd[17] = start / 255 / 255 % 255;
cmd[16] = start / 255 % 255;
cmd[15] = start % 255;
//(軟元件 讀取的區(qū)域) : 表示讀取PLC寄存器的類型: 這里的A8表示D點(diǎn);其他常見的有: 90-M點(diǎn);9C-X點(diǎn);9D-Y點(diǎn);B0-ZR外部存儲(chǔ)卡
unsigned char areaHex = decodeArea(area);
if (areaHex == 0x00)
{
std::cout << "不存在的地址 " << area << std::endl;
return false;
}
cmd[18] = areaHex;
//寫入長度 00 02 =10進(jìn)制2個(gè)字 =32位 = 4個(gè)字節(jié) =1個(gè)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;
}
// 讀取數(shù)據(jù)
unsigned char recv[512] = {0};
if (!socket.read(recv, sizeof(recv)))
{
return false;
}
if (recv[0] != 0xD0 && recv[1] != 0x00)
{
std::cout << "數(shù)據(jù)格式不正確" << 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);
//請求數(shù)據(jù)長度 也要反過來,值是000C,也就是12;表示后面的報(bào)文內(nèi)容的長度是12
cmd[7] = 0x0C;
cmd[8] = 0x00;
// CPU監(jiān)視定時(shí)器 表示等待PLC響應(yīng)的timeout時(shí)間
cmd[9] = 0x0A;
cmd[10] = 0x00;
// 批量讀命令 值是0401(所有值都要反過來看);表示批量讀??;如果是1401就是隨機(jī)寫取;
cmd[11] = 0x01;
cmd[12] = 0x04;
//(子命令) : 值是0表示按字讀取(1個(gè)字=16位),如果值是1就按位讀取
cmd[13] = 0x00;
cmd[14] = 0x00;
//(首地址):地址因?yàn)榭缍缺容^大,所以用了3個(gè)字節(jié);值640000 返過來是000064,十進(jìn)制就是100
cmd[17] = start / 255 / 255 % 255;
cmd[16] = start / 255 % 255;
cmd[15] = start % 255;
//(軟元件 讀取的區(qū)域) : 表示讀取PLC寄存器的類型: 這里的A8表示D點(diǎn);其他常見的有: 90-M點(diǎn);9C-X點(diǎn);9D-Y點(diǎn);B0-ZR外部存儲(chǔ)卡
unsigned char areaHex = decodeArea(area);
if (areaHex == 0x00)
{
std::cout << "不存在的地址 " << area << std::endl;
return false;
}
cmd[18] = areaHex;
// 讀取長度 00 02 =10進(jìn)制2個(gè)字 =32位 = 4個(gè)字節(jié) =1個(gè)int
cmd[19] = 0x02;
cmd[20] = 0x00;
// 發(fā)送數(shù)據(jù)
if (!socket.write(cmd, sizeof(cmd)))
{
return false;
}
// 讀取數(shù)據(jù)
unsigned char recv[512] = {0};
if (!socket.read(recv, sizeof(recv)))
{
return false;
}
// 解析數(shù)據(jù)
// D0 00 00 FF FF 03 00 06 00 00 00 BB 02 96 49
// D0 00 (響應(yīng)) :表示反饋信息,固定D0 00
// 00 (網(wǎng)絡(luò)編號 ): 與上同
// FF (PLC編號) : 與上同
// FF 03 (請求目標(biāo)模塊IO編號) : 與上同
// 00 (請求目標(biāo)模塊站編號): 與上同
// 06 00 應(yīng)答數(shù)據(jù)物理長度
if (recv[0] != 0xD0 && recv[7] != 0x06)
{
std::cout << "數(shù)據(jù)格式不正確" << 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 << "數(shù)據(jù)格式不正確" << 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 << "數(shù)據(jù)格式不正確" << 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;
}
可利用 這個(gè)工具進(jìn)行測試:
?協(xié)議參考:文章來源:http://www.zghlxwxcb.cn/news/detail-696984.html
https://www.jianshu.com/p/ca7f1609c8c1文章來源地址http://www.zghlxwxcb.cn/news/detail-696984.html
到了這里,關(guān)于linux 下 C++ 與三菱PLC 通過MC Qna3E 二進(jìn)制 協(xié)議進(jìn)行交互的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!