Beckhoff / ADS

Beckhoff protocol to communicate with TwinCAT devices.
MIT License
491 stars 193 forks source link

ADS-sum command: Read or Write a list of variables with one single ADS-command #205

Open li317546360 opened 1 year ago

li317546360 commented 1 year ago

ADS-sum command wrapper example, intended as a reference to assist those in need. It has been successfully tested on macOS.

#pragma once

#include "AdsDevice.h"
#include "vector"

#include <array>

struct AdsVariableList {
    AdsVariableList(const AdsDevice& route, std::vector<std::string> symbolNames)
        :m_route{route},
        m_symboleNames {symbolNames},
        m_symboleEntrys {route.getSymbolEntrys(m_symboleNames)}
    {
        int dataSize = 0;
        for (const AdsSymbolEntry &en : m_symboleEntrys) {
            dataSize += en.size;
            AdsSymbolInfoByName info {en.iGroup,en.iOffs,en.size};
            m_symboleInfos.push_back(info);
        }
        m_rBuf.resize(4 * m_symboleNames.size() + dataSize,0);
        m_wBuf.resize(12 * symbolNames.size() + dataSize,0);
        initWBuf();
    }

    void read() {
        uint32_t bytesRead = 0;
        size_t symboleSize = m_symboleNames.size();
        uint32_t error = m_route.ReadWriteReqEx2(
            ADSIGRP_SUMUP_READ,
            symboleSize,
            m_rBuf.size(),
            const_cast<uint8_t*>(m_rBuf.data()),
            12 * symboleSize,
            m_symboleInfos.data(),
            &bytesRead);
        if (error || (m_rBuf.size() != bytesRead)) {
            throw AdsException(error);
        }
    }

    void write() {
        uint32_t bytesRead = 0;
        size_t symboleSize = m_symboleNames.size();
        copyData2WriteBuf();

        size_t readSize = 0;
        auto rbf = getStateBuf(&readSize);

        uint32_t error = m_route.ReadWriteReqEx2(
            ADSIGRP_SUMUP_WRITE,
            symboleSize,
            readSize,
            rbf,
            m_wBuf.size(),
            m_wBuf.data(),
            &bytesRead);
        if (error || (readSize != bytesRead)) {
            throw AdsException(error);
        }
    }

    size_t getSymboleSize(const std::string& name) const {
        int index = 0;
        for (const std::string &n : m_symboleNames) {
            if (name == n) {
                return m_symboleEntrys.at(index).size;
            }
            ++index;
        }
    }

    void * getSymboleData(const std::string& name, size_t* length) {

        auto pdbf = getDataBuf(nullptr);

        for (int i=0; i<m_symboleEntrys.size();++i) {
            if (name == m_symboleNames.at(i)) {
                if (length) {
                    *length = m_symboleEntrys.at(i).size;
                }
                return pdbf;
            }
            pdbf += m_symboleEntrys.at(i).size;
        }
    }

    void setSymbolData(const std::string& name, void *data, size_t length, size_t begin = 0) {
        size_t size = 0;
        auto buf = static_cast<uint8_t*>(getSymboleData(name,&size));
        if ((begin + length) > size)
            return;
        std::memcpy((buf + begin),data,length);
    }

    int32_t getStateCode(const std::string& name) const {

        auto psbf = getStateBuf(nullptr);
        for(int i=0;i<m_symboleNames.size();++i) {
            if (name == m_symboleNames.at(i)) {
                return *reinterpret_cast<int32_t*>(psbf + 4 * i);
            }
        }
    }

private:
    uint8_t * getStateBuf(size_t* size) const{
        size_t len = 4 * m_symboleNames.size();

        if (size) {
            *size = len;
        }
        return const_cast<uint8_t*>(m_rBuf.data());
    }

    uint8_t * getDataBuf(size_t* size) const{
        size_t stlen = 4 * m_symboleNames.size();
        size_t len = m_rBuf.size() - stlen;

        if (size) {
            *size = len;
        }
        return const_cast<uint8_t*>(m_rBuf.data()) + stlen;
    }

    uint8_t * getWriteDataBuf(size_t* size) const{
        size_t inflen = 12 * m_symboleNames.size();
        size_t len = m_rBuf.size() - inflen;

        if (size) {
            *size = len;
        }
        return const_cast<uint8_t*>(m_wBuf.data()) + inflen;
    }

    void copyData2WriteBuf() {
        size_t rdataSize = 0;
        auto rbf = getDataBuf(&rdataSize);
        auto wbf = getWriteDataBuf(nullptr);
        std::memcpy(wbf,rbf,rdataSize);
    }

    void initWBuf() {
        auto pInt32buf = reinterpret_cast<int32_t*>(const_cast<uint8_t*>(m_wBuf.data()));
        auto symboleSize = m_symboleNames.size();
        for (int i = 0; i < symboleSize; ++i) {
            *pInt32buf = m_symboleEntrys.at(i).iGroup;
            ++pInt32buf;
            *pInt32buf = m_symboleEntrys.at(i).iOffs;
            ++pInt32buf;
            *pInt32buf = m_symboleEntrys.at(i).size;
            ++pInt32buf;
        }
    }

    const AdsDevice &m_route;
    const std::vector<std::string> m_symboleNames;
    const std::vector<AdsSymbolEntry> m_symboleEntrys;
    std::vector<AdsSymbolInfoByName> m_symboleInfos;
    std::vector<uint8_t> m_rBuf;
    std::vector<uint8_t> m_wBuf;
};

Use

static void readWriteMulteExample(std::ostream& out,const AdsDevice& route,const std::vector<std::string> names) {
    AdsVariableList vbl{route,names};
    vbl.read();
    size_t size = 0;

    auto data = static_cast<uint8_t*>(vbl.getSymboleData(names[0], &size));
    auto data2 = static_cast<int16_t*>(vbl.getSymboleData(names[1],nullptr));
    auto data3 = static_cast<int16_t*>(vbl.getSymboleData(names[2],nullptr));
    auto data4 = static_cast<int16_t*>(vbl.getSymboleData(names[3],nullptr));
    for (size_t i=0;i<size;++i,++data) {
        out << static_cast<int>(*data) << ";";
    }
    out << "\n" << names[1] << "=" << *data2 << std::endl;
    out << names[2] << "=" << *data3 << std::endl;
    out << names[3] << "=" << *data4 << std::endl;
    std::array<uint8_t,10> arr {1,2,3,4,5,6,7,8,9,10};
    // std::reverse(arr.begin(),arr.end());
    vbl.setSymbolData(names[0],arr.data(),arr.size(),10);
    int16_t temp = 1122;
    vbl.setSymbolData(names[1],&temp,sizeof(int16_t));
    temp = 2211;
    vbl.setSymbolData(names[2],&temp,sizeof(int16_t));
    temp = 3344;
    vbl.setSymbolData(names[3],&temp,sizeof(int16_t));
    vbl.write();
    vbl.read();
    data = static_cast<uint8_t*>(vbl.getSymboleData(names[0], &size));
    data2 = static_cast<int16_t*>(vbl.getSymboleData(names[1],nullptr));
    data3 = static_cast<int16_t*>(vbl.getSymboleData(names[2],nullptr));
    data4 = static_cast<int16_t*>(vbl.getSymboleData(names[3],nullptr));
    for (size_t i=0;i<size;++i,++data) {
        out << static_cast<int>(*data) << ";";
    }
    out << "\n" << names[1] << "=" << *data2 << std::endl;
    out << names[2] << "=" << *data3 << std::endl;
    out << names[3] << "=" << *data4 << std::endl;
}

static void runExample(std::ostream& out)
{
    static const AmsNetId remoteNetId { 192, 168, 3, 15, 1, 1 };
    static const char remoteIpV4[] = "192.168.2.38";

    // uncomment and adjust if automatic AmsNetId deduction is not working as expected
    //bhf::ads::SetLocalAddress({192, 168, 0, 1, 1, 1});

    AdsDevice route {remoteIpV4, remoteNetId, AMSPORT_R0_PLC_TC3};
    // std::vector<std::string> symbols{"MAIN.byByte","MAIN.ai_data","MAIN.ao_data"};
    std::vector<std::string> symbols{"MAIN.byByte","MAIN.ao_data","MAIN.ao_data2","MAIN.ai_Data"};
    readWriteMulteExample(out,route,symbols);
    // notificationExample(out, route);
    // notificationByNameExample(out, route);
    // readExample(out, route);
    // readByNameExample(out, route);
    // readWriteExample(out, route);
    // readWriteArrayExample(out, route);
    // readStateExample(out, route);
li317546360 commented 1 year ago

I forgot about the extension part of AdsDevice, which is used to query variable information

AdsSymbolEntry AdsDevice::getSymbolEntry(const std::string &symbolName) const
{
    AdsSymbolEntry entry;
    uint32_t bytesRead = 0;
    uint32_t error = ReadWriteReqEx2(
        ADSIGRP_SYM_INFOBYNAMEEX,
        0x0,
        sizeof(entry),
        &entry,
        sizeof(symbolName),
        symbolName.c_str(),
        &bytesRead);
    if (error) {
        throw AdsException(error);
    }
    return entry;
}

std::vector<AdsSymbolEntry> AdsDevice::getSymbolEntrys(const std::vector<std::string>& symbolNames) const
{
    std::vector<AdsSymbolEntry> re;
    for (const std::string &name : symbolNames){
        re.push_back(getSymbolEntry(name));
    }
    return re;
}