前言
建议初学者先看这一章节内容,里面包括一些基础的环境配置和项目建立流程,以后开发项目这些流程是通用的,务必掌握并熟练。
链接: 上位机使用C++通过ADS协议与倍福PLC通信例程-布尔变量的读取
字符串知识
C/C++对字符串的处理
字符串是字符按一定顺序排列的结果,大部分情况下就是由字母组成的单词和句子,在C/C++中,字符串不是一个基本变量类型,基本变量类型是指char,int,float等这种变量类型,C/C++把字符串储存在字符数组中,即char类型的数组中,并在数组的末尾添加“\0”空字符来表示字符串的结束,可以理解为这是一个结束标记,这样处理以后,实际字符串占的存储单元就多了1个字节,如果声明一个char[20]的数组,那它最多能存储19个字符,如果直接声明一个例如string类型的变量,例如下面这个
// string类型的变量
std::string VarString1 = "Trump is back";
那么 VarString1将占用14个字节(空格也要占用一个字节)
TwinCAT对字符串的处理
在TwinCAT中,如果使用的是ST编程语言(IEC61131-3中规定的PLC编程语言中的一种),它是提供string类型的变量的,但其存储方式与C/C++对字符串的处理是一样的,同样用字符数组来存储,如果不指定大小,系统默认会分配一个80个字符的空间留给用户使用,在存储单元中则占据81个字节(因为要存储\0结束符号),如下图所示,我们实际赋值的时候,不要超过80个字符就可以了。
上位机程序实现
在上位机中,声明一个char Data1[81]的数组用来存放从PLC中读取上来的字符串值,这里的81即和PLC中string声明出来的变量所占空间匹配的结果。
#include <iostream>
#include<conio.h>
#include <stdio.h>
#include<iomanip>
#include<Windows.h>
#include "TcAdsDef.h" //结构体和常量的声明
#include "TcAdsAPI.h" // ADS函数的声明
long nErr, nPort; //定义端口变量
AmsAddr LocalAddr; //定义上位机AMS地址变量
PAmsAddr LocalpAddr = &LocalAddr; //定义上位机ADS端口地址变量(上位机用来进行ADS通信的端口)
AmsAddr Tc3Addr; //定义安装有TwinCAT3的下位机AMS地址变量(由于是本机测试,上位机和下位机的AMS地址是一样的)
PAmsAddr Tc3pAddr = &Tc3Addr; //定义下位机端口地址变量(这里是读取PLC程序中变量,所以该端口固定为851)
/* 读取bool量的配置信息 */
unsigned long IndexGroup = 0xF020; //要读取的对象的索引编号
unsigned long IndexOffset = 0x5DE93; // 要读取对象的偏移量编号
unsigned long Length = 1; //要读取对象的大小,计量单位为byte
bool Data; //该变量用来存放读上来的值,变量类型要和待读取的值匹配
/* 读取字符串变量的配置信息 */
unsigned long IndexGroup1 = 0x4040;//要读取的对象的索引编号
unsigned long IndexOffset1 = 0x6CF40; // 要读取对象的偏移量编号
unsigned long Length1 = 81; //要读取对象的大小,计量单位为byte
char Data1[81]; //该变量用来存放读上来的值,变量类型要和待读取的值匹配
void ReadBoolen(void);
void ReadString(void);
int main()
{
//建立接口通讯连接
nPort = AdsPortOpen(); //打开ADS通讯端口
nErr = AdsGetLocalAddress(LocalpAddr); //自动获取本地地址
if (nErr)
std::cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
else
{
std::cout << "LocalAdsAddress is: ";
printf("%d.", LocalpAddr->netId.b[0]);
printf("%d.", LocalpAddr->netId.b[1]);
printf("%d.", LocalpAddr->netId.b[2]);
printf("%d.", LocalpAddr->netId.b[3]);
printf("%d.", LocalpAddr->netId.b[4]);
printf("%d.", LocalpAddr->netId.b[5]);
std::cout << std::endl;
std::cout << "LocalAdsPort Assigned is:" << LocalpAddr->port << std::endl;
}
if (nPort == LocalpAddr->port)
{
std::cout << "LocalAdsPort: " << nPort << " opened!" << std::endl;
}
else
{
std::cout << "LocalAdsPort open failed!" << std::endl;
}
Tc3pAddr->netId.b[0] = 5; //填入下位机的地址,即远程PLC的AmsNetID
Tc3pAddr->netId.b[1] = 19;
Tc3pAddr->netId.b[2] = 218;
Tc3pAddr->netId.b[3] = 60;
Tc3pAddr->netId.b[4] = 1;
Tc3pAddr->netId.b[5] = 1;
Tc3pAddr->port = 851; //填入下位机的TC3 PLC的通讯端口(851),这是倍福官方规定的,固定为851,TC2中为801,增加几个PLC,就依次增加为852,853...
std::cout << "The Remote PLC AmsNetID is: ";
printf("%d.", Tc3pAddr->netId.b[0]);
printf("%d.", Tc3pAddr->netId.b[1]);
printf("%d.", Tc3pAddr->netId.b[2]);
printf("%d.", Tc3pAddr->netId.b[3]);
printf("%d.", Tc3pAddr->netId.b[4]);
printf("%d.", Tc3pAddr->netId.b[5]);
std::cout << std::endl;
std::cout << "The Remote PLC ADS ComunicationPort is : "<<Tc3pAddr->port<<std::endl;
while (1)
{
Sleep(500);
ReadBoolen();
ReadString();
}
}
void ReadBoolen(void)
{
nErr = AdsSyncReadReq(Tc3pAddr, IndexGroup, IndexOffset, Length, &Data);
if (nErr)
std::cerr << "ReadFailed! The Error Code is: " << nErr << '\n';
else
{
std::cout << "Operation Succeed!" << std::endl;
std::cout << "The Value Read is:" << Data << std::endl;
}
Sleep(500);//每500ms刷新一次值
}
void ReadString(void)
{
nErr = AdsSyncReadReq(Tc3pAddr, IndexGroup1, IndexOffset1, Length1, &Data1);
if (nErr)
std::cerr << "ReadFailed! The Error Code is: " << nErr << '\n';
else
{
std::cout << "Operation Succeed!" << std::endl;
std::cout << "The Value Read is:" << Data1 << std::endl;
}
Sleep(500);//每500ms刷新一次值
}
下位机程序实现
变量声明区的内容
PROGRAM MAIN
VAR
bVarOut1 AT%Q*:BOOL;
bVarIn1 AT%I*:BOOL;
sString:STRING;
END_VAR
程序区的内容
bVarOut1 := bVarIn1;
sString :='Trump is back!';
运行上位机程序和远程PLC(切换到运行状态下),可以看见能成功读取到PLC中声明的string类型变量