Speicherverbrauch shared_ptr und normaler *



  • Hi camper.

    Du bist aktuell so weit wie ich :-).

    Dies gilt aber nicht nur für Array -Elemente sondern auch für die Instanzen von Strukturen.

    Die Daten werden dazu gebraucht um Zugriffsfunktionalität herzustellen um auf bestimmte Variablen der SPS direkt zugreifen zu können.

    Ok also praktisches Beipiel:

    Ich bekomme eine Liste von Variablennamen.
    z.B

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"

    Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.

    Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
    Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.

    Das hat bisher wie gesagt auch sehr gut funktioniert. Die Lookup Tabelle wird ja nur einmal ab Start erzeugt und danach wieder frei gegeben.


  • Mod

    booster schrieb:

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState

    Die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch. Es ist völlig absurd, die absolut gleiche Information mehrfach zu speichern, solange nur das Array groß genug ist, wird dir immer am Ende der Speicher ausgehen, unabhängig von allen anderen Versuchen, möglichst wenig Speicher zu verbruachen.

    Wie wäre es mit folgender Änderung

    UINT	CAdsParseSymbols::SubSymbolCount(PAdsDatatypeEntry	pEntry)
    ...		else if ( pEntry->arrayDim )
    		{
    			cnt = 1;
    		}
    ...
    
    BOOL	CAdsParseSymbols::SubSymbolInfo(CAdsSymbolInfo main, UINT sub, CAdsSymbolInfo& info)
    ...
    		else if ( pEntry->arrayDim )
    		{
                            // store one generic element representing any array elements
    
    			info.iGrp	= main.iGrp;
    			info.iOffs	= main.iOffs;
    			info.size	= baseSize;
    			info.dataType	= pEntry->dataType;
    			info.flags	= pEntry->flags;
    			info.type	= PADSDATATYPETYPE(pEntry);
    			info.comment	= PADSDATATYPECOMMENT(pEntry);
    			info.name	= main.name + "[]";
    			info.fullname	= main.fullname + "[]";
    			return TRUE;
    		}
    ...
    

    Bevor jetzt ein Lookup durchgeführt wird, muss nur zunächst geprüft werden, ob auf ein Arrayelement zugegriffen wird (name enthält Indexinformationen zwischen "[]"), dann muss dieser name entsprechend angepasst werden, indem die Indexinformationen gelöscht werden. Im Anschluss ggf. noch überprüfen, ob der Index innerhalb des Arrays liegt.



  • @camper

    Wenn ich den Code richtig lese, haben Arrayelemente nie selbst Subelemente.

    Also die haben schon Subelemente aber jeder Index halt die selben.
    Also ich kann ja ein Array mit Structs haben.

    Und die Information, die für Arrayelemente gespeichert wird, ist für alle Elemente identisch.

    Für das Oberste Element dann schon. Bis auf den IndexOffset der ist bei jedem Element anderes.

    Den muss ich dann halt noch berechnen. Also Offset von Array selbst + Arrayindex * Sizeof(Array Datatype)


  • Mod

    booster schrieb:

    Ich bekomme eine Liste von Variablennamen.
    z.B

    "Main.FunctionBlockComplex.FirstStruct.States[5].ErrorState"

    Zu dieser Variable muss ich nun die benötigten Informationen (Indexgroup, IndexOffest, Size, DataType usw.) einholen um dann später diese Variable direkt lesen bzw. schreiben zu können.

    Jetzt würde die Möglichkeit bestehen, nur die Variablendeklaration dieser einen Variablen auszulesen. Da es aber nicht nur eine Variable ist von der man die Informationen benötigt war die Idee alle Variablendeklarationen auf einmal auszulesen. (um nur einmal die SPS zu kontaktieren) Diese in einer Struktur zu speichern um schnellen Zugriff auf die Variablendeklartionen zu haben.
    Nach dem ich mir dann meine eigenen Klasseninstanzen der benötigten Variablen erzeugt habe kann ich diese "Lookup Tabelle" wieder löschen.

    Mir scheint dein Algorithmus arbeitet genau falschherum. Da du bereits eine vollständige Liste aller gesuchten Variablen hast, wäre es doch sinnvoller, beim Iterieren über die SPS-Daten direkt nur die Daten zu kopieren, die benötigt werden, anstatt erst eine umständliche Datenstruktur aller potentiell möglichen Variablen zu erstellen. Damit wird gar nicht erst unnötiger Speicher für Symbole verbraucht, die sowieso am Ende nicht benötigt werden, ausserdem kann der Parser von vornherein Subsymbole übergehen, die nicht benötigt werden.

    Die Aufgabe besteht also sinngemäß darin eine Funktion
    void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const vector<char>& sym_data, const vector<char>& dt_data)
    zu schreiben, wobei eingangs in vars die Namen der gesuchten Variablen bereits initialisiert sind, und die Funktion die restlichen Informationen der einzelnen vars-Elemente anhand der übergebenen Rohdaten aktualisiert?

    In dem Fall solltest du wahrscheinlich den Beispielcode wegwerfen und neuschreiben.
    Wenn du ein paar Beispieldaten zum runterladen und testen bereitstelltst, versuche ich mich evtl. auch selbst daran.


  • Mod

    Compiliert (g++/clang++ unter Linux, deshalb die paar Zeilen am Anfang) als C++17, aber nat. völlig ungetest:
    Fast keine dynamische Allokation (lediglich zwei kleine Vektoren und die string-Zuweisungen am Ende).

    #ifndef __MSC_VER
    #include <cstddef>
    using __int64 = long long;
    using BOOL = int;
    using PCHAR = char*;
    #define __stdcall
    #define __declspec(x)
    #endif
    #include "TcAdsDef.h" // https://raw.githubusercontent.com/bgstaal/ofxBeckhoffADS/master/include/TcAdsDef.h
    
    //////////////////////////////////////////////////////
    #include <algorithm>
    #include <cstddef>
    #include <cstdint>
    #include <cstring>
    #include <iostream>
    #include <string>
    #include <string_view>
    #include <stdexcept>
    #include <type_traits>
    #include <vector>
    using namespace std::literals::string_view_literals;
    
    enum class AdsDatatypeId;
    struct ITcAdsSymbol
    {
        std::string                           name;
        std::string                           shortName;
        std::string                           type;
        std::string                           comment;
        decltype(AdsSymbolEntry::size)        size;
        decltype(AdsSymbolEntry::iGroup)      indexGroup;
        decltype(AdsSymbolEntry::iOffs)       indexOffset;
        AdsDatatypeId                         datatype;       //enum
    };
    
    template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
    T read_raw(const char* p) {
        T res;
        std::memcpy(&res, p, sizeof res);
        return res;
    }
    
    std::size_t checked_sum(std::initializer_list<std::size_t> args) {
        std::size_t result = 0;
        for ( auto arg : args ) {
            auto sum = result + arg;
            if ( sum < result )
                throw std::runtime_error("overflow");
            result = sum;
        }
        return result;
    }
    
    class array_info {
        using size_type = decltype(AdsSymbolEntry::iOffs);
    public:
        array_info(const char* dim_data, size_type dims, size_type size) noexcept
        : dim_data(dim_data), dims(dims), size(size)
        {}
        array_info(const array_info&) noexcept = default;
        array_info(array_info&&) noexcept = default;
        array_info& operator=(const array_info&) noexcept = default;
        array_info& operator=(array_info&&) noexcept = default;
        ~array_info() noexcept = default;
        auto elem_info(std::string_view index) const {
            size_type real_index = 0;
            auto elem_size = size;
            for ( size_type i = 0; i != dims; ++i ) {
                auto pos = index.find(',');
                if ( ( i != dims - 1 ) == ( pos == index.npos ) )
                    throw std::runtime_error("index with wrong number of dimensions");
                auto cur_index = index;
                if ( pos != index.npos ) {
                    cur_index.remove_suffix(cur_index.size() - pos);
                    index.remove_prefix(pos + 1);
                }
                auto n = svtoi(cur_index);
                auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo));
                if ( n < info.lBound || n >= info.lBound + info.elements )
                    throw std::runtime_error("index out of range");
                real_index = real_index * info.elements + ( n - info.lBound );
                elem_size /= info.elements;
            }
            return std::pair(real_index * elem_size, elem_size);
        }
    private:
        static size_type svtoi(std::string_view s) {
            size_type result = 0;
            for ( ; !s.empty(); ) {
                if ( s[0] < '0' || s[0] > '9' )
                    throw std::runtime_error("index corrupted");
                result = result * 10 + ( s[0] - '0' );
                s.remove_prefix(1);
            }
            return result;
        }
        const char* dim_data;
        size_type dims;
        size_type size;
    };
    
    class adsDT {
    public:
        explicit adsDT(const char* raw_dt_data, bool check_size = true)
        : dt_data(raw_dt_data)
        {
            if ( check_size )
                check_item_size(this_len(dt_data), dt_data, 1);
            if ( PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0'
              || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0'
              || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0' )
                throw std::runtime_error("datatype corrupted: missing terminator");
            if ( strlen(name().data()) != name().length()
              || strlen(type().data()) != type().length()
              || strlen(comment().data()) != comment().length() )
                throw std::runtime_error("datatype corrupted: embedded \\0");
        }
        adsDT(const adsDT&) noexcept = default;
        adsDT(adsDT&&) noexcept = default;
        adsDT& operator=(const adsDT&) noexcept = default;
        adsDT& operator=(adsDT&&) noexcept = default;
        ~adsDT() noexcept = default;
        std::string_view name() const noexcept {
            return { PADSDATATYPENAME(dt_data), name_len(dt_data) };
        }
        std::string_view type() const noexcept {
            return { PADSDATATYPETYPE(dt_data), type_len(dt_data) };
        }
        std::string_view comment() const noexcept {
            return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) };
        }
        auto offset() const noexcept {
           return read_raw<decltype(AdsDatatypeEntry::offs)>(dt_data + offsetof(AdsDatatypeEntry, offs));
        }
        auto size() const noexcept {
           return read_raw<decltype(AdsDatatypeEntry::size)>(dt_data + offsetof(AdsDatatypeEntry, size));
        }
        static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name,
                [](const adsDT& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } );
            return it != data.end() && it->name() == name ? &*it : nullptr;
        }
        static std::size_t this_len(const char* data) noexcept {
             return read_raw<decltype(AdsDatatypeEntry::entryLength)>(data + offsetof(AdsDatatypeEntry, entryLength));
        }
        auto sub_symbols() const noexcept {
            return sub_items(dt_data);
        }
        const char* sub_symbol_data() const noexcept {
            return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data)
                           + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo);
        }
        array_info arr_info() const noexcept {
            return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data),
                array_dim(dt_data), size() };
        }
    private:
        static std::size_t name_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::nameLength)>(data + offsetof(AdsDatatypeEntry, nameLength));
        }
        static std::size_t type_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::typeLength)>(data + offsetof(AdsDatatypeEntry, typeLength));
        }
        static std::size_t comment_len(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::commentLength)>(data + offsetof(AdsDatatypeEntry, commentLength));
        }
        static std::size_t array_dim(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::arrayDim)>(data + offsetof(AdsDatatypeEntry, arrayDim));
        }
        static std::size_t sub_items(const char* data) noexcept {
            return read_raw<decltype(AdsDatatypeEntry::subItems)>(data + offsetof(AdsDatatypeEntry, subItems));
        }
        static void check_item_size(std::size_t limit, const char* data, std::size_t count) {
            for ( ; count--; ) {
                if ( this_len(data) < sizeof(AdsDatatypeEntry) + 3 )
                    throw std::runtime_error("datatype corrupted: entryLength (1)");
                if ( this_len(data) > limit )
                    throw std::runtime_error("datatype corrupted: entryLength (2)");
                if ( count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3 )
                    throw std::runtime_error("datatype corrupted: entryLength (3)");
                auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry), 3, name_len(data), type_len(data), comment_len(data) });
                if ( this_len(data) < arrayinfo_offset )
                    throw std::runtime_error("datatype corrupted: entryLength (4)");
                if ( ( this_len(data) - arrayinfo_offset ) / sizeof(AdsDatatypeArrayInfo) < array_dim(data) )
                    throw std::runtime_error("datatype corrupted: arrayDim");
                auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo);
                if ( this_len(data) < subitems_offset )
                    throw std::runtime_error("datatype corrupted: entryLength (5)");
                if ( ( this_len(data) - subitems_offset ) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data) )
                    throw std::runtime_error("datatype corrupted: subItems");
                check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data));
                limit -= this_len(data);
                data += this_len(data);
            }
            if ( limit != 0 )
                throw std::runtime_error("datatype corrupted: unused memory");
        }
        const char* dt_data;
    };
    
    std::vector<adsDT> parse_dt_data(const std::vector<char>& dt_data) {
        std::vector<adsDT> result;
        auto len = dt_data.size();
        if ( len == 0 )
            return result;
        std::size_t dt_count = 0;
        for ( auto p = &dt_data[0]; len != 0; ) {
            if ( len < sizeof(AdsDatatypeEntry) + 3)
                throw std::runtime_error("datatypes corrupted (1)");
            auto cur_len = adsDT::this_len(p);
            if ( cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len )
                throw std::runtime_error("datatypes corrupted (2)");
            ++dt_count;
            p += cur_len;
            len -= cur_len;
        }
        result.reserve(dt_count);
        for ( auto p = &dt_data[0]; dt_count--; ) {
            result.emplace_back(p);
            p += adsDT::this_len(p);
        }
        // should be sorted & unique
        if ( std::adjacent_find(result.begin(), result.end(),
                 [](const adsDT& lhs, const adsDT& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() )
            throw std::runtime_error("datatypes not sorted or not unique");
        return result;
    }
    
    class adsSym {
    public:
        explicit adsSym(const char* raw_sym_data)
        : sym_data(raw_sym_data)
        {
            check_item_size(sym_data);
            if ( PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0'
              || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0'
              || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0' )
                throw std::runtime_error("Symbol corrupted: missing terminator");
            if ( strlen(name().data()) != name().length()
              || strlen(type().data()) != type().length()
              || strlen(comment().data()) != comment().length() )
                throw std::runtime_error("Symbol corrupted: embedded \\0");
            if ( name().find_first_of(".["sv) != name().npos )
                throw std::runtime_error("Symbol name corrupted: embedded . or [");
        }
        adsSym(const adsSym&) noexcept = default;
        adsSym(adsSym&&) noexcept = default;
        adsSym& operator=(const adsSym&) noexcept = default;
        adsSym& operator=(adsSym&&) noexcept = default;
        ~adsSym() noexcept = default;
        std::string_view name() const noexcept {
            return { PADSSYMBOLNAME(sym_data), name_len(sym_data) };
        }
        std::string_view type() const noexcept {
            return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) };
        }
        std::string_view comment() const noexcept {
            return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) };
        }
        auto size() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::size)>(sym_data + offsetof(AdsSymbolEntry, size));
        }
        auto indexGroup() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::iGroup)>(sym_data + offsetof(AdsSymbolEntry, iGroup));
        }
        auto indexOffset() const noexcept {
            return read_raw<decltype(AdsSymbolEntry::iOffs)>(sym_data + offsetof(AdsSymbolEntry, iOffs));
        }
        static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name,
                [](const adsSym& lhs, const std::string_view& rhs) { return lhs.name() < rhs; } );
            return it != data.end() && it->name() == name ? &*it : nullptr;
        }
        static std::size_t this_len(const char* data) noexcept {
             return read_raw<decltype(AdsSymbolEntry::entryLength)>(data + offsetof(AdsSymbolEntry, entryLength));
        }
    private:
        static std::size_t name_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::nameLength)>(data + offsetof(AdsSymbolEntry, nameLength));
        }
        static std::size_t type_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::typeLength)>(data + offsetof(AdsSymbolEntry, typeLength));
        }
        static std::size_t comment_len(const char* data) noexcept {
            return read_raw<decltype(AdsSymbolEntry::commentLength)>(data + offsetof(AdsSymbolEntry, commentLength));
        }
        static void check_item_size(const char* data) {
            if ( this_len(data) < sizeof(AdsSymbolEntry) + 3 )
                throw std::runtime_error("Symbol corrupted (1)");
            if ( this_len(data) != checked_sum({ sizeof(AdsSymbolEntry), 3, name_len(data), type_len(data), comment_len(data) }) )
                throw std::runtime_error("Symbol corrupted (2)");
        }
        const char* sym_data;
    };
    
    std::vector<adsSym> parse_sym_data(const std::vector<char>& sym_data) {
        std::vector<adsSym> result;
        auto len = sym_data.size();
        if ( len == 0 )
            return result;
        std::size_t sym_count = 0;
        for ( auto p = &sym_data[0]; len != 0; ) {
            if ( len < sizeof(AdsSymbolEntry) + 3)
                throw std::runtime_error("symboldata corrupted (1)");
            auto cur_len = adsSym::this_len(p);
            if ( cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len )
                throw std::runtime_error("symboldata corrupted (2)");
            ++sym_count;
            p += cur_len;
            len -= cur_len;
        }
        result.reserve(sym_count);
        for ( auto p = &sym_data[0]; sym_count--; ) {
            result.emplace_back(p);
            p += adsSym::this_len(p);
        }
        // should be sorted & unique
        if ( std::adjacent_find(result.begin(), result.end(),
                 [](const adsSym& lhs, const adsSym& rhs) { return !(lhs.name() < rhs.name()); } ) != result.end() )
            throw std::runtime_error("symbols not sorted or not unique");
        return result;
    }
    
    template <typename T, typename std::enable_if_t<std::is_same_v<T, adsSym> || std::is_same_v<T, adsDT>, int> = 0>
    void update_var(ITcAdsSymbol& var, const T& data,
                    decltype(ITcAdsSymbol::indexGroup) group,
                    decltype(ITcAdsSymbol::indexOffset) offset,
                    decltype(AdsSymbolEntry::size) size) {
       	var.shortName = data.name();
       	var.type = data.type();
       	var.comment = data.comment();
       	var.size = size;
       	var.indexGroup = group;
       	var.indexOffset = offset;
       	//var.datatype =
    }
    
    void update_from_sps_data(std::vector<ITcAdsSymbol>& vars, const std::vector<char>& sym_data, const std::vector<char>& dt_data) {
        auto datatypes = parse_dt_data(dt_data);
        auto symbols = parse_sym_data(sym_data);
        for ( auto& var : vars ) {
            std::string_view cur_name = var.name;
            auto sym_name = cur_name;
            auto pos = sym_name.find_first_of(".[");
            if ( pos != sym_name.npos )
                sym_name.remove_suffix(sym_name.size() - pos);
            auto sym = adsSym::find_by_name(symbols, sym_name);
            if ( !sym )
                throw std::runtime_error("var not found");
            auto group = sym->indexGroup();
            auto offset = sym->indexOffset();
            if ( pos == cur_name.npos ) {
                update_var(var, *sym, group, offset, sym->size());
                continue;
            }
            auto type_name = sym->type();
            for ( ;; ) {
                auto dt = adsDT::find_by_name(datatypes, type_name);
                if ( !dt )
                    throw std::runtime_error("type not found");
                cur_name.remove_prefix(pos);
                if ( cur_name[0] == '.' ) {
                    cur_name.remove_prefix(1);
                    auto short_name = cur_name;
                    pos = short_name.find_first_of(".[");
                    if ( pos != short_name.npos ) {
                        short_name.remove_suffix(short_name.size() - pos);
                    }
                    // check all subsymbols in order
                    auto sub = dt->sub_symbol_data();
                    std::size_t n = 0;
                    for ( ; n != dt->sub_symbols(); ++n ) {
                        if ( adsDT(sub, false).name() == short_name )
                            break;
                    }
                    if ( n == dt->sub_symbols() )
                        throw std::runtime_error("subsymbol not found");
                    adsDT sub_item(sub, false);
                    offset += sub_item.offset();
                    if ( pos == short_name.npos ) {
                        update_var(var, sub_item, group, offset, sub_item.size());
                        break;
                    }
                    type_name = sub_item.type();
                } else {
                    // cur_name[0] == '['
                    cur_name.remove_prefix(1);
                    pos = cur_name.find("]");
                    if ( pos == cur_name.npos )
                        throw std::runtime_error("missing ]");
                    auto index = cur_name;
                    index.remove_suffix(index.size() - pos);
                    auto [elem_offset, elem_size] = dt->arr_info().elem_info(index);
                    offset += elem_offset;
                    cur_name.remove_prefix(pos + 1);
                    if ( cur_name.empty() ) {
                        update_var(var, *dt, group, offset, elem_size);
                        break;
                    }
                    if ( cur_name[0] != '[' && cur_name[0] != '.' )
                        throw std::runtime_error("missing . or [");
                    pos = 0;
                    type_name = dt->type();
                }
            }
        }
    }
    
    int main() {
        std::vector<ITcAdsSymbol> vars;
        std::vector<char> raw;
        try {
            update_from_sps_data(vars, raw, raw);
        } catch ( std::runtime_error& e ) {
            std::cout << e.what() << '\n';
            return 1;
        }
    }
    


  • @camper.

    Ok. Wollte noch vor deiner letzten Antwort sagen: "Wie soll das gehen".

    Da bin ich jetzt aber baff. Danke. Ich werde das sofort am Montag testen. Hatte heute Urlaub.

    Vielen Dank erst mal dafür. Muss mir das erst mal in Ruhe ansehen.


  • Mod

    Noch ein bisschen damit herumgespielt. Das scheint (zumindest) rudimentär zu funktionieren.
    adsVarData benutzt hier für strings bis auf den vollen Variablennamen jeweils string_view, d.h. die von der SPS geladenen Daten müssen im Speicher bleiben. Dafür werden unnötige Heapallokationen für strings vermieden. WTFPL gilt.

    90% des Codes ist leider langweilig, ausserdem versaut Visual C++ die Formatierung 😡
    adsData.h

    #pragma once
    
    #include <cstddef>
    #include <cstdint>
    #include <string>
    #include <string_view>
    #include <vector>
    
    #include <wtypes.h>
    #include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
    #include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsAPI.h"
    
    using namespace std::literals::string_view_literals;
    
    enum class adsDatatypeId : std::uint16_t
    {
        void_     = VT_EMPTY,
        int8_     = VT_I1,
        uint8_    = VT_UI1,
        int16_    = VT_I2,
        uint16_   = VT_UI2,
        int32_    = VT_I4,
        uint32_   = VT_UI4,
        int64_    = VT_I8,
        uint64_   = VT_UI8,
        float_    = VT_R4,
        double_   = VT_R8,
        cstring_  = VT_LPSTR,
        wcstring_ = VT_LPWSTR,
        ldouble_  = VT_LPWSTR + 1,
        bool_     = 33,
        bit_      = VT_LPWSTR + 2,
        big_      = VT_BLOB,
    };
    
    struct adsVarData
    {
        std::string                      name;
        std::string_view                 shortName;
        std::string_view                 typeName;
        std::string_view                 comment;
        decltype(AdsSymbolEntry::size)   size;
        decltype(AdsSymbolEntry::iGroup) group;
        decltype(AdsSymbolEntry::iOffs)  offset;
        adsDatatypeId                    type;
    };
    
    #include "adsData_impl.h"
    
    class adsData
    {
    public:
        explicit adsData();
        adsData(std::vector<char>&& sym_data, std::vector<char>&& dt_data);
    
        adsData(const adsData&)                = delete;
        adsData& operator=(const adsData&)     = delete;
    
        adsData(adsData&&)            noexcept = default;
        adsData& operator=(adsData&&) noexcept = default;
    
        ~adsData()                    noexcept = default;
    
        adsVarData operator[](std::string name) const;
    private:
        std::vector<char> sym_data;
        std::vector<char> dt_data;
        std::vector<adsData_impl::adsSym> symbols;
        std::vector<adsData_impl::adsDT> datatypes;
    };
    

    adsData_impl.h

    #pragma once
    
    #include <algorithm>
    #include <cstring>
    #include <string_view>
    #include <stdexcept>
    
    namespace adsData_impl {
    
        template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
        T read_raw(const char* p) noexcept {
            T res;
            std::memcpy(&res, p, sizeof res);
            return res;
        }
    #define READ_RAW(p, class, member) read_raw<decltype(class::member)>(p + offsetof(class, member))
    
        inline std::size_t checked_sum(std::initializer_list<std::size_t> args) {
            std::size_t result = 0;
            for (auto arg : args) {
                auto sum = result + arg;
                if (sum < result)
                    throw std::overflow_error("data corrupted: length");
                result = sum;
            }
            return result;
        }
    
        class array_info {
            using size_type = decltype(AdsSymbolEntry::iOffs);
        public:
            array_info(const char* dim_data, std::size_t dims, std::size_t size) noexcept
                : dim_data(dim_data), dims(dims), size(size)
            {}
            array_info(const array_info&) noexcept = default;
            array_info(array_info&&) noexcept = default;
            array_info& operator=(const array_info&) noexcept = default;
            array_info& operator=(array_info&&) noexcept = default;
            ~array_info() noexcept = default;
            auto elem_info(std::string_view index) const {
                size_type real_index = 0;
                auto elem_size = size;
                for (size_type i = 0; i != dims; ++i) {
                    auto pos = index.find(',');
                    if ((i != dims - 1) == (pos == index.npos))
                        throw std::out_of_range("index with wrong number of dimensions");
                    auto cur_index = index;
                    if (pos != index.npos) {
                        cur_index.remove_suffix(cur_index.size() - pos);
                        index.remove_prefix(pos + 1);
                    }
                    auto n = svtoi(cur_index);
                    auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo));
                    if (n < info.lBound || n - info.lBound >= info.elements)
                        throw std::out_of_range("index out of range");
                    real_index = real_index * info.elements + (n - info.lBound);
                    if (elem_size % info.elements != 0)
                        throw std::runtime_error("array info corrupted");
                    elem_size /= info.elements;
                }
                return std::make_pair(real_index * elem_size, elem_size);
            }
        private:
            static size_type svtoi(std::string_view s) {
                size_type result = 0;
                if (s.empty())
                    throw std::out_of_range("index corrupted");
                for (; !s.empty(); ) {
                    if (s[0] < '0' || s[0] > '9')
                        throw std::out_of_range("index corrupted");
                    result = result * 10 + (s[0] - '0');
                    s.remove_prefix(1);
                }
                return result;
            }
            const char* dim_data;
            size_type dims;
            size_type size;
        };
    
        class adsDT
        {
        public:
            explicit adsDT(const char* raw_dt_data, bool check_size = true)
                : dt_data{ raw_dt_data }
            {
                if (check_size)
                    check_item_size(this_len(dt_data), dt_data, 1);
                if (PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0'
                    || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0'
                    || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0')
                    throw std::runtime_error("datatype corrupted: missing terminator");
                if (strlen(name().data()) != name().length()
                    || strlen(type_name().data()) != type_name().length()
                    || strlen(comment().data()) != comment().length())
                    throw std::runtime_error("datatype corrupted: embedded \\0");
            }
            adsDT(const adsDT&) noexcept = default;
            adsDT(adsDT&&) noexcept = default;
            adsDT& operator=(const adsDT&) noexcept = default;
            adsDT& operator=(adsDT&&) noexcept = default;
            ~adsDT() noexcept = default;
            std::string_view name()      const noexcept { return { PADSDATATYPENAME(dt_data), name_len(dt_data) }; }
            std::string_view type_name() const noexcept { return { PADSDATATYPETYPE(dt_data), type_len(dt_data) }; }
            std::string_view comment()   const noexcept { return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) }; }
            auto offset()  const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, offs); }
            auto size()    const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, size); }
            auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(dt_data, AdsDatatypeEntry, dataType)); }
            array_info arr_info() const noexcept
            {
                return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data),
                    array_dim(dt_data), size() };
            }
            auto sub_symbols() const noexcept { return sub_items(dt_data); }
            const char* sub_symbol_data() const noexcept
            {
                return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data)
                    + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo);
            }
    
            static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept
            {
                auto it = std::lower_bound(data.begin(), data.end(), name,
                    [](const adsDT& lhs, const std::string_view& rhs)
                {
                    return lhs.name() < rhs;
                });
                return it != data.end() && it->name() == name ? &*it : nullptr;
            }
    
            static std::size_t this_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, entryLength); }
        private:
            static std::size_t name_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, nameLength); }
            static std::size_t type_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, typeLength); }
            static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, commentLength); }
            static std::size_t array_dim(const char* p)   noexcept { return READ_RAW(p, AdsDatatypeEntry, arrayDim); }
            static std::size_t sub_items(const char* p)   noexcept { return READ_RAW(p, AdsDatatypeEntry, subItems); }
            static void check_item_size(std::size_t limit, const char* data, std::size_t count)
            {
                for (; count--; )
                {
                    if (this_len(data) < sizeof(AdsDatatypeEntry) + 3)
                        throw std::runtime_error("datatype corrupted: entryLength (1)");
                    if (this_len(data) > limit)
                        throw std::runtime_error("datatype corrupted: entryLength (2)");
                    if (count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3)
                        throw std::runtime_error("datatype corrupted: entryLength (3)");
                    auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry) + 3,
                        name_len(data), type_len(data), comment_len(data) });
                    if (this_len(data) < arrayinfo_offset)
                        throw std::runtime_error("datatype corrupted: entryLength (4)");
                    if ((this_len(data) - arrayinfo_offset) / sizeof(AdsDatatypeArrayInfo) < array_dim(data))
                        throw std::runtime_error("datatype corrupted: arrayDim");
                    auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo);
                    if ((this_len(data) - subitems_offset) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data))
                        throw std::runtime_error("datatype corrupted: subItems");
                    check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data));
                    limit -= this_len(data);
                    data += this_len(data);
                }
                if (limit != 0)
                    throw std::runtime_error("datatype corrupted: unused memory");
            }
            const char* dt_data;
        };
    
        class adsSym {
        public:
            explicit adsSym(const char* raw_sym_data)
                : sym_data{ raw_sym_data }
            {
                check_item_size(sym_data);
                if (PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0'
                    || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0'
                    || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0')
                    throw std::runtime_error("Symbol corrupted: missing terminator");
                if (strlen(name().data()) != name().length()
                    || strlen(type_name().data()) != type_name().length()
                    || strlen(comment().data()) != comment().length())
                    throw std::runtime_error("Symbol corrupted: embedded \\0");
                if (name().find('[') != name().npos)
                    throw std::runtime_error("Symbol name must not contain '['");
            }
            adsSym(const adsSym&) noexcept = default;
            adsSym(adsSym&&) noexcept = default;
            adsSym& operator=(const adsSym&) noexcept = default;
            adsSym& operator=(adsSym&&) noexcept = default;
            ~adsSym() noexcept = default;
            std::string_view name()      const noexcept { return { PADSSYMBOLNAME(sym_data), name_len(sym_data) }; }
            std::string_view type_name() const noexcept { return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) }; }
            std::string_view comment()   const noexcept { return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) }; }
            auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(sym_data, AdsSymbolEntry, dataType)); }
            auto size()    const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, size); }
            auto group()   const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iGroup); }
            auto offset()  const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iOffs); }
    
            static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept {
                // TODO: find something less ugly
                static_assert('.' < 'A' && '.' < '0');
                auto it = std::upper_bound(data.begin(), data.end(), name,
                    [](const std::string_view& lhs, const adsSym& rhs) noexcept { return lhs < rhs.name(); });
                return it != data.begin() && (--it)->name().size() <= name.size() &&
                    it->name() == std::string_view{ name.data(), it->name().size() } ? &*it : nullptr;
            }
    
            static std::size_t this_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, entryLength); }
        private:
            static std::size_t name_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, nameLength); }
            static std::size_t type_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, typeLength); }
            static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, commentLength); }
            static void check_item_size(const char* data) {
                if (this_len(data) < sizeof(AdsSymbolEntry) + 3)
                    throw std::runtime_error("Symbol corrupted (1)");
                if (this_len(data) != checked_sum({ sizeof(AdsSymbolEntry) + 3,
                    name_len(data), type_len(data), comment_len(data) }))
                    throw std::runtime_error("Symbol corrupted (2)");
            }
            const char* sym_data;
        };
    
    }
    

    adsData.cpp

    #include <iostream>
    #include <memory>
    #include <stdexcept>
    #include <string>
    #include <string_view>
    #include <type_traits>
    #include <vector>
    
    #include "adsData.h"
    
    namespace {
    
        std::vector<adsData_impl::adsDT> parse_dt_data(const std::vector<char>& dt_data) {
            std::vector<adsData_impl::adsDT> result;
            auto len = dt_data.size();
            if (len == 0)
                return result;
            std::size_t dt_count = 0;
            for (auto p = &dt_data[0]; len != 0; ) {
                if (len < sizeof(AdsDatatypeEntry) + 3)
                    throw std::runtime_error("datatypes corrupted (1)");
                auto cur_len = adsData_impl::adsDT::this_len(p);
                if (cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len)
                    throw std::runtime_error("datatypes corrupted (2)");
                ++dt_count;
                p += cur_len;
                len -= cur_len;
            }
            result.reserve(dt_count);
            for (auto p = &dt_data[0]; dt_count--; ) {
                result.emplace_back(p);
                p += adsData_impl::adsDT::this_len(p);
            }
            std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return lhs.name() < rhs.name(); });
            if (std::adjacent_find(result.begin(), result.end(),
                [](auto& lhs, auto& rhs) noexcept { return !(lhs.name() < rhs.name()); }) != result.end())
                throw std::runtime_error("datatypes not unique");
            return result;
        }
    
        std::vector<adsData_impl::adsSym> parse_sym_data(const std::vector<char>& sym_data) {
            std::vector<adsData_impl::adsSym> result;
            auto len = sym_data.size();
            if (len == 0)
                return result;
            std::size_t sym_count = 0;
            for (auto p = &sym_data[0]; len != 0; ) {
                if (len < sizeof(AdsSymbolEntry) + 3)
                    throw std::runtime_error("symboldata corrupted (1)");
                auto cur_len = adsData_impl::adsSym::this_len(p);
                if (cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len)
                    throw std::runtime_error("symboldata corrupted (2)");
                ++sym_count;
                p += cur_len;
                len -= cur_len;
            }
            result.reserve(sym_count);
            for (auto p = &sym_data[0]; sym_count--; ) {
                result.emplace_back(p);
                p += adsData_impl::adsSym::this_len(p);
            }
            std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) noexcept { return lhs.name() < rhs.name(); });
            if (std::adjacent_find(result.begin(), result.end(),
                [](auto& lhs, auto& rhs) noexcept { return !(lhs.name() < rhs.name()); }) != result.end())
                throw std::runtime_error("symbols not unique");
            return result;
        }
    
        template <typename T,
                  typename std::enable_if_t<std::is_same_v<T, adsData_impl::adsDT> || std::is_same_v<T, adsData_impl::adsSym>, int> = 0>
        adsVarData get_var(std::string&& name, const T& data,
            decltype(adsVarData::group) group,
            decltype(adsVarData::offset) offset,
            decltype(adsVarData::size) size) noexcept {
            return { std::move(name), data.name(), data.type_name(), data.comment(), size, group, offset, data.type_id() };
        }
    
        std::string to_string_hex(std::uint64_t val) {
            char buf[2*sizeof(val) + 1];
            std::snprintf(buf, sizeof(buf), "%llx", val);
            return buf;
        }
    } // namespace
    
    adsData::adsData()
        : sym_data{}, dt_data{}, symbols{}, datatypes{}
    {
        auto ads_port = AdsPortOpenEx();
        if (ads_port == 0)
            throw std::runtime_error("cannot open ads port");
        {
            std::unique_ptr<decltype(ads_port), void(*)(decltype(ads_port)*)> guard(&ads_port,
                [](decltype(ads_port)* p) { AdsPortCloseEx(*p); });
            AmsAddr	ams_addr{};
            if (auto err = AdsGetLocalAddressEx(ads_port, &ams_addr); err != ADSERR_NOERR)
                throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err));
            ams_addr.port = AMSPORT_R0_PLC_RTS1;
            AdsSymbolUploadInfo2 info{};
            unsigned long bytes_read = 0;
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info),
                &info, &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read symbol upload info: 0x" + to_string_hex(err));
            if (bytes_read != sizeof(info))
                throw std::runtime_error("error reading sym_uploadinfo2: " + std::to_string(bytes_read) + " bytes read");
            sym_data.resize(info.nSymSize);
            dt_data.resize(info.nDatatypeSize);
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize,
                &sym_data[0], &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read symbol info: 0x" + to_string_hex(err));
            if (bytes_read != info.nSymSize)
                throw std::runtime_error("error reading symbols: " + std::to_string(bytes_read) + " bytes read, "
                + std::to_string(info.nSymSize) + " expected");
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize,
                &dt_data[0], &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read datatype info: 0x" + to_string_hex(err));
            if (bytes_read != info.nDatatypeSize)
                throw std::runtime_error("error reading datatypes: " + std::to_string(bytes_read) + " bytes read, "
                    + std::to_string(info.nDatatypeSize) + " expected");
        }
        symbols = parse_sym_data(sym_data);
        datatypes = parse_dt_data(dt_data);
    }
    
    adsData::adsData(std::vector<char>&& sym_data_, std::vector<char>&& dt_data_)
        : sym_data{ std::move(sym_data_) }, dt_data{ std::move(dt_data_) },
        symbols{ parse_sym_data(sym_data) }, datatypes{ parse_dt_data(dt_data) }
    {}
    
    adsVarData adsData::operator[](std::string name) const {
        std::string_view cur_name = name;
        if (auto pos = cur_name.find('['); pos != cur_name.npos)
            cur_name.remove_suffix(cur_name.size() - pos);
        auto sym = adsData_impl::adsSym::find_by_name(symbols, cur_name);
        if (!sym)
            throw std::out_of_range("var not found: " + name);
        auto group = sym->group();
        auto offset = sym->offset();
        if (name.size() == sym->name().size())
            return get_var(std::move(name), *sym, group, offset, sym->size());
        cur_name = std::string_view{ name.data() + sym->name().size(), name.size() - sym->name().size() };
        if (cur_name[0] != '.' && cur_name[0] != '[' )
            throw std::out_of_range("var not found: " + name);
        std::size_t pos = 0;
        auto type_name = sym->type_name();
        for (;;) {
            auto dt = adsData_impl::adsDT::find_by_name(datatypes, type_name);
            if (!dt)
                throw std::out_of_range("type not found");
            cur_name.remove_prefix(pos);
            if (cur_name[0] == '.') {
                cur_name.remove_prefix(1);
                auto short_name = cur_name;
                pos = short_name.find_first_of(".["sv);
                if (pos != short_name.npos) {
                    short_name.remove_suffix(short_name.size() - pos);
                }
                // check all subsymbols in order
                auto sub = dt->sub_symbol_data();
                std::size_t n = 0;
                for (; n != dt->sub_symbols(); ++n) {
                    if (adsData_impl::adsDT(sub, false).name() == short_name)
                        break;
                    sub += adsData_impl::adsDT::this_len(sub);
                }
                if (n == dt->sub_symbols())
                    throw std::out_of_range("subsymbol not found");
                adsData_impl::adsDT sub_item(sub, false);
                offset += sub_item.offset();
                if (pos == short_name.npos)
                    return get_var(std::move(name), sub_item, group, offset, sub_item.size());
                type_name = sub_item.type_name();
            }
            else {
                // cur_name[0] == '['
                cur_name.remove_prefix(1);
                pos = cur_name.find(']');
                if (pos == cur_name.npos)
                    throw std::out_of_range("missing ]");
                auto index = cur_name;
                index.remove_suffix(index.size() - pos);
                auto[elem_offset, elem_size] = dt->arr_info().elem_info(index);
                offset += elem_offset;
                cur_name.remove_prefix(pos + 1);
                if (cur_name.empty())
                    return get_var(std::move(name), *dt, group, offset, elem_size);
                if (cur_name[0] != '[' && cur_name[0] != '.')
                    throw std::out_of_range("missing . or [");
                pos = 0;
                type_name = dt->type_name();
            }
        }
    }
    

    main.cpp

    #include <algorithm>
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <utility>
    #include "adsData.h"
    
    std::ostream& operator<<(std::ostream& s, adsDatatypeId val)
    {
        static constexpr std::pair<adsDatatypeId, std::string_view> typenames[] =
        {
            { adsDatatypeId::void_,		"void"sv, },
            { adsDatatypeId::int16_,    "int16"sv },
            { adsDatatypeId::int32_,    "int32"sv },
            { adsDatatypeId::float_,    "float"sv },
            { adsDatatypeId::double_,   "double"sv },
            { adsDatatypeId::int8_,     "int8"sv },
            { adsDatatypeId::uint8_,    "uint8"sv },
            { adsDatatypeId::uint16_,   "uint16"sv },
            { adsDatatypeId::uint32_,   "uint32"sv },
            { adsDatatypeId::int64_,    "int64"sv },
            { adsDatatypeId::uint64_,   "uint64"sv },
            { adsDatatypeId::cstring_,  "cstring"sv },
            { adsDatatypeId::wcstring_, "wcstring"sv },
            { adsDatatypeId::bool_,     "bool"sv },
            { adsDatatypeId::big_,      "blob"sv },
        };
        auto it = std::lower_bound(std::begin(typenames), std::end(typenames), val,
            [](auto& x, auto& y) { return x.first < y; });
        if (it == std::end(typenames) || it->first != val)
            s << "unknown(" << static_cast<int>(val) << ')';
        else
            s << it->second << '(' << static_cast<int>(val) << ')';
        return s;
    }
    
    std::ostream& operator << (std::ostream& s, const adsVarData& var)
    {
        s   << "name: " << var.name << '\n'
            << "short: " << var.shortName << '\n'
            << "typename: " << var.typeName << '\n'
            << "comment: " << var.comment << '\n'
            << "type: " << var.type << '\n'
            << "group: " << var.group << '\n'
            << "offset: " << var.offset << '\n'
            << "size: " << var.size << '\n';
        return s;
    }
    
    int main() {
        std::string var_names[] = {
            "MAIN.X",
            "MAIN.X.X",
            "MAIN.X.Y",
            "MAIN.X.X.INT8",
            "MAIN.X.X.INT16",
            "MAIN.X.X.INT32",
            "MAIN.X.X.UINT8",
            "MAIN.X.X.UINT16",
            "MAIN.X.X.UINT32",
            "MAIN.X.X.CSTRING",
            "MAIN.X.X.BOOL_",
            "MAIN.X.X.FLOAT",
            "MAIN.X.X.DOUBLE",
            "MAIN.X.Y",
            "MAIN.X.Y[0]",
            "MAIN.X.Y[15]",
            "MAIN.X.Y[15].BOOL_",
            "MAIN.X.Z",
            "MAIN.X.Z[4,150]",
            "MAIN.X.Z[4,150].INT8",
            "MAIN.A",
            "MAIN.AA",
            "MAIN.A[5]"
        };
        try {
            adsData plc_data{};
            for (auto& name : var_names)
                try {
                    std::cout << plc_data[std::move(name)] << '\n';
                }
                catch (std::out_of_range& e) {
                    std::cout << e.what() << '\n';
                }
        }
        catch (std::runtime_error& e) {
            std::cout << e.what() << '\n';
            return 1;
        }
    }
    

  • Mod

    Worum geht's hier noch mal?



  • @camper.

    Vielen Dank für deine Hilfe.
    Muss noch einiges lernen :-):

    - string_view kannte ich noch nicht
    - namespace ohne name kannte ich auch noch nicht.

    Und dann noch ne Frage:
    Verstehe noch nicht ganz die Trennung von .h .cpp und _impl.h:
    Bei mir war header immer definition und cpp implementierung. Was mach man in _impl.h genau? Und dann auch gleich nen eigenen namespace namespace adsData_impl. Kannte ich so noch nicht.


  • Mod

    boster schrieb:

    Verstehe noch nicht ganz die Trennung von .h .cpp und _impl.h:
    Bei mir war header immer definition und cpp implementierung. Was mach man in _impl.h genau? Und dann auch gleich nen eigenen namespace namespace adsData_impl. Kannte ich so noch nicht.

    Das Ganze dient dazu, den Header, den der Anwender direkt inkludiert (und wo du im Zweifel als erstes nachschaust, was du überhaupt mit dem ganzen Zeug machen kannst) nicht mit Implementierungsdetails überfrachtet wird.
    adsData enthält aber private Daten (vector<adsData> etc.), bei denen der Compiler zumindest die entsprechende Typinformation schon gesehen haben muss (dafür ist der _impl.h Header da, manchmal gibt man solchen Dateien auch andere Endungen, etwa .impl - wenn mit Bibliotheken gearbeitet wird, die sehr viel Templatecode enthalten, sieht man das öfter).
    Weil es aber Implementationsdetails sind, gibt es keinen Grund, dafür den globalen Namensraum zu verwenden.
    Eine mögliche Alternative wäre das pimpl-Idiom.

    Btw. adsSym::find_by_name ist mit ziemlicher Sicherheit fehlerhaft.



  • nur noch mein Abschluss-Senf

    1. niemals die Arrays/Structs im Speicher expandieren - es reicht wenn deine Verwaltung versteht das es ein Array ist und bei einer Abfrage die richtigen Daten liefert

    d.h. sorgen nicht dafür das im Ergebnis alles elementare Typen sind sondern das deine Zugriffsfunktionen dir solche liefern - also unter der Haube das Array of Struct of 100.000 zerlegen

    spätestens wenn du alle Array-Knoten in einem GUI-Tree anzeigen muss merkst du das es nicht funktioniert und die Partitionierungs/Zugriffslogik die du dann dafür baust ist genau das was du unten schon machen musst

    mir ist klar das das immer einfach ist es direkt in nur-elementare umzuwandeln - kostet aber tierisch viel Speicher

    2. wenn du von jedem Item einen Vollpfad bekommst macht es wohl sinn diesen nicht immer direkt so zu speichern - lieber einen echten Baum + Hashwert oder so was

    3. wenn es wirklich super viel Eingangsdaten sind kann es auch sinn machen diese auf der Platte in einer RocksDB/Sqlite oder sonstwas kleines zu speichern und darüber dann ein iteratives durchlaufen anzubieten



  • @camper

    Ok. Noch ne andere Frage. Du hast die Funktion AdsSyncReadReq verwendet und nicht die AdsSyncReadReqEx2 wie im genannten Beispiel.

    Hierzu hat Beckhoff ja auch ein Beispiel.
    Anscheinend ist das Beispiel mit AdsSyncReadReq die statische Variante
    und das Beispiel mit AdsSyncReadReqEx2 die dynamische Variante, mit dem Kommentar:

    "This sample describes how to upload the PLC symbol information in a more efficient dynamic way. "

    Gab es einen Grund für dich die Funktion "AdsSyncReadReq" zu verwenden?

    Und was ich auch noch nicht verstanden habe, wie du auf die Allokation der beiden Byte arrays verzichten konntest.


  • Mod

    booster schrieb:

    Noch ne andere Frage. Du hast die Funktion AdsSyncReadReq verwendet und nicht die AdsSyncReadReqEx2 wie im genannten Beispiel.

    Welches Beispiel nochmal? Das hier verwendet AdsSyncReadReq. Ansonsten: kein anderer Grund als das ich das so vorgefunden habe.

    booster schrieb:

    Und was ich auch noch nicht verstanden habe, wie du auf die Allokation der beiden Byte arrays verzichten konntest.

    Konnte ich nicht. Dafür ist resize da.



  • Stimmt du hast recht.
    Die AdsSyncReadReq oder AdsSyncReadReqEx2 sind nicht abhängig davon ob statisch oder dynamisch. Hat andere Gründe

    Hier trotzdem die beiden Besipiele.

    das ist das "statische" Beipiel:
    https://infosys.beckhoff.de/content/1031/tcsample_vc/html/tcadsdll_api_cpp_sample10.htm?id=3746003215985854275

    und das hier das "dynamische"
    https://infosys.beckhoff.de/content/1031/tcsample_vc/html/tcadsdll_api_cpp_sample16.htm?id=3653485731247715096


  • Mod

    Habe den Code noch mal ein wenig korrigiert. Symbollookup sollte jetzt in der Regel richtig funktionieren - ohne formale Spezifikation ist das nat. schwer zu sagen (das Ausgangsproblem ist, dass Symbolnamen auch '.' enthalten können).

    Beispielprogramm dazu:

    TYPE foo :
    STRUCT
        int8 : SINT;
        int16 : INT;
        int32 : DINT;
        uint8 : USINT;
        uint16 : UINT;
        uint32 : UDINT;
        float : REAL;
        double : LREAL;
        cstring : STRING;
        bool_ : BOOL;
    END_STRUCT
    END_TYPE
    TYPE bar :
    STRUCT
        x : foo;
        y : ARRAY [10..20] OF foo;
       z : ARRAY [1..5,100..200] OF foo;
    END_STRUCT
    END_TYPE
    PROGRAM MAIN
    VAR
        x:bar;
        A : ARRAY [0..9] OF FOO;
        AA : FOO;
    END_VAR
    


  • Hi camper.

    Hast du das mit TwinCat 3.1.x oder mit 2.x getestet?
    Der gelieferte Code von Beckhoff hatte noch einen Fehler für die Suche der Symbole bei TwinCat 3.1.

    In TwinCat 3.1 werden die Variablen per ADS so geliefert wie sie in der PLC programmiert sind (üblicherweise CamelCase). In TwinCat 2 werden alle Variablen in uppercase geliefert egal wie sie in der PLC geschrieben sind.

    Desweiteren werden die Variablen beginnend mit Underline bzw. Punkt an in TwinCat2.1 am Anfang der Liste gestellt und in TwinCat 3 ans Ende. Wenn ich micht richtig erinnere.

    Die Funktion GetTypeByName mit CompareDTByName in AdsParseSymbols such ja mit einem binary search und strcmp. Da werden die Symbole in TwinCat 3 nicht immer gefunden.

    Du hast die Klasse AdsParseSymbols und auch gar kein strcmp verwendet.



  • string_view ist das einzige in deinem Code das erst in c++ 17 kam wenn ich das so richtig überblicke?

    Gibts da eine alternative für den string_view. Ich habe das Problem, dass wir unserem Projekt gettext für die Sprachumschaltung von boost nutzen.

    Selbst in der neusten boost wird aber noch auto_ptr verwendet das in c++ 17 nicht mehr zur Verfügung steht. Das heißt ich kann nicht mit c++ 17 kompilieren. 😞


  • Mod

    Mit 2.11 getestet.

    booster schrieb:

    In TwinCat 3.1 werden die Variablen per ADS so geliefert wie sie in der PLC programmiert sind (üblicherweise CamelCase). In TwinCat 2 werden alle Variablen in uppercase geliefert egal wie sie in der PLC geschrieben sind.

    Also Vergleich ohne Beachtung von Groß-/Kleinschreibung. Geringfügig nervig, aber nicht schwer zu implementieren. Oder man fragt die Variablen gleich mit der richtigen Schreibweise ab. Ich weiss ja nicht, ob Groß-/Kleinschreibung systembedingt unbeachtlich sein soll, oder ob das nur ein zufälliges Implementationsartekt ist.

    booster schrieb:

    Desweiteren werden die Variablen beginnend mit Underline bzw. Punkt an in TwinCat2.1 am Anfang der Liste gestellt und in TwinCat 3 ans Ende. Wenn ich micht richtig erinnere.

    Das sollte keine Rolle spielen, da die Liste sowieso nochmal sortiert wird.

    booster schrieb:

    string_view ist das einzige in deinem Code das erst in c++ 17 kam wenn ich das so richtig überblicke?

    Gibts da eine alternative für den string_view. Ich habe das Problem, dass wir unserem Projekt gettext für die Sprachumschaltung von boost nutzen.

    Selbst in der neusten boost wird aber noch auto_ptr verwendet das in c++ 17 nicht mehr zur Verfügung steht. Das heißt ich kann nicht mit c++ 17 kompilieren. 😞

    string_view und ein paar Kleinigkeiten für die Bequemlichkeit (structured bindings, Variablendefinition in if, mit folgender Bedingung), die trivial in C++14 ausgedrückt werden können.

    #define BOOST_CONFIG_SUPPRESS_OUTDATED_MESSAGE
    #define _HAS_AUTO_PTR_ETC 1
    

    oder entsprechende Compileroptionen sollte das auto_ptr Problem lösen, bis boost gefixt ist.

    Man kann sich nat. auch ein string_view selbst schreiben, das ist ein ziemlich einfache Klasse.


  • Mod

    Version mit Stringvergleich ohne Beachtung der Groß-/Kleinschreibung.

    adsData_impl.h

    #pragma once
    
    #include <algorithm>
    #include <cctype>
    #include <cstring>
    #include <string_view>
    #include <stdexcept>
    
    namespace adsData_impl {
    
        template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
        T read_raw(const char* p) noexcept {
            T res;
            std::memcpy(&res, p, sizeof res);
            return res;
        }
    #define READ_RAW(p, class, member) read_raw<decltype(class::member)>(p + offsetof(class, member))
    
        inline std::size_t checked_sum(std::initializer_list<std::size_t> args) {
            std::size_t result = 0;
            for (auto arg : args) {
                auto sum = result + arg;
                if (sum < result)
                    throw std::overflow_error("data corrupted: length");
                result = sum;
            }
            return result;
        }
    
        class array_info {
            using size_type = decltype(AdsSymbolEntry::iOffs);
        public:
            array_info(const char* dim_data, std::size_t dims, std::size_t size) noexcept
                : dim_data(dim_data), dims(dims), size(size)
            {}
            array_info(const array_info&) noexcept = default;
            array_info(array_info&&) noexcept = default;
            array_info& operator=(const array_info&) noexcept = default;
            array_info& operator=(array_info&&) noexcept = default;
            ~array_info() noexcept = default;
            auto elem_info(std::string_view index) const {
                size_type real_index = 0;
                auto elem_size = size;
                for (size_type i = 0; i != dims; ++i) {
                    auto pos = index.find(',');
                    if ((i != dims - 1) == (pos == index.npos))
                        throw std::out_of_range("index with wrong number of dimensions");
                    auto cur_index = index;
                    if (pos != index.npos) {
                        cur_index.remove_suffix(cur_index.size() - pos);
                        index.remove_prefix(pos + 1);
                    }
                    auto n = svtoi(cur_index);
                    auto info = read_raw<AdsDatatypeArrayInfo>(dim_data + i * sizeof(AdsDatatypeArrayInfo));
                    if (n < info.lBound || n - info.lBound >= info.elements)
                        throw std::out_of_range("index out of range");
                    real_index = real_index * info.elements + (n - info.lBound);
                    if (elem_size % info.elements != 0)
                        throw std::runtime_error("array info corrupted");
                    elem_size /= info.elements;
                }
                return std::make_pair(real_index * elem_size, elem_size);
            }
        private:
            static size_type svtoi(std::string_view s) {
                size_type result = 0;
                if (s.empty())
                    throw std::out_of_range("index corrupted");
                for (; !s.empty(); ) {
                    if (s[0] < '0' || s[0] > '9')
                        throw std::out_of_range("index corrupted");
                    result = result * 10 + (s[0] - '0');
                    s.remove_prefix(1);
                }
                return result;
            }
            const char* dim_data;
            size_type dims;
            size_type size;
        };
    
        inline bool icmp_less(const std::string_view& lhs, const std::string_view& rhs)
        {
            return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
                [](auto x, auto y) { return std::tolower(x) < std::tolower(y); });
        }
        inline bool icmp_equal(const std::string_view& lhs, const std::string_view& rhs)
        {
            return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
                [](auto x, auto y) { return std::tolower(x) == std::tolower(y); });
        }
    
        class adsDT
        {
        public:
            explicit adsDT(const char* raw_dt_data, bool check_size = true)
                : dt_data{ raw_dt_data }
            {
                if (check_size)
                    check_item_size(this_len(dt_data), dt_data, 1);
                if (PADSDATATYPENAME(dt_data)[name_len(dt_data)] != '\0'
                    || PADSDATATYPETYPE(dt_data)[type_len(dt_data)] != '\0'
                    || PADSDATATYPECOMMENT(dt_data)[comment_len(dt_data)] != '\0')
                    throw std::runtime_error("datatype corrupted: missing terminator");
                if (strlen(name().data()) != name().length()
                    || strlen(type_name().data()) != type_name().length()
                    || strlen(comment().data()) != comment().length())
                    throw std::runtime_error("datatype corrupted: embedded \\0");
            }
            adsDT(const adsDT&) noexcept = default;
            adsDT(adsDT&&) noexcept = default;
            adsDT& operator=(const adsDT&) noexcept = default;
            adsDT& operator=(adsDT&&) noexcept = default;
            ~adsDT() noexcept = default;
            std::string_view name()      const noexcept { return { PADSDATATYPENAME(dt_data), name_len(dt_data) }; }
            std::string_view type_name() const noexcept { return { PADSDATATYPETYPE(dt_data), type_len(dt_data) }; }
            std::string_view comment()   const noexcept { return { PADSDATATYPECOMMENT(dt_data), comment_len(dt_data) }; }
            auto offset()  const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, offs); }
            auto size()    const noexcept { return READ_RAW(dt_data, AdsDatatypeEntry, size); }
            auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(dt_data, AdsDatatypeEntry, dataType)); }
            array_info arr_info() const noexcept
            {
                return { dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data),
                    array_dim(dt_data), size() };
            }
            auto sub_symbols() const noexcept { return sub_items(dt_data); }
            const char* sub_symbol_data() const noexcept
            {
                return dt_data + sizeof(AdsDatatypeEntry) + 3 + name_len(dt_data) + type_len(dt_data) + comment_len(dt_data)
                    + array_dim(dt_data) * sizeof(AdsDatatypeArrayInfo);
            }
    
            static const adsDT* find_by_name(const std::vector<adsDT>& data, const std::string_view& name) noexcept
            {
                auto it = std::lower_bound(data.begin(), data.end(), name,
                    [](const adsDT& lhs, const std::string_view& rhs)
                {
                    return icmp_less(lhs.name(), rhs);
                });
                return it != data.end() && icmp_equal(it->name(), name) ? &*it : nullptr;
            }
    
            static std::size_t this_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, entryLength); }
        private:
            static std::size_t name_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, nameLength); }
            static std::size_t type_len(const char* p)    noexcept { return READ_RAW(p, AdsDatatypeEntry, typeLength); }
            static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsDatatypeEntry, commentLength); }
            static std::size_t array_dim(const char* p)   noexcept { return READ_RAW(p, AdsDatatypeEntry, arrayDim); }
            static std::size_t sub_items(const char* p)   noexcept { return READ_RAW(p, AdsDatatypeEntry, subItems); }
            static void check_item_size(std::size_t limit, const char* data, std::size_t count)
            {
                for (; count--; )
                {
                    if (this_len(data) < sizeof(AdsDatatypeEntry) + 3)
                        throw std::runtime_error("datatype corrupted: entryLength (1)");
                    if (this_len(data) > limit)
                        throw std::runtime_error("datatype corrupted: entryLength (2)");
                    if (count && limit - this_len(data) < sizeof(AdsDatatypeEntry) + 3)
                        throw std::runtime_error("datatype corrupted: entryLength (3)");
                    auto arrayinfo_offset = checked_sum({ sizeof(AdsDatatypeEntry) + 3,
                        name_len(data), type_len(data), comment_len(data) });
                    if (this_len(data) < arrayinfo_offset)
                        throw std::runtime_error("datatype corrupted: entryLength (4)");
                    if ((this_len(data) - arrayinfo_offset) / sizeof(AdsDatatypeArrayInfo) < array_dim(data))
                        throw std::runtime_error("datatype corrupted: arrayDim");
                    auto subitems_offset = arrayinfo_offset + array_dim(data) * sizeof(AdsDatatypeArrayInfo);
                    if ((this_len(data) - subitems_offset) / (sizeof(AdsDatatypeEntry) + 3) < sub_items(data))
                        throw std::runtime_error("datatype corrupted: subItems");
                    check_item_size(this_len(data) - subitems_offset, data + subitems_offset, sub_items(data));
                    limit -= this_len(data);
                    data += this_len(data);
                }
                if (limit != 0)
                    throw std::runtime_error("datatype corrupted: unused memory");
            }
            union {
                const char* dt_data;
                const AdsDatatypeEntry* dt_data_;
            };
        };
    
        class adsSym {
        public:
            explicit adsSym(const char* raw_sym_data)
                : sym_data{ raw_sym_data }
            {
                check_item_size(sym_data);
                if (PADSSYMBOLNAME(sym_data)[name_len(sym_data)] != '\0'
                    || PADSSYMBOLTYPE(sym_data)[type_len(sym_data)] != '\0'
                    || PADSSYMBOLCOMMENT(sym_data)[comment_len(sym_data)] != '\0')
                    throw std::runtime_error("Symbol corrupted: missing terminator");
                if (strlen(name().data()) != name().length()
                    || strlen(type_name().data()) != type_name().length()
                    || strlen(comment().data()) != comment().length())
                    throw std::runtime_error("Symbol corrupted: embedded \\0");
                if (name().find('[') != name().npos)
                    throw std::runtime_error("Symbol name must not contain '['");
            }
            adsSym(const adsSym&) noexcept = default;
            adsSym(adsSym&&) noexcept = default;
            adsSym& operator=(const adsSym&) noexcept = default;
            adsSym& operator=(adsSym&&) noexcept = default;
            ~adsSym() noexcept = default;
            std::string_view name()      const noexcept { return { PADSSYMBOLNAME(sym_data), name_len(sym_data) }; }
            std::string_view type_name() const noexcept { return { PADSSYMBOLTYPE(sym_data), type_len(sym_data) }; }
            std::string_view comment()   const noexcept { return { PADSSYMBOLCOMMENT(sym_data), comment_len(sym_data) }; }
            auto type_id() const noexcept { return static_cast<adsDatatypeId>(READ_RAW(sym_data, AdsSymbolEntry, dataType)); }
            auto size()    const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, size); }
            auto group()   const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iGroup); }
            auto offset()  const noexcept { return READ_RAW(sym_data, AdsSymbolEntry, iOffs); }
    
            static const adsSym* find_by_name(const std::vector<adsSym>& data, const std::string_view& name) noexcept {
                // TODO: find something less ugly
                static_assert('.' < 'a' && '.' < '0' && '.' < '_');
                auto it = std::upper_bound(data.begin(), data.end(), name,
                    [](const std::string_view& lhs, const adsSym& rhs) {
                        return icmp_less(lhs, rhs.name());
                    });
                return it != data.begin() && (--it)->name().size() <= name.size() &&
                    icmp_equal(it->name(), { name.data(), it->name().size() }) ? &*it : nullptr;
            }
    
            static std::size_t this_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, entryLength); }
        private:
            static std::size_t name_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, nameLength); }
            static std::size_t type_len(const char* p)    noexcept { return READ_RAW(p, AdsSymbolEntry, typeLength); }
            static std::size_t comment_len(const char* p) noexcept { return READ_RAW(p, AdsSymbolEntry, commentLength); }
            static void check_item_size(const char* data) {
                if (this_len(data) < sizeof(AdsSymbolEntry) + 3)
                    throw std::runtime_error("Symbol corrupted (1)");
                if (this_len(data) != checked_sum({ sizeof(AdsSymbolEntry) + 3,
                    name_len(data), type_len(data), comment_len(data) }))
                    throw std::runtime_error("Symbol corrupted (2)");
            }
            const char* sym_data;
        };
    
    }
    

    adsData.cpp

    #include <iostream>
    #include <memory>
    #include <stdexcept>
    #include <string>
    #include <string_view>
    #include <type_traits>
    #include <vector>
    
    #include "adsData.h"
    
    namespace {
    
        std::vector<adsData_impl::adsDT> parse_dt_data(const std::vector<char>& dt_data) {
            std::vector<adsData_impl::adsDT> result;
            auto len = dt_data.size();
            if (len == 0)
                return result;
            std::size_t dt_count = 0;
            for (auto p = &dt_data[0]; len != 0; ) {
                if (len < sizeof(AdsDatatypeEntry) + 3)
                    throw std::runtime_error("datatypes corrupted (1)");
                auto cur_len = adsData_impl::adsDT::this_len(p);
                if (cur_len < sizeof(AdsDatatypeEntry) + 3 || cur_len > len)
                    throw std::runtime_error("datatypes corrupted (2)");
                ++dt_count;
                p += cur_len;
                len -= cur_len;
            }
            result.reserve(dt_count);
            for (auto p = &dt_data[0]; dt_count--; ) {
                result.emplace_back(p);
                p += adsData_impl::adsDT::this_len(p);
            }
            std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) { return adsData_impl::icmp_less(lhs.name(), rhs.name()); });
            if (std::adjacent_find(result.begin(), result.end(),
                [](auto& lhs, auto& rhs) { return !(adsData_impl::icmp_less(lhs.name(), rhs.name())); }) != result.end())
                throw std::runtime_error("datatypes not unique");
            return result;
        }
    
        std::vector<adsData_impl::adsSym> parse_sym_data(const std::vector<char>& sym_data) {
            std::vector<adsData_impl::adsSym> result;
            auto len = sym_data.size();
            if (len == 0)
                return result;
            std::size_t sym_count = 0;
            for (auto p = &sym_data[0]; len != 0; ) {
                if (len < sizeof(AdsSymbolEntry) + 3)
                    throw std::runtime_error("symboldata corrupted (1)");
                auto cur_len = adsData_impl::adsSym::this_len(p);
                if (cur_len < sizeof(AdsSymbolEntry) + 3 || cur_len > len)
                    throw std::runtime_error("symboldata corrupted (2)");
                ++sym_count;
                p += cur_len;
                len -= cur_len;
            }
            result.reserve(sym_count);
            for (auto p = &sym_data[0]; sym_count--; ) {
                result.emplace_back(p);
                p += adsData_impl::adsSym::this_len(p);
            }
            std::sort(result.begin(), result.end(), [](auto& lhs, auto& rhs) { return adsData_impl::icmp_less(lhs.name(), rhs.name()); });
            if (std::adjacent_find(result.begin(), result.end(),
                [](auto& lhs, auto& rhs) { return !adsData_impl::icmp_less(lhs.name(), rhs.name()); }) != result.end())
                throw std::runtime_error("symbols not unique");
            return result;
        }
    
        template <typename T,
                  typename std::enable_if_t<std::is_same_v<T, adsData_impl::adsDT> || std::is_same_v<T, adsData_impl::adsSym>, int> = 0>
        adsVarData get_var(std::string&& name, const std::string_view& short_name, const T& data,
            decltype(adsVarData::group) group,
            decltype(adsVarData::offset) offset,
            decltype(adsVarData::size) size) noexcept {
            return { std::move(name), short_name, data.type_name(), data.comment(), size, group, offset, data.type_id() };
        }
    
        std::string to_string_hex(std::uint64_t val) {
            char buf[2*sizeof(val) + 1];
            std::snprintf(buf, sizeof(buf), "%llx", val);
            return buf;
        }
    } // namespace
    
    adsData::adsData()
        : sym_data{}, dt_data{}, symbols{}, datatypes{}
    {
        auto ads_port = AdsPortOpenEx();
        if (ads_port == 0)
            throw std::runtime_error("cannot open ads port");
        {
            std::unique_ptr<decltype(ads_port), void(*)(decltype(ads_port)*)> guard(&ads_port,
                [](decltype(ads_port)* p) { AdsPortCloseEx(*p); });
            AmsAddr	ams_addr{};
            if (auto err = AdsGetLocalAddressEx(ads_port, &ams_addr); err != ADSERR_NOERR)
                throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err));
            ams_addr.port = AMSPORT_R0_PLC_RTS1;
            AdsSymbolUploadInfo2 info{};
            unsigned long bytes_read = 0;
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info),
                &info, &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read symbol upload info: 0x" + to_string_hex(err));
            if (bytes_read != sizeof(info))
                throw std::runtime_error("error reading sym_uploadinfo2: " + std::to_string(bytes_read) + " bytes read");
            sym_data.resize(info.nSymSize);
            dt_data.resize(info.nDatatypeSize);
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize,
                &sym_data[0], &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read symbol info: 0x" + to_string_hex(err));
            if (bytes_read != info.nSymSize)
                throw std::runtime_error("error reading symbols: " + std::to_string(bytes_read) + " bytes read, "
                + std::to_string(info.nSymSize) + " expected");
            if (auto err = AdsSyncReadReqEx2(ads_port, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize,
                &dt_data[0], &bytes_read); err != ADSERR_NOERR)
                throw std::runtime_error("cannot read datatype info: 0x" + to_string_hex(err));
            if (bytes_read != info.nDatatypeSize)
                throw std::runtime_error("error reading datatypes: " + std::to_string(bytes_read) + " bytes read, "
                    + std::to_string(info.nDatatypeSize) + " expected");
        }
        symbols = parse_sym_data(sym_data);
        datatypes = parse_dt_data(dt_data);
    }
    
    adsData::adsData(std::vector<char>&& sym_data_, std::vector<char>&& dt_data_)
        : sym_data{ std::move(sym_data_) }, dt_data{ std::move(dt_data_) },
        symbols{ parse_sym_data(sym_data) }, datatypes{ parse_dt_data(dt_data) }
    {}
    
    adsVarData adsData::operator[](std::string name) const {
        std::string_view cur_name = name;
        if (auto pos = cur_name.find('['); pos != cur_name.npos)
            cur_name.remove_suffix(cur_name.size() - pos);
        auto sym = adsData_impl::adsSym::find_by_name(symbols, cur_name);
        if (!sym)
            throw std::out_of_range("var not found: " + name);
        auto group = sym->group();
        auto offset = sym->offset();
        if (name.size() == sym->name().size())
            return get_var(std::move(name), sym->name(), *sym, group, offset, sym->size());
        cur_name = std::string_view{ name.data() + sym->name().size(), name.size() - sym->name().size() };
        if (cur_name[0] != '.' && cur_name[0] != '[' )
            throw std::out_of_range("var not found: " + name);
        auto type_name = sym->type_name();
        for (;;) {
            auto dt = adsData_impl::adsDT::find_by_name(datatypes, type_name);
            if (!dt)
                throw std::out_of_range("type not found in " + name);
            if (cur_name[0] == '.') {
                cur_name.remove_prefix(1);
                auto short_name = cur_name;
                auto pos = short_name.find_first_of(".["sv);
                if (pos != short_name.npos) {
                    short_name.remove_suffix(short_name.size() - pos);
                }
                // check all subsymbols in order
                auto sub = dt->sub_symbol_data();
                std::size_t n = 0;
                for (; n != dt->sub_symbols(); ++n) {
                    if (adsData_impl::icmp_equal(adsData_impl::adsDT(sub, false).name(), short_name))
                        break;
                    sub += adsData_impl::adsDT::this_len(sub);
                }
                if (n == dt->sub_symbols())
                    throw std::out_of_range("subsymbol not found in " + name);
                adsData_impl::adsDT sub_item(sub, false);
                offset += sub_item.offset();
                if (pos == short_name.npos)
                    return get_var(std::move(name), sub_item.name(), sub_item, group, offset, sub_item.size());
                cur_name.remove_prefix(pos);
                type_name = sub_item.type_name();
            }
            else {
                // cur_name[0] == '['
                cur_name.remove_prefix(1);
                auto pos = cur_name.find(']');
                if (pos == cur_name.npos)
                    throw std::out_of_range("missing ] in " + name);
                auto index = cur_name;
                index.remove_suffix(index.size() - pos);
                auto[elem_offset, elem_size] = dt->arr_info().elem_info(index);
                offset += elem_offset;
                cur_name.remove_prefix(pos + 1);
                if (cur_name.empty())
                    return get_var(std::move(name), {}, *dt, group, offset, elem_size);
                if (cur_name[0] != '[' && cur_name[0] != '.')
                    throw std::out_of_range("missing . or [ in " + name);
                type_name = dt->type_name();
            }
        }
    }
    


  • Hi camper.
    ->
    Kann mich nur nochmals bedanken dass du dir so viel Zeit nimmst und mir hilfst.

    ->

    Oder man fragt die Variablen gleich mit der richtigen Schreibweise ab. Ich weiss ja nicht, ob Groß-/Kleinschreibung systembedingt unbeachtlich sein soll, oder ob das nur ein zufälliges Implementationsartekt ist.

    Man kann die Variable in TwinCat nur einmal anlegen. Also nicht in verschiedenen Varianten mit Groß und Kleinschreibung. Man kann also hier case insensitive suchen, wenn man vorher alles entsprechend in groß oder klein gewandelt hat.

    Bisher war wie gesagt eher das Problem, dass die Sonderzeichen "." und "_" an verschiedenen Stellen waren. Aber du sortierst ja eh um wie du sagtest.

    ->
    zum Thema c++ 17 und boost. Die compiler options habe ich gesetzt. (was macht das genau?) wird im code auch erkannt und bringt keinen Fehler mehr. Jedoch beim kompilieren. erhalte ich immer noch: auto_ptr': is not a member of 'std'

    ->
    wo hast du im übrigen die enums und structs definiert.
    ADSDYNSYM_SUBINFO
    ADSGETDYNSYMBOLTYPE
    AdsSymbolUploadInfo2
    müssen die nicht auch in die header Datei?

    ->
    und wieso liegt die class declaration "adsData" im Header nicht im namespace ads?


Anmelden zum Antworten