前段时间用上位机与倍福PLC通讯,调试通过了,代码发出来,大家一起学习
头文件
cpp
#pragma once
#include <string>
using namespace std;
int TestPLCConn();
class PLC{
private:
//记录PLC的IP地址和端口号
string ip;
int port;
//暂时这么多参数,后续再添加
int BeginCommu();
int EndCommu();
bool isOpen;
public:
PLC();
PLC(PLCPara ¶);
//测试PLC连接是否成功
int TestConn();
//设置PLC参数
int SetPara(PLCPara& para);
//接收心跳包
int RecvHeartBeat(bool& heartBeat);
//发送心跳包
int SendHeartBeat();
};
CPP文件,其中的异常捕获机制是我自己写的,参考的朋友需要自行修改。
cpp
#include "PLC.h"
#include <WinSock2.h>
#include "TcAdsDef.h"
#include "TcAdsAPI.h"
#include "CommonMatTool.h"
using namespace std;
#define RETURN_OK 0
#define RETURN_ERR -1
//用于记录PLC对象的执行情况,结束就置为true,自动发送心跳包直接关闭
bool isClose = false;
//用于记录PLC连接情况,连接就置为true
bool isLink = false;
//全局变量
AmsAddr Addr;
//将IP地址字符串解析成四个整数
void parseIPAddress(const std::string& ip, int& a, int& b, int& c, int& d) {
// 使用 sscanf 函数解析 IP 地址字符串
sscanf(ip.c_str(), "%d.%d.%d.%d", &a, &b, &c, &d);
}
PLC::PLC()
{
}
PLC::PLC(PLCPara& para)
{
ip = para.ip;
port = para.port;
}
int PLC::TestConn()
{
long nPort;
int result = 0;
nPort = AdsPortOpen();
if (nPort == 0)
{
result = RETURN_ERR;
}
else
{
result = RETURN_OK;
}
AdsPortClose();
return result;
}
//这是一个多线程的函数,用于发送心跳包给PLC
void AutoConnThread(void* para)
{
while (true)
{
((Robot*)para)->SendHeartBeat();
Sleep(500);
((Robot*)para)->RecvHeartBeat(isLink);
// 这个标志位为true,表明可以结束该进程了
if (isClose)
{
break;
}
}
}
int PLC::SetPara(PLCPara& para)
{
try
{
ip = para.ip;
port = para.port;
// 创建线程
isClose = false;
std::thread t(AutoConnThread, this);
t.detach(); // 让线程在后台运行,不会自动销毁
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
int PLC::BeginCommu()
{
try
{
//如果已经建立了通信,需等待
if (true == isOpen)
{
Sleep(10);
}
isOpen = true;
long nPort;
long nErr;
//初始化内容:建立路由、获取NetID、设置PLC端口号
nPort = AdsPortOpen();
//如果是远程ADS则使用下面注释的两句确定AmsNetId,id填入远程控制器的ip
int ip1, ip2, ip3, ip4;
parseIPAddress(ip, ip1, ip2, ip3, ip4);
AmsNetId id = { ip1,ip2,ip3,ip4,1,1 };
Addr.netId = id;
//确定端口号
Addr.port = port;
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
int PLC::EndCommu()
{
try
{
// Close communication port
AdsPortClose();
isOpen = false;
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
//写数据
int WriteData(string& str, void* p, int length)
{
try
{
long nErr;
ULONG lHdlVar;
char szVar[100];// = { "MAIN.Test" };//通过变量名通讯
std::strcpy(szVar, str.c_str());
//得到句柄
nErr = AdsSyncReadWriteReq(&Addr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar),
&lHdlVar, sizeof(szVar), szVar);
//写入值
nErr = AdsSyncWriteReq(&Addr, ADSIGRP_SYM_VALBYHND, lHdlVar, length, p);
//释放句柄
nErr = AdsSyncWriteReq(&Addr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
//读取数据
int ReadData(string& str, void* p, int length)
{
try
{
long nErr;
ULONG lHdlVar;
char szVar[100];// = { "MAIN.Test" };//通过变量名通讯
std::strcpy(szVar, str.c_str());
//得到句柄
nErr = AdsSyncReadWriteReq(&Addr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
//读出值
nErr = AdsSyncReadReq(&Addr, ADSIGRP_SYM_VALBYHND, lHdlVar, length, p);
//释放句柄
nErr = AdsSyncWriteReq(&Addr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
//读取PLC的状态
int PLC::GetStatus(STATUS& status)
{
try
{
BeginCommu();
bool result = false;
string varName = "MAIN_Ads.st_plc_to_pc_check.bRq_GlassCheck";
ReadData(varName, (void*)&result, sizeof(result));
if (true == result)
{
status = STEP1;
goto end;
}
varName = "MAIN_Ads.st_plc_to_pc_check.bRq_PositionTest";
ReadData(varName, (void*)&result, sizeof(result));
if (true == result)
{
status = STEP2;
goto end;
}
varName = "MAIN_Ads.st_plc_to_pc_check.bRq_LoadCheck";
ReadData(varName, (void*)&result, sizeof(result));
if (true == result)
{
status = STEP3;
goto end;
}
varName = "MAIN_Ads.st_plc_to_pc_check.bRq_UnloadCheck";
ReadData(varName, (void*)&result, sizeof(result));
if (true == result)
{
status = STEP4;
goto end;
}
status = IDLE;
end:
EndCommu();
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
/// <summary>
/// 接收PLC端的心跳信息
/// </summary>
/// <returns></returns>
int Robot::RecvHeartBeat(bool &heartBeat)
{
try
{
heartBeat = false;
BeginCommu();
string varName = "MAIN_Ads.st_plc_to_pc_check.bConn";
ReadData(varName, (void*)&heartBeat, sizeof(heartBeat));
EndCommu();
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
/// <summary>
/// 发送心跳信息
/// </summary>
/// <returns></returns>
int Robot::SendHeartBeat()
{
try
{
static bool heartBeat = false;
BeginCommu();
string varName = "MAIN_Ads.st_pc_to_plc_check.bConn";
//BOOL begin = true;
WriteData(varName, (void*)&heartBeat, sizeof(heartBeat));
//心跳信息取相反的值
heartBeat = !heartBeat;
EndCommu();
return RETURN_OK;
}
PRINT_ERR;
return RETURN_ERR;
}
//用作机器人通讯的测试,不是正式代码
int TestRobotConn()
{
Robot robot;
RobotPara para;
para.ip = "192.168.1.2";
para.port = 801;
robot.SetPara(para);
robot.SendHeartBeat();
getchar();
return RETURN_OK;
}