TwinCat Load Symbol Data @camper


  • Mod

    booster schrieb:

    Was hast du angelegt? Ein TwinCAT XAE Project (XML format)?

    yep. Dann unter SPS ein Standard PLC Projekt angelegt, das neu erstellt, und jetzt weiss ich nicht weiter.



  • - Gegebenenfalls erst mal unter "TwinCAT\Activate Configuration".

    - 7 Tage Lizenz generieren: SolutionExplorer im Projekt unter SYSTEM\License Doppelklick -> Button "7 Days TrialLicense" dann captcha eingeben.

    - TwinCat\Restart TwinCAT System

    - Wenn TwinCAT System läuft (Logo in Statuszeile muss grün sein und Zahnrad muss sich bewegen) PLC\Login MessageBoxen bestättigen.

    - PLC\Start

    Hinweis: VMWare darf keine laufen.


  • Mod

    jetzt kommen diese Fehler beim Startversuch:

    ERR | 16.03.2018 14:23:44 730 ms | 'TCOM Server' (10): Gerät 1 (EtherCAT) (Adapter): Failed to connect to network adapater!
    WRN | 16.03.2018 14:23:44 730 ms | 'TCOM Server' (10): PREOP to SAFEOP of 'Gerät 1 (EtherCAT) (Adapter)' (0x03010011) failed - 'request is aborted' 0x9811071F
    ERR | 16.03.2018 14:23:44 733 ms | 'TwinCAT System' (10000): Fehler beim senden des AMS-Kommandos >> Init12\IO: Set State TComObj SAFEOP: Set Objects (4) to SAFEOP >> AdsWarning: 1823 (0x71f, ADS ERROR: device aborted the action) << !
    

    (VirtualBox VM mit Windows 7 x86)

    Sehe gerade, dass C:\TwinCAT\3.1\sdk\Includes eine andere Version von TcAdsDef.h hat, in der AdsSymbolUploadInfo2 fehlt, dafür gibt es dort Ads.h mit einer Menge neuerer Definition, insbesondere sind dort AdsSymbolEntry und AdsDatatypeEntry erweitert, was wahrscheinlich erklärt, wieso mein Code, der mit TwinCAT2 geschrieben und getestet wurde, fehlschlägt.
    Wenn im Test != durch < ersetzt wird, funktioniert es möglicherweise.



  • Vermutlich liegt es an der VM Ware. Oder was mir gerade der Kollege sagt: Man braucht mindestens eine phyisikalische Netzwerkkarte. Das würde auch den Fehlertext etwas erklären.

    Wenn du TwinCat noch gar nicht zum laufen bekommen hast,... dann hast du dein Code noch gar nie getestet. Alles so runter programmiert?

    Woran ich auch noch hänge: AdsVarData liefert mir nur die Informationen der Variable selber. Nicht die der SubVars.

    Wenn ich z.B- eine struct Variable in der PLC habe benötige ich von dieser auch die Information der unterliegenden Variablen.


  • Mod

    booster schrieb:

    Vermutlich liegt es an der VM Ware. Oder was mir gerade der Kollege sagt: Man braucht mindestens eine phyisikalische Netzwerkkarte. Das würde auch den Fehlertext etwas erklären.

    Ah nein, Ich musste noch den zu verwendenden Adapter unter E/A / Gerät 1/Adapter konfigurieren, paravirtualisiert ist dabei kein Problem.

    booster schrieb:

    Wenn du TwinCat noch gar nicht zum laufen bekommen hast,... dann hast du dein Code noch gar nie getestet. Alles so runter programmiert?

    Wie ich schon mehrfach sagte, ich habe mit TwinCAT2 geschrieben und getestet - das ist nicht zu bescheuert, nach einem Netzwerkaddapter zu fragen, wenn nur einer im System zu finden ist :p

    P.S. Sehe den throw Fehler jetzt auch, hier werden zu den Symbolen offenbar noch zusätzlich GUIDs geliefert.



  • Ah nein, Ich musste noch den zu verwendenden Adapter unter E/A / Gerät 1/Adapter konfigurieren, paravirtualisiert ist dabei kein Problem.

    Keine Ahnung mußte ich bisher noch nicht machen. Bei mir lief das nach Installation direkt.

    Wie ich schon mehrfach sagte, ich habe mit TwinCAT2 geschrieben
    

    Ach ja stimmt. 😃


  • Mod

    Neue Version mit Iterator-Interface. Ein Framework um zusätzliche Informationen wie Attribute, Enums, Funktionsdaten etc. auszulesen, existiert auch schon; der Aufwand, diee Informationen zugänglich zu machen, ist relativ gering. Ausserdem werden jetzt alle erkannten Daten in eigene Datenstrukturen kopiert, was beim Debuggen hilfreich sein dürfte.

    adsData.h

    #pragma once
    
    #include <cstdint>
    #include <iterator>
    #include <memory>
    #include <string>
    #include <string_view>
    #include <type_traits>
    #include <variant>
    #include <vector>
    
    #define NOMINMAX
    // %TwinCAT3DIR%\sdk\Include
    #include <Ads.h>
    namespace tcAdsAPI {
    // <TcAdsDef.h> defines many of the same types as <Ads.h> so we move these into another namespace,
    // functions in <TcAdsAPI.h> have external "C" linkange so linking is not affected
    #include <TcAdsDef.h>
    #include <TcAdsAPI.h>
    }
    
    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,
    };
    
    class adsVarData;
    
    class adsData {
    public:
        using sizeType = decltype(AdsSymbolEntry::entryLength);
        class symbolInfo;
        class subSymbolInfo;
        class datatypeInfo;
        class iterator;
        using const_iterator = iterator;
        explicit adsData(decltype(AmsAddr::port) port = AMSPORT_R0_PLC_RTS1);
    
        adsData(const adsData&) = delete;
        adsData& operator=(const adsData&) = delete;
    
        adsData(adsData&&) noexcept;
        adsData& operator=(adsData&&) noexcept;
    
        ~adsData() noexcept;
    
        adsVarData operator[](std::string_view name) const;
    
        iterator begin() const noexcept;
        iterator end() const noexcept;
        iterator cbegin() const noexcept;
        iterator cend() const noexcept;
    private:
        void initialize(const std::vector<char>& symData, const std::vector<char>& dtData);
        std::vector<symbolInfo> symbols_;
        std::vector<datatypeInfo> types_;
    };
    
    class adsVarData {
    public:
        using sizeType = adsData::sizeType;
        class iterator;
        using const_iterator = iterator;
    
        std::string        name(std::string prefix) const;
        std::string        shortName() const;
        const std::string& type() const noexcept;
        const std::string& comment() const noexcept;
        adsDatatypeId      typeId() const noexcept;
        sizeType           group() const noexcept;
        sizeType           offset() const noexcept;
        sizeType           size() const noexcept;
        bool               isStatic() const noexcept;
        const GUID*        typeGuid() const noexcept;
    
        bool is_scalar() const noexcept;
        bool is_array() const noexcept;
        bool is_struct() const noexcept;
        bool empty() const noexcept;
        sizeType subElements() const noexcept;
        iterator begin() const noexcept;
        iterator end() const noexcept;
        iterator cbegin() const noexcept;
        iterator cend() const noexcept;
    private:
        friend class adsData;
        friend class adsData::iterator;
        adsVarData(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index);
        const adsData::subSymbolInfo* info_;
        sizeType group_;
        sizeType offset_;
        sizeType index_;
    };
    
    class adsData::iterator {
    public:
        using value_type = adsVarData;
        using reference = adsVarData;
        using pointer = adsVarData;
        using difference_type = std::ptrdiff_t;
        using iterator_category = std::input_iterator_tag;
        reference operator*() const noexcept;
        pointer operator->() const noexcept;
        friend bool operator==(iterator lhs, iterator rhs) noexcept;
        friend bool operator!=(iterator lhs, iterator rhs) noexcept;
        iterator& operator++() noexcept;
        iterator operator++(int) noexcept;
    private:
        explicit iterator(std::vector<adsData::symbolInfo>::const_iterator it) noexcept;
        std::vector<adsData::symbolInfo>::const_iterator iter_;
        friend class adsData;
    };
    
    class adsVarData::iterator {
    public:
        using value_type = adsVarData;
        using reference = adsVarData;
        using pointer = adsVarData;
        using difference_type = std::ptrdiff_t;
        using iterator_category = std::input_iterator_tag;
        reference operator*() const noexcept;
        pointer operator->() const noexcept;
        friend bool operator==(const iterator& lhs, const iterator& rhs) noexcept;
        friend bool operator!=(const iterator& lhs, const iterator& rhs) noexcept;
        iterator& operator++() noexcept;
        iterator operator++(int) noexcept;
    private:
        explicit iterator(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index) noexcept;
        const adsData::subSymbolInfo* info_;
        sizeType group_;
        sizeType offset_;
        sizeType index_;
        friend class adsVarData;
    };
    

  • Mod

    adsData.cpp Teil 1

    #include <algorithm>
    #include <cassert>
    #include <cctype>
    #include <cstring>
    #include <optional>
    #include <string>
    #include <string_view>
    #include <stdexcept>
    #include <type_traits>
    #include <vector>
    
    #include "adsData.h"
    
    using namespace std::literals::string_literals;
    using namespace std::literals::string_view_literals;
    
    adsVarData::adsVarData(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index = -1)
        : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
    {}
    
    namespace {
        std::string operator+(std::string lhs, std::string_view rhs) {
            return std::move(lhs.append(rhs.data(), rhs.size()));
        }
        std::string operator+(std::string_view lhs, const std::string& rhs) {
            std::string result;
            result.reserve(lhs.size() + rhs.size());
            result.assign(lhs.data(), lhs.size()).append(rhs);
            return result;
        }
        using sizeType = adsData::sizeType;
    
        inline bool icmp_less(std::string_view lhs, std::string_view rhs) {
            return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
                [](auto x, auto y) { return
                x == '\0' ? y != '\0' : y == '\0' ? false
                : x == '.' ? y != '.' : y == '.' ? false
                : std::tolower(x) < std::tolower(y); });
        }
        inline bool icmp_equal(std::string_view lhs, 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); });
        }
    
        template <typename T, std::enable_if_t<std::is_trivial_v<T>, int> = 0>
        T readRaw(const char* p) noexcept {
            T res;
            std::memcpy(&res, p, sizeof res);
            return res;
        }
    
        namespace detail {
    #define ASSERT_THROW_(c) \
    { \
        assert(c); \
        if (!(c)) \
            throw std::runtime_error(#c); \
    }
    #define ASSERT_THROW(c) ASSERT_THROW_(c)
    
            template <typename T> struct properties_of;
    
            enum class ref_prop {
                null,
                typeSize,
                entryLen,
                nameLen,
                typeLen,
                commentLen,
                nameData,
                typeData,
                commentData,
                name,
                type,
                comment,
                nameTerminator,
                typeTerminator,
                commentTerminator,
                group,
                offset,
                size,
                flags,
                hasGuid,
                guid,
                hasAttributes,
                numAttributes,
                attributeData,
                typeId,
                elements,
                lBound,
                valueLen,
                valueData,
                value,
                valueTerminator,
                reserved,
                expEntryLen,
                version,
                arrayDim,
                arrayInfo,
                subItems,
                subItemData,
                hasCopyMask,
                copyMaskLen,
                copyMask,
                hasMethods,
                methods,
                methodData,
                hasEnumInfos,
                enumInfos,
                enums,
                alignSize,
                lengthIsPara,
                vTableIndex,
                paras,
                paraData,
                isStatic
            };
    
            template <ref_prop Name, typename Accessor>
            struct ref_property {
                static constexpr ref_prop name = Name;
                using accessor = Accessor;
            };
    
            // simple property accessors
            template <typename T>
            struct fixed_size {
                static constexpr std::size_t get(const char*, const char*) { return sizeof(T); }
                static void check(const char*, const char*, std::size_t) noexcept {}
            };
            template <typename T>
            struct is_fixed_size : std::false_type {};
            template <typename T>
            struct is_fixed_size<fixed_size<T>> : std::true_type {};
            template <typename T>
            constexpr bool is_fixed_size_v = is_fixed_size<T>::value;
    
            template <typename T, std::size_t disp, typename U = T>
            struct element {
                static U get(const char* p, const char*) noexcept { return static_cast<U>(readRaw<T>(p + disp)); };
                static constexpr std::size_t offset(const char*, const char*) noexcept { return disp; }
                static constexpr std::size_t size(const char*, const char*) noexcept { return sizeof(T); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
            template <typename T, std::size_t disp, T expected>
            struct element_ : element<T, disp> {
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    element<T, disp>::check(p, q, entryLimit);
                    ASSERT_THROW(get(p, q) == expected);
                }
            };
    #define ELEMENT(CLASS, MEMBER) \
        element<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER)>
    #define ELEMENT_TYPE(CLASS, MEMBER, TYPE) \
        element<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), TYPE>
    #define ELEMENT_EXP(CLASS, MEMBER, EXPECTED) \
        element_<std::decay_t<decltype(std::declval<CLASS&>().*&CLASS::MEMBER)>, offsetof(CLASS, MEMBER), EXPECTED>
    
            // compound property accessors
            template <typename T, ref_prop before, ref_prop expected = ref_prop::null>
            struct next_data {
                static decltype(auto) get(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); };
                static void check(const char* p, const char* q, std::size_t) {
                    static_cast<void>(p);
                    static_cast<void>(q);
                    if constexpr (expected != ref_prop::null)
                        ASSERT_THROW(get(p, q) == properties_of<T>::template get<expected>(p, q));
                }
            };
            template <typename T, ref_prop before, typename U>
            struct next_element {
                static U get(const char* p, const char* q) noexcept { return readRaw<U>(p + offset(p, q)); };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static constexpr std::size_t size(const char*, const char*) noexcept { return sizeof(U); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
            template <typename T, ref_prop before, typename U, U expected>
            struct next_element_ : next_element<T, before, U> {
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    next_element<T, before, U>::check(p, q, entryLimit);
                    ASSERT_THROW(get(p, q) == expected);
                }
            };
    
            template <typename T, ref_prop data, ref_prop length>
            struct string {
                static constexpr std::string_view get(const char* p, const char* q) noexcept { return { p + offset(p, q), size(p, q) }; };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template get<data>(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept { return properties_of<T>::template get<length>(p, q); }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                    ASSERT_THROW(get(p, q).find('\0') == std::string_view::npos); // no embedded \0
                }
            };
    
            template <typename T, ref_prop ref, unsigned long long  mask>
            struct flag {
                static constexpr bool get(const char* p, const char* q) noexcept { return (properties_of<T>::template get<ref>(p, q) & mask) != 0; };
                static void check(const char*, const char*, std::size_t) noexcept {}
            };
    
            template <typename T, ref_prop flag, typename Access>
            struct optional {
                static constexpr auto get(const char* p, const char* q) noexcept {
                    using return_type = std::optional<decltype(value(p, q))>;
                    return exists(p, q) ? return_type{ value(p, q) } : return_type{};
                }
                static constexpr auto value(const char* p, const char* q) noexcept { return Access::get(p, q); }
                static constexpr bool exists(const char* p, const char* q) noexcept { return properties_of<T>::template get<flag>(p, q); };
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return Access::offset(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept { return exists(p, q) ? Access::size(p, q) : 0; }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(exists(p, q) ? offset(p, q) < entryLimit : offset(p, q) <= entryLimit);
                    if (exists(p, q))
                        Access::check(p, q, entryLimit);
                }
            };
            template <typename T, ref_prop flag, typename Access, decltype(Access::get(std::declval<const char*>(), std::declval<const char*>())) default_>
            struct optional_ : optional<T, flag, Access> {
                static constexpr auto get(const char* p, const char* q) noexcept {
                    return exists(p, q) ? value(p, q) : default_;
                }
            };
    
            template <typename T, ref_prop before, ref_prop num, typename Element>
            struct array {
                static constexpr const char* get(const char* p, const char* q) noexcept { return p + offset(p, q); }
                static constexpr std::size_t numElements(const char* p, const char* q) noexcept { return properties_of<T>::template get<num>(p, q); }
                static constexpr std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static constexpr std::size_t size(const char* p, const char* q) noexcept {
                    if constexpr (is_fixed_size_v<typename properties_of<Element>::template accessor<ref_prop::entryLen>>) {
                        return numElements(p, q) * properties_of<Element>::template get<ref_prop::entryLen>(get(p, q), p);
                    } else {
                        auto first = get(p, q);
                        auto last = first;
                        for (auto n = numElements(p, q); n--; )
                            last += properties_of<Element>::template get<ref_prop::entryLen>(last, p);
                        return static_cast<std::size_t>(last - first);
                    }
                }
                static constexpr std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) <= entryLimit);
                    q = p;
                    entryLimit -= offset(p, q);
                    p += offset(p, q);
                    for (auto n = numElements(q, q); n--; ) {
                        ASSERT_THROW(sizeof(Element) <= entryLimit);
                        properties_of<Element>::check(p, q, entryLimit);
                        entryLimit -= properties_of<Element>::template get<ref_prop::entryLen>(p, q);
                        p += properties_of<Element>::template get<ref_prop::entryLen>(p, q);
                    }
                }
            };
    
            template <typename T, ref_prop before, typename U, ref_prop size_>
            struct var_int {
                static auto get(const char* p, const char* q) noexcept; // TODO
                static std::size_t offset(const char* p, const char* q) noexcept { return properties_of<T>::template next_offset<before>(p, q); }
                static std::size_t size(const char*, const char* q) noexcept { return properties_of<U>::template get<size_>(q, q); }
                static std::size_t next_offset(const char* p, const char* q) noexcept { return offset(p, q) + size(p, q); }
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    ASSERT_THROW(offset(p, q) < entryLimit && entryLimit - offset(p, q) >= size(p, q));
                }
            };
    
            /////////////////////////////////////////
            struct unmatchedAccessor {
                friend constexpr auto operator^(unmatchedAccessor, unmatchedAccessor) noexcept { return unmatchedAccessor{}; }
                template <typename T>
                friend constexpr auto operator^(unmatchedAccessor, T) noexcept { return T{}; }
                template <typename T>
                friend constexpr auto operator^(T, unmatchedAccessor) noexcept { return T{}; }
            };
    
            template <typename... Properties>
            struct structured_type {
                using type = structured_type<Properties...>;
    
                template <ref_prop name>
                static auto get_accessor() noexcept {
                    return (... ^ std::conditional_t<Properties::name == name, typename Properties::accessor, unmatchedAccessor>{});
                };
                template <ref_prop name>
                using accessor = decltype(get_accessor<name>());
    
                template <ref_prop name> static constexpr decltype(auto) get(const char* p, const char* q)         noexcept { return accessor<name>::get(p, q); }
                template <ref_prop name> static constexpr decltype(auto) offset(const char* p, const char* q)      noexcept { return accessor<name>::offset(p, q); }
                template <ref_prop name> static constexpr decltype(auto) size(const char* p, const char* q)        noexcept { return accessor<name>::size(p, q); }
                template <ref_prop name> static constexpr decltype(auto) next_offset(const char* p, const char* q) noexcept { return accessor<name>::next_offset(p, q); }
                template <ref_prop name> static constexpr decltype(auto) value(const char* p, const char* q)       noexcept { return accessor<name>::value(p, q); }
                template <ref_prop name> static constexpr decltype(auto) exists(const char* p, const char* q)      noexcept { return accessor<name>::exists(p, q); }
                template <ref_prop name> static constexpr decltype(auto) numElements(const char* p, const char* q) noexcept { return accessor<name>::numElements(p, q); }
    
                static void check(const char* p, const char* q, std::size_t entryLimit) {
                    (..., Properties::accessor::check(p, q, entryLimit)); // check all properties in order
                }
            };
    
            template <>
            struct properties_of<ADS_UINT8>
                : structured_type<
                ref_property<ref_prop::entryLen, fixed_size<  ADS_UINT8>>>
            {};
    
            template <>
            struct properties_of<AdsDatatypeArrayInfo>
                : structured_type<
                ref_property<ref_prop::entryLen, fixed_size<  AdsDatatypeArrayInfo>>,
                ref_property<ref_prop::lBound, ELEMENT(AdsDatatypeArrayInfo, lBound)>,
                ref_property<ref_prop::elements, ELEMENT(AdsDatatypeArrayInfo, elements)>>
            {};
    
            template <>
            struct properties_of<AdsSymbolEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsSymbolEntry, entryLength)>,
                ref_property<ref_prop::group, ELEMENT(AdsSymbolEntry, iGroup)>,
                ref_property<ref_prop::offset, ELEMENT(AdsSymbolEntry, iOffs)>,
                ref_property<ref_prop::size, ELEMENT(AdsSymbolEntry, size)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsSymbolEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsSymbolEntry, flags)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsSymbolEntry, reserved, 0)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsSymbolEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsSymbolEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsSymbolEntry, commentLength)>,
                ref_property<ref_prop::nameData, next_data<AdsSymbolEntry, ref_prop::commentLen>>,
                ref_property<ref_prop::name, string<AdsSymbolEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsSymbolEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsSymbolEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsSymbolEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsSymbolEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsSymbolEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsSymbolEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsSymbolEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::hasGuid, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_TYPEGUID>>,
                ref_property<ref_prop::guid, optional<AdsSymbolEntry, ref_prop::hasGuid, next_element<AdsSymbolEntry, ref_prop::commentTerminator, GUID>>>,
                ref_property<ref_prop::hasAttributes, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_ATTRIBUTES>>,
                ref_property<ref_prop::numAttributes, optional_<AdsSymbolEntry, ref_prop::hasAttributes, next_element<AdsSymbolEntry, ref_prop::guid, ADS_UINT16>, 0>>,
                ref_property<ref_prop::attributeData, array<AdsSymbolEntry, ref_prop::numAttributes, ref_prop::numAttributes, AdsAttributeEntry>>,
    //            ref_property<ref_prop::expEntryLen, next_data<AdsSymbolEntry, ref_prop::attributeData, ref_prop::entryLen>>,
                ref_property<ref_prop::isStatic, flag<AdsSymbolEntry, ref_prop::flags, ADSSYMBOLFLAG_STATIC>>>
            {
                static constexpr ref_prop sort_prop = ref_prop::name;
                static constexpr auto cmp_less = icmp_less;
                static constexpr auto cmp_equal = icmp_equal;
            };
    
            template <>
            struct properties_of<AdsAttributeEntry>
                : structured_type<
                ref_property<ref_prop::nameLen, ELEMENT(AdsAttributeEntry, nameLength)>,
                ref_property<ref_prop::valueLen, ELEMENT(AdsAttributeEntry, valueLength)>,
                ref_property<ref_prop::nameData, next_data<AdsAttributeEntry, ref_prop::valueLen>>,
                ref_property<ref_prop::name, string<AdsAttributeEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsAttributeEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::valueData, next_data<AdsAttributeEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::value, string<AdsAttributeEntry, ref_prop::valueData, ref_prop::valueLen>>,
                ref_property<ref_prop::valueTerminator, next_element_<AdsAttributeEntry, ref_prop::value, char, '\0'>>,
                ref_property<ref_prop::entryLen, next_data<AdsAttributeEntry, ref_prop::valueTerminator>>>
            {};
    
            template <>
            struct properties_of<AdsDatatypeEntry>
                : structured_type<
                ref_property<ref_prop::typeSize, fixed_size<  AdsDatatypeEntry>>,
                ref_property<ref_prop::entryLen, ELEMENT(AdsDatatypeEntry, entryLength)>,
                ref_property<ref_prop::version, ELEMENT_EXP(AdsDatatypeEntry, version, 1)>,
                ref_property<ref_prop::size, ELEMENT(AdsDatatypeEntry, size)>,
                ref_property<ref_prop::offset, ELEMENT(AdsDatatypeEntry, offs)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsDatatypeEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsDatatypeEntry, flags)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsDatatypeEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsDatatypeEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsDatatypeEntry, commentLength)>,
                ref_property<ref_prop::arrayDim, ELEMENT(AdsDatatypeEntry, arrayDim)>,
                ref_property<ref_prop::subItems, ELEMENT(AdsDatatypeEntry, subItems)>,
                ref_property<ref_prop::nameData, next_data<AdsDatatypeEntry, ref_prop::subItems>>,
                ref_property<ref_prop::name, string<AdsDatatypeEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsDatatypeEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsDatatypeEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsDatatypeEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsDatatypeEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsDatatypeEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsDatatypeEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsDatatypeEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::arrayInfo, array<AdsDatatypeEntry, ref_prop::commentTerminator, ref_prop::arrayDim, AdsDatatypeArrayInfo>>,
                ref_property<ref_prop::subItemData, array<AdsDatatypeEntry, ref_prop::arrayInfo, ref_prop::subItems, AdsDatatypeEntry>>,
                ref_property<ref_prop::hasGuid, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_TYPEGUID>>,
                ref_property<ref_prop::guid, optional<AdsDatatypeEntry, ref_prop::hasGuid, next_element<AdsDatatypeEntry, ref_prop::subItemData, GUID>>>,
                ref_property<ref_prop::hasCopyMask, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_COPYMASK>>,
                ref_property<ref_prop::copyMaskLen, optional_<AdsDatatypeEntry, ref_prop::hasCopyMask, ELEMENT(AdsDatatypeEntry, size), 0>>,
                ref_property<ref_prop::copyMask, array<AdsDatatypeEntry, ref_prop::guid, ref_prop::copyMaskLen, ADS_UINT8>>,
                ref_property<ref_prop::hasMethods, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_METHODINFOS>>,
                ref_property<ref_prop::methods, optional_<AdsDatatypeEntry, ref_prop::hasMethods, next_element<AdsDatatypeEntry, ref_prop::copyMask, ADS_UINT16>, 0>>,
                ref_property<ref_prop::methodData, array<AdsDatatypeEntry, ref_prop::methods, ref_prop::methods, AdsMethodEntry>>,
                ref_property<ref_prop::hasAttributes, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_ATTRIBUTES>>,
                ref_property<ref_prop::numAttributes, optional_<AdsDatatypeEntry, ref_prop::hasAttributes, next_element<AdsDatatypeEntry, ref_prop::methodData, ADS_UINT16>, 0>>,
                ref_property<ref_prop::attributeData, array<AdsDatatypeEntry, ref_prop::numAttributes, ref_prop::numAttributes, AdsAttributeEntry>>,
                ref_property<ref_prop::hasEnumInfos, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_ENUMINFOS>>,
                ref_property<ref_prop::enumInfos, optional_<AdsDatatypeEntry, ref_prop::hasEnumInfos, next_element<AdsDatatypeEntry, ref_prop::attributeData, ADS_UINT16>, 0>>,
                ref_property<ref_prop::enums, array<AdsDatatypeEntry, ref_prop::enumInfos, ref_prop::enumInfos, AdsEnumInfoEntry>>,
    //            ref_property<ref_prop::expEntryLen, next_data<AdsDatatypeEntry, ref_prop::enums, ref_prop::entryLen>>,
                ref_property<ref_prop::isStatic, flag<AdsDatatypeEntry, ref_prop::flags, ADSDATATYPEFLAG_STATIC>>>
            {
                static constexpr ref_prop sort_prop = ref_prop::name;
                static constexpr auto cmp_less = std::less<>{};
                static constexpr auto cmp_equal = std::equal_to<>{};
            };
    
            template <>
            struct properties_of<AdsMethodParaEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsMethodParaEntry, entryLength)>,
                ref_property<ref_prop::size, ELEMENT(AdsMethodParaEntry, size)>,
                ref_property<ref_prop::alignSize, ELEMENT(AdsMethodParaEntry, alignSize)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsMethodParaEntry, dataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsMethodParaEntry, flags)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsMethodParaEntry, reserved, 0)>,
                ref_property<ref_prop::guid, ELEMENT(AdsMethodParaEntry, typeGuid)>,
                ref_property<ref_prop::lengthIsPara, ELEMENT(AdsMethodParaEntry, lengthIsPara)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsMethodParaEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsMethodParaEntry, typeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsMethodParaEntry, commentLength)>,
                ref_property<ref_prop::nameData, next_data<AdsMethodParaEntry, ref_prop::commentLen>>,
                ref_property<ref_prop::name, string<AdsMethodParaEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsMethodParaEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsMethodParaEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsMethodParaEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsMethodParaEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsMethodParaEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsMethodParaEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsMethodParaEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::expEntryLen, next_data<AdsMethodParaEntry, ref_prop::commentTerminator, ref_prop::entryLen>>>
            {};
    
            template <>
            struct properties_of<AdsMethodEntry>
                : structured_type<
                ref_property<ref_prop::entryLen, ELEMENT(AdsMethodEntry, entryLength)>,
                ref_property<ref_prop::version, ELEMENT_EXP(AdsMethodEntry, version, 1)>,
                ref_property<ref_prop::vTableIndex, ELEMENT(AdsMethodEntry, vTableIndex)>,
                ref_property<ref_prop::size, ELEMENT(AdsMethodEntry, returnSize)>,
                ref_property<ref_prop::alignSize, ELEMENT(AdsMethodEntry, returnAlignSize)>,
                ref_property<ref_prop::reserved, ELEMENT_EXP(AdsMethodEntry, reserved, 0)>,
                ref_property<ref_prop::guid, ELEMENT(AdsMethodEntry, returnTypeGuid)>,
                ref_property<ref_prop::typeId, ELEMENT_TYPE(AdsMethodEntry, returnDataType, adsDatatypeId)>,
                ref_property<ref_prop::flags, ELEMENT(AdsMethodEntry, flags)>,
                ref_property<ref_prop::nameLen, ELEMENT(AdsMethodEntry, nameLength)>,
                ref_property<ref_prop::typeLen, ELEMENT(AdsMethodEntry, returnTypeLength)>,
                ref_property<ref_prop::commentLen, ELEMENT(AdsMethodEntry, commentLength)>,
                ref_property<ref_prop::paras, ELEMENT(AdsMethodEntry, paras)>,
                ref_property<ref_prop::nameData, next_data<AdsMethodEntry, ref_prop::paras>>,
                ref_property<ref_prop::name, string<AdsMethodEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsMethodEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::typeData, next_data<AdsMethodEntry, ref_prop::nameTerminator>>,
                ref_property<ref_prop::type, string<AdsMethodEntry, ref_prop::typeData, ref_prop::typeLen>>,
                ref_property<ref_prop::typeTerminator, next_element_<AdsMethodEntry, ref_prop::type, char, '\0'>>,
                ref_property<ref_prop::commentData, next_data<AdsMethodEntry, ref_prop::typeTerminator>>,
                ref_property<ref_prop::comment, string<AdsMethodEntry, ref_prop::commentData, ref_prop::commentLen>>,
                ref_property<ref_prop::commentTerminator, next_element_<AdsMethodEntry, ref_prop::comment, char, '\0'>>,
                ref_property<ref_prop::paraData, array<AdsMethodEntry, ref_prop::commentTerminator, ref_prop::paras, AdsMethodParaEntry>>,
                ref_property<ref_prop::expEntryLen, next_data<AdsMethodEntry, ref_prop::paraData, ref_prop::entryLen>>>
            {};
    
            template <>
            struct properties_of<AdsEnumInfoEntry>
                : structured_type<
                ref_property<ref_prop::nameLen, ELEMENT(AdsEnumInfoEntry, nameLength)>,
                ref_property<ref_prop::nameData, next_data<AdsEnumInfoEntry, ref_prop::nameLen>>,
                ref_property<ref_prop::name, string<AdsEnumInfoEntry, ref_prop::nameData, ref_prop::nameLen>>,
                ref_property<ref_prop::nameTerminator, next_element_<AdsEnumInfoEntry, ref_prop::name, char, '\0'>>,
                ref_property<ref_prop::value, var_int<AdsEnumInfoEntry, ref_prop::nameTerminator, AdsDatatypeEntry, ref_prop::size>>,
                ref_property<ref_prop::entryLen, next_data<AdsEnumInfoEntry, ref_prop::value>>>
            {};
    
            template <typename T>
            class ref {
            public:
                constexpr explicit ref(const char* p) noexcept : data(p) {}
                ref(const char* p, std::size_t entryLimit) : data(p) {
                    properties_of<T>::check(p, nullptr, entryLimit);
                }
                template <ref_prop prop>
                auto get() const noexcept {
                    return get<prop>(data);
                }
                template <ref_prop prop>
                static decltype(auto) get(const char*p) noexcept {
                    return properties_of<T>::template get<prop>(p, nullptr);
                }
            private:
                const char* data;
    
                friend bool operator<(const ref& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs.get<prop::sort_prop>(), rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator<(const U& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs, rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator<(const ref& lhs, const U& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_less(lhs.get<prop::sort_prop>(), rhs);
                }
                friend bool operator==(const ref& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs.get<prop::sort_prop>(), rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator==(const U& lhs, const ref& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs, rhs.get<prop::sort_prop>());
                }
                template <typename U>
                friend bool operator==(const ref& lhs, const U& rhs) noexcept {
                    using prop = properties_of<T>;
                    return prop::cmp_equal(lhs.get<prop::sort_prop>(), rhs);
                }
            };
        } // namespace detail
        using detail::ref;
        using detail::ref_prop;
    
        inline std::size_t checkedSum(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;
        }
    
        using std::rbegin;
        using std::rend;
    
        template <typename T>
        struct reverse_view { T& iterable; };
    
        template <typename T>
        auto begin(reverse_view<T> w) { return rbegin(w.iterable); }
    
        template <typename T>
        auto end(reverse_view<T> w) { return rend(w.iterable); }
    
        template <typename T>
        reverse_view<T> reverse(T&& iterable) { return { iterable }; }
    }
    
    class adsData::subSymbolInfo {
    public:
        subSymbolInfo(std::string_view name, std::string_view comment,
            sizeType offset, bool isStatic,
            adsData::datatypeInfo* typeData = nullptr)
            : name{ name }, comment{ comment }, offset{ offset },
              isStatic { isStatic }, typeData{ typeData }
        {}
        std::string name;
        std::string comment;
        sizeType offset;
        bool isStatic;
        adsData::datatypeInfo* typeData;
        friend bool operator==(const subSymbolInfo& lhs, const subSymbolInfo& rhs) {
            return icmp_equal(lhs.name, rhs.name);
        }
        friend bool operator==(const subSymbolInfo& lhs, std::string_view rhs) {
            return icmp_equal(lhs.name, rhs);
        }
        friend bool operator==(std::string_view lhs, const subSymbolInfo& rhs) {
            return icmp_equal(lhs, rhs.name);
        }
    };
    
    namespace {
    
        struct adsArrayInfo : adsData::subSymbolInfo
        {
            adsArrayInfo(std::string_view comment, sizeType arraySize, sizeType arrayDims, const char* arrayInfoData)
                : adsData::subSymbolInfo{ ""sv, comment, 0, false }, arrayInfo{}, numElements_{ 1 }
            {
                assert(arrayDims != 0);
                arrayInfo.reserve(arrayDims);
                for (; arrayDims--; ) {
                    ref<AdsDatatypeArrayInfo> ref(arrayInfoData);
                    auto& info = arrayInfo.emplace_back(AdsDatatypeArrayInfo{ ref.get<ref_prop::lBound>(), ref.get<ref_prop::elements>() });
                    sizeType rBound = info.lBound + info.elements - 1;
                    if (info.elements == 0 || rBound < info.lBound || arraySize % info.elements != 0)
                        throw std::runtime_error("array info corrupted");
                    arraySize /= info.elements;
                    numElements_ *= info.elements;
                    arrayInfoData += ref.get<ref_prop::entryLen>();
                }
            }
            sizeType index(std::string_view i) const {
                auto workIndex = i;
                sizeType realIndex = 0;
                for (auto& info : arrayInfo) {
                    auto pos = workIndex.find(',');
                    if ((&info == &arrayInfo.back()) == (pos == workIndex.npos))
                        throw std::out_of_range("index with wrong number of dimensions: "s + i);
                    auto curIndex = workIndex;
                    if (pos != workIndex.npos) {
                        curIndex.remove_suffix(curIndex.size() - pos);
                        workIndex.remove_prefix(pos + 1);
                    }
                    auto n = svtoi(curIndex);
                    if (n < info.lBound || n - info.lBound >= info.elements)
                        throw std::out_of_range("index out of range: "s + i);
                    // we don't need to check for overflow here since the constructor already ensures that 
                    // indizes stay within proper bounds
                    realIndex = realIndex * info.elements + (n - info.lBound);
                }
                return realIndex;
            }
            std::string toString(sizeType i) const {
                if (i >= numElements_)
                    throw std::out_of_range("index out of range");
                std::string result = "]";
                for (auto& info : reverse(arrayInfo)) {
                    auto curIndex = i % info.elements;
                    i /= info.elements;
                    do {
                        result.push_back(static_cast<char>('0' + curIndex % 10));
                    } while (curIndex /= 10);
                    if (&info != &arrayInfo.front())
                        result.push_back(',');
                }
                result.push_back('[');
                std::reverse(result.begin(), result.end());
                return result;
            }
            sizeType elemSize() const noexcept;
            sizeType numElements() const noexcept { return numElements_; }
            static sizeType svtoi(std::string_view s) {
                sizeType result = 0;
                if (s.empty())
                    throw std::out_of_range("index corrupted");
                for (; !s.empty(); s.remove_prefix(1)) {
                    if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
                        throw std::out_of_range("index corrupted");
                    result = result * 10 + (s[0] - '0');
                }
                return result;
            }
            std::vector<AdsDatatypeArrayInfo> arrayInfo;
            sizeType numElements_;
        };
    }
    
    class adsData::symbolInfo {
    public:
        symbolInfo(std::string_view name, std::string_view comment,
            sizeType offset, sizeType group, bool isStatic, datatypeInfo* typeData )
            : baseInfo{ name, comment, offset, isStatic, typeData }, group{ group }
        {}
        subSymbolInfo baseInfo;
        sizeType group;
        friend bool operator<(const symbolInfo& lhs, const symbolInfo& rhs) {
            return icmp_less(lhs.baseInfo.name, rhs.baseInfo.name);
        }
        friend bool operator<(const symbolInfo& lhs, std::string_view rhs) {
            return icmp_less(lhs.baseInfo.name, rhs);
        }
        friend bool operator<(std::string_view lhs, const symbolInfo& rhs) {
            return icmp_less(lhs, rhs.baseInfo.name);
        }
    };
    class adsData::datatypeInfo {
    public:
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid)
            : name{ name }, size{ size }, guid{ guid }, id { id }, visited(false),
              typeSpecs{}
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            datatypeInfo* info)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ info }
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            adsArrayInfo&& arrayInfo)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ std::move(arrayInfo) }
        {}
        datatypeInfo(std::string_view name, sizeType size, adsDatatypeId id, std::optional<GUID> guid,
            sizeType numSymbols)
            : name{ name }, size{ size }, guid{ guid }, id{ id }, visited(false),
              typeSpecs{ std::vector<subSymbolInfo>{} }
        {
            std::get<std::vector<subSymbolInfo>>(typeSpecs).reserve(numSymbols);
        }
        std::string name;
        sizeType size;
        std::optional<GUID> guid;
        adsDatatypeId id;
        bool visited; // used for cycle detection
        enum { baseSpec, compoundSpec, arraySpec, classSpec };
        std::variant<std::monostate,                         // base type
                     adsData::datatypeInfo*,                 // compound type
                     adsArrayInfo,                           // array
                     std::vector<subSymbolInfo>> typeSpecs;  // class
    
        friend bool operator<(const adsData::datatypeInfo& lhs, const adsData::datatypeInfo& rhs) {
            return lhs.name < rhs.name;
        }
        friend bool operator<(const adsData::datatypeInfo& lhs, std::string_view rhs) {
            return lhs.name < rhs;
        }
        friend bool operator<(std::string_view lhs, const adsData::datatypeInfo& rhs) {
            return lhs < rhs.name;
        }
    };
    namespace {
        sizeType adsArrayInfo::elemSize() const noexcept { return typeData->size; }
    
        using adsDT = ref<AdsDatatypeEntry>;
        using adsSym = ref<AdsSymbolEntry>;
    
        template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
        std::string to_string_hex(T v) {
            auto val = static_cast<std::uint64_t>(v);
            char buf[2 * sizeof(val) + 1];
            std::snprintf(buf, sizeof(buf), "%llx", val);
            return buf;
        }
    
        const adsDT* find(const std::vector<adsDT>& data, std::string_view name) noexcept {
            auto it = std::lower_bound(data.begin(), data.end(), name);
            return it != data.end() && it->get<ref_prop::name>() == name ? &*it : nullptr;
        }
        const adsSym* find(const std::vector<adsSym>& data, std::string_view name) noexcept {
            auto it = std::upper_bound(data.begin(), data.end(), name);
            // prev is a match if it is a proper prefix of name
            return it != data.begin() && (--it)->get<ref_prop::nameLen>() <= name.size()
                && icmp_equal(it->get<ref_prop::name>(), std::string_view{ 0, it->get<ref_prop::nameLen>() }) ? &*it : nullptr;
        }
    
        void addDatatypes(const std::vector<char>& dtData, std::vector<adsData::datatypeInfo>& types) {
            auto limit = dtData.size();
            std::size_t count = 0;
            for (auto p = &dtData[0]; limit != 0; ) {
                adsDT cur(p, limit);
                ++count;
                p += cur.get<ref_prop::entryLen>();
                limit -= cur.get<ref_prop::entryLen>();
            }
            types.reserve(count);
            for (auto p = &dtData[0]; count--; ) {
                adsDT dt(p);
                p += dt.get<ref_prop::entryLen>();
                if (dt.get<ref_prop::arrayDim>() != 0) {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>(),
                        adsArrayInfo{ dt.get<ref_prop::comment>(), dt.get<ref_prop::size>(), dt.get<ref_prop::arrayDim>(), dt.get<ref_prop::arrayInfo>() });
                } else if (dt.get<ref_prop::subItems>() != 0) {
                    auto& ref = types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(),
                        dt.get<ref_prop::guid>(), dt.get<ref_prop::subItems>());
                    auto& subs = std::get<adsData::datatypeInfo::classSpec>(ref.typeSpecs);
                    auto q = dt.get<ref_prop::subItemData>();
                    for (auto n = dt.get<ref_prop::subItems>(); n--;) {
                        adsDT sub(q);
                        q += sub.get<ref_prop::entryLen>();
                        subs.emplace_back(sub.get<ref_prop::name>(), sub.get<ref_prop::comment>(), sub.get<ref_prop::offset>(),
                            sub.get<ref_prop::isStatic>());
                    }
                } else if (dt.get<ref_prop::type>().empty()) {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>());
                } else {
                    types.emplace_back(dt.get<ref_prop::name>(), dt.get<ref_prop::size>(), dt.get<ref_prop::typeId>(), dt.get<ref_prop::guid>(), nullptr );
                }
            }
            std::sort(types.begin(), types.end());
        }
        void linkDatatypes(const std::vector<char>& dtData, std::vector<adsData::datatypeInfo>& types) {
            auto p = &dtData[0];
            for (auto count = types.size(); count--; ) {
                adsDT dt(p);
                p += dt.get<ref_prop::entryLen>();
                auto type = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::name>());
                assert(type != types.end());
                switch (type->typeSpecs.index()) {
                    default:
                        assert(false);
                    case adsData::datatypeInfo::baseSpec:
                        break;
                    case adsData::datatypeInfo::compoundSpec: {
                        auto it = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::type>());
                        if (it == types.end())
                            throw std::runtime_error("Datytypes corrupted: missing type data");
                        std::get<adsData::datatypeInfo::compoundSpec>(type->typeSpecs) = &*it;
                        break;
                    }
                    case adsData::datatypeInfo::arraySpec: {
                        auto it = std::lower_bound(types.begin(), types.end(), dt.get<ref_prop::type>());
                        if (it == types.end())
                            throw std::runtime_error("Datytypes corrupted: missing type data");
                        auto& info = std::get<adsData::datatypeInfo::arraySpec>(type->typeSpecs);
                        if (dt.get<ref_prop::size>() / info.numElements() != it->size)
                            throw std::runtime_error("Datatypes corrupted: mismatched element size");
                        info.typeData = &*it;
                        break;
                    }
                    case adsData::datatypeInfo::classSpec: {
                        const char* q = dt.get<ref_prop::subItemData>();
                        for (auto& info : std::get<adsData::datatypeInfo::classSpec>(type->typeSpecs)) {
                            adsDT sub(q);
                            q += sub.get<ref_prop::entryLen>();
                            auto it = std::lower_bound(types.begin(), types.end(), sub.get<ref_prop::type>());
                            if (it == types.end())
                                throw std::runtime_error("Datytypes corrupted: missing type data");
                            if (sub.get<ref_prop::size>() != it->size)
                                throw std::runtime_error("Datatypes corrupted: alias mismatched size");
                            info.typeData = &*it;
                        }
                        break;
                    }
                }
            }
        }
        void addSymbols(const std::vector<char>& symData, std::vector<adsData::datatypeInfo>& types,
            std::vector<adsData::symbolInfo>& symbols) {
            auto limit = symData.size();
            std::size_t count = 0;
            for (auto p = &symData[0]; limit != 0; ) {
                adsSym cur(p, limit);
                ++count;
                p += cur.get<ref_prop::entryLen>();
                limit -= cur.get<ref_prop::entryLen>();
            }
            symbols.reserve(symData.size());
            for (auto p = &symData[0]; count--; ) {
                adsSym sym(p);
                p += sym.get<ref_prop::entryLen>();
                if (sym.get<ref_prop::type>().empty())
                    throw std::runtime_error("Symboldata corrupted: symbol has no type");
                auto it = std::lower_bound(types.begin(), types.end(), sym.get<ref_prop::type>());
                if (it == types.end())
                    throw std::runtime_error("Symboldata corrupted: missing type data");
                if (sym.get<ref_prop::size>() != it->size)
                    throw std::runtime_error("Symboldata corrupted: mismatched size");
                if (sym.get<ref_prop::guid>() != it->guid)
                    throw std::runtime_error("mismatched guid");
                symbols.emplace_back(sym.get<ref_prop::name>(), sym.get<ref_prop::comment>(), sym.get<ref_prop::offset>(),
                    sym.get<ref_prop::group>(), sym.get<ref_prop::isStatic>(), &*it);
            }
            std::sort(symbols.begin(), symbols.end());
        }
        void cycleCheck(adsData::datatypeInfo* p) {
            if (p->visited)
                throw std::runtime_error("Datatypes corrupted: cycle detected");
            switch (p->typeSpecs.index()) {
                default:
                    assert(false);
                case adsData::datatypeInfo::baseSpec:
                    return;
                case adsData::datatypeInfo::compoundSpec:
                    p->visited = true;
                    cycleCheck(std::get<adsData::datatypeInfo::compoundSpec>(p->typeSpecs));
                    p->visited = false;
                    return;
                case adsData::datatypeInfo::arraySpec: {
                    auto& info = std::get<adsData::datatypeInfo::arraySpec>(p->typeSpecs);
                    if (info.numElements() != 1)
                        return;
                    p->visited = true;
                    cycleCheck(info.typeData);
                    p->visited = false;
                    return;
                }
                case adsData::datatypeInfo::classSpec: {
                    auto& info = std::get<adsData::datatypeInfo::classSpec>(p->typeSpecs);
                    p->visited = true;
                    for (auto& sub : info)
                        if (sub.typeData->size == p->size)
                            cycleCheck(sub.typeData);
                    p->visited = false;
                    return;
                }
            }
        }
        void cycleCheck(std::vector<adsData::datatypeInfo>& types) {
            for (auto& info : types)
                cycleCheck(&info);
        }
    } // namespace
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsData::adsData(decltype(AmsAddr::port) port)
        : symbols_{}, types_{}
    {
        auto adsPort = tcAdsAPI::AdsPortOpenEx();
        if (adsPort == 0)
            throw std::runtime_error("cannot open ads port");
        std::unique_ptr<decltype(adsPort), void(*)(decltype(adsPort)*)> guard(&adsPort,
            [](decltype(adsPort)* p) { tcAdsAPI::AdsPortCloseEx(*p); });
        tcAdsAPI::AmsAddr ams_addr{};
        if (auto err = tcAdsAPI::AdsGetLocalAddressEx(adsPort, &ams_addr); err != ADSERR_NOERR)
            throw std::runtime_error("cannot get local ams address: 0x" + to_string_hex(err));
        ams_addr.port = port;
        AdsSymbolUploadInfo2 info{};
        unsigned long bytes_read = 0;
        if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &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");
        std::vector<char> symData(info.nSymSize);
        std::vector<char> dtData(info.nDatatypeSize);
        if (auto err = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize,
            &symData[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 = tcAdsAPI::AdsSyncReadReqEx2(adsPort, &ams_addr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize,
            &dtData[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");
        guard.release();
        initialize(symData, dtData);
    }
    
    adsData::adsData(adsData&&) noexcept = default;
    adsData& adsData::operator=(adsData&&) noexcept = default;
    
    adsData::~adsData() noexcept = default;
    
    void adsData::initialize(const std::vector<char>& symData, const std::vector<char>& dtData) {
        addDatatypes(dtData, types_);
        linkDatatypes(dtData, types_);
        addSymbols(symData, types_, symbols_);
        cycleCheck(types_);
    }
    
    adsVarData adsData::operator[](std::string_view name) const {
        auto curName = name;
        if (auto pos = curName.find('['); pos != curName.npos)
            curName.remove_suffix(curName.size() - pos);
        auto sym = std::lower_bound(symbols_.begin(), symbols_.end(), curName);
        if (sym == symbols_.end())
            throw std::out_of_range("var not found: " + name);
        auto group = sym->group;
        auto offset = sym->baseInfo.offset;
        if (name.size() == sym->baseInfo.name.size())
            return adsVarData{ &sym->baseInfo, group, offset };
        curName = std::string_view{ name.data() + sym->baseInfo.name.size(), name.size() - sym->baseInfo.name.size() };
        if (curName[0] != '.' && curName[0] != '[')
            throw std::out_of_range("var not found: " + name);
        auto dt = sym->baseInfo.typeData;
        for (;;) {
            if (curName[0] == '.') {
                if (dt->typeSpecs.index() != adsData::datatypeInfo::classSpec)
                    throw std::out_of_range("has no subobjects: "s
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                        + " with "sv + curName);
                curName.remove_prefix(1); // '.'
                auto shortName = curName;
                auto pos = shortName.find_first_of(".["sv);
                if (pos != shortName.npos) {
                    shortName.remove_suffix(shortName.size() - pos);
                }
                auto& subs = std::get<adsData::datatypeInfo::classSpec>(dt->typeSpecs);
                auto sub = std::find_if(subs.begin(), subs.end(), [=](auto& v) { return icmp_equal(shortName, v.name); });
                if (sub == subs.end())
                    throw std::out_of_range("subobjects not found: "s + shortName + " of "sv
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                        + " with "sv + name);
                if (sub->isStatic)
                    offset = sub->offset;
                else
                    offset += sub->offset;
                if (pos == shortName.npos)
                    return adsVarData{ &*sub, group, offset };
                curName.remove_prefix(pos);
                dt = sub->typeData;
            } else {
                // cur_name[0] == '['
                if (dt->typeSpecs.index() != adsData::datatypeInfo::arraySpec)
                    throw std::out_of_range("is no array: "s
                        + std::string_view{ name.data(), static_cast<std::size_t>(curName.data() - name.data()) }
                +" with "sv + curName);
                curName.remove_prefix(1); // '['
                auto pos = curName.find(']');
                if (pos == curName.npos)
                    throw std::out_of_range("missing ]");
                auto index = curName;
                index.remove_suffix(index.size() - pos);
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(dt->typeSpecs);
                auto i = info.index(index);
                offset += info.elemSize() * i;
                curName.remove_prefix(pos + 1); // "index]"
                if (curName.empty())
                    return adsVarData{ &info, group, offset, i };
                if (curName[0] != '[' && curName[0] != '.')
                    throw std::out_of_range("missing . or [");
                dt = info.typeData;
            }
        }
    }
    
    adsData::iterator adsData::begin() const noexcept { return iterator{ symbols_.begin() }; }
    adsData::iterator adsData::end() const noexcept { return iterator{ symbols_.end() }; }
    adsData::iterator adsData::cbegin() const noexcept { return begin(); }
    adsData::iterator adsData::cend() const noexcept { return end(); }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    std::string adsVarData::name(std::string prefix) const {
        if (!info_->name.empty()) {
            prefix.reserve(prefix.size() + 1 + info_->name.size());
            prefix += '.';
            prefix += info_->name;
            return std::move(prefix);
        } else
            return std::move(prefix) + shortName();
    }
    std::string adsVarData::shortName() const {
        return info_->name.empty() ? static_cast<const adsArrayInfo*>(info_)->toString(index_) : info_->name;
    }
    const std::string& adsVarData::type() const noexcept { return info_->typeData->name; }
    const std::string& adsVarData::comment() const noexcept { return info_->comment; }
    adsDatatypeId adsVarData::typeId() const noexcept { return info_->typeData->id; }
    sizeType adsVarData::group() const noexcept { return group_;  }
    sizeType adsVarData::offset() const noexcept { return offset_; }
    sizeType adsVarData::size() const noexcept { return info_->typeData->size; }
    bool adsVarData::isStatic() const noexcept { return info_->isStatic; }
    const GUID* adsVarData::typeGuid() const noexcept {
        return info_->typeData->guid.has_value() ? &*info_->typeData->guid : nullptr;
    }
    
    bool adsVarData::is_scalar() const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::baseSpec
            || info_->typeData->typeSpecs.index() == adsData::datatypeInfo::compoundSpec; }
    bool adsVarData::is_array()  const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::arraySpec;
    }
    bool adsVarData::is_struct() const noexcept {
        return info_->typeData->typeSpecs.index() == adsData::datatypeInfo::classSpec; }
    bool adsVarData::empty() const noexcept {
        return is_scalar() || is_struct()
            && std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs).empty();
    }
    sizeType adsVarData::subElements() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
        default:
            assert(false);
        case adsData::datatypeInfo::baseSpec:
        case adsData::datatypeInfo::compoundSpec:
            return 0;
        case adsData::datatypeInfo::arraySpec:
            return std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs).numElements();
        case adsData::datatypeInfo::classSpec:
            return std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs).size();
        }
    }
    adsVarData::iterator adsVarData::begin() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
            default:
                assert(false);
            case adsData::datatypeInfo::baseSpec:
            case adsData::datatypeInfo::compoundSpec:
                return iterator{ nullptr, 0, 0, 0 };
            case adsData::datatypeInfo::arraySpec: {
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs);
                return iterator{ &info, group_, offset_, 0 };
            }
            case adsData::datatypeInfo::classSpec: {
                auto& info = std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs);
                return iterator{ info.empty() ? nullptr : &info.front(), group_, offset_, 0 };
            }
        }
    }
    adsVarData::iterator adsVarData::end() const noexcept {
        switch (info_->typeData->typeSpecs.index()) {
            default:
                assert(false);
            case adsData::datatypeInfo::baseSpec:
            case adsData::datatypeInfo::compoundSpec:
                return iterator{ nullptr, 0, 0, 0 };
            case adsData::datatypeInfo::arraySpec: {
                auto& info = std::get<adsData::datatypeInfo::arraySpec>(info_->typeData->typeSpecs);
                return iterator{ &info, group_, offset_, info.numElements() };
            }
            case adsData::datatypeInfo::classSpec: {
                auto& info = std::get<adsData::datatypeInfo::classSpec>(info_->typeData->typeSpecs);
                return iterator{ info.empty() ? nullptr : &info.back() + 1, group_, offset_, 0 };
            }
        }
    }
    adsVarData::iterator adsVarData::cbegin() const noexcept { return begin(); }
    adsVarData::iterator adsVarData::cend() const noexcept { return end(); }
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsData::iterator::reference adsData::iterator::operator*() const noexcept {
        return adsVarData{ &iter_->baseInfo, iter_->group, iter_->baseInfo.offset };
    }
    adsData::iterator::pointer adsData::iterator::operator->() const noexcept { return **this; }
    bool operator==(adsData::iterator lhs, adsData::iterator rhs) noexcept { return lhs.iter_ == rhs.iter_; }
    bool operator!=(adsData::iterator lhs, adsData::iterator rhs) noexcept { return lhs.iter_ != rhs.iter_; }
    adsData::iterator& adsData::iterator::operator++() noexcept { ++iter_; return *this; }
    adsData::iterator adsData::iterator::operator++(int) noexcept { auto tmp = *this; ++*this; return tmp; }
    adsData::iterator::iterator(std::vector<adsData::symbolInfo>::const_iterator it) noexcept : iter_{ it } {}
    

  • Mod

    adsData.cpp Teil 2

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    adsVarData::iterator::reference adsVarData::iterator::operator*() const noexcept {
        if (info_->name.empty())
            return adsVarData{ info_, group_, offset_ + index_ * info_->typeData->size, index_ };
        else
            return adsVarData{ info_, group_, info_->isStatic ? offset_ + info_->offset : info_->offset };
    }
    adsVarData::iterator::pointer adsVarData::iterator::operator->() const noexcept { return **this; }
    bool operator==(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept {
        return lhs.info_ == rhs.info_ && lhs.index_ == rhs.index_
            && lhs.offset_ == rhs.offset_ && lhs.group_ == rhs.group_;
    }
    bool operator!=(const adsVarData::iterator& lhs, const adsVarData::iterator& rhs) noexcept { return !(lhs == rhs); }
    adsVarData::iterator& adsVarData::iterator::operator++() noexcept {
        if (info_->name.empty())
            ++index_;
        else
            ++info_;
        return *this;
    }
    adsVarData::iterator adsVarData::iterator::operator++(int) noexcept { auto tmp = *this; ++*this; return tmp; }
    adsVarData::iterator::iterator(const adsData::subSymbolInfo* info, sizeType group, sizeType offset, sizeType index) noexcept
        : info_{ info }, group_{ group }, offset_{ offset }, index_{ index }
    {}
    

    Demo

    #include <algorithm>
    #include <iomanip>
    #include <iostream>
    #include <stdexcept>
    #include <string>
    #include <string_view>
    #include <utility>
    #include "adsData.h"
    
    using namespace std::literals::string_view_literals;
    
    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 GUID& guid) {
        return s << std::uppercase << std::hex << std::setfill('0')
            << std::setw(8) << guid.Data1 << '-'
            << std::setw(4) << guid.Data2 << '-'
            << std::setw(4) << guid.Data3 << '-'
            << std::setw(2) << static_cast<short>(guid.Data4[0])
            << static_cast<short>(guid.Data4[1])
            << '-' << static_cast<short>(guid.Data4[2])
            << static_cast<short>(guid.Data4[3])
            << static_cast<short>(guid.Data4[4])
            << static_cast<short>(guid.Data4[5])
            << static_cast<short>(guid.Data4[6])
            << static_cast<short>(guid.Data4[7])
            << std::nouppercase << std::dec;
    }
    
    std::ostream& operator<<(std::ostream& s, const adsVarData& var) {
        s   << "short: " << var.shortName() << '\n'
            << "type: " << var.type() << '\n'
            << "comment: " << var.comment() << '\n'
            << "typeId: " << var.typeId() << '\n'
            << "group: " << var.group() << '\n'
            << "offset: " << var.offset() << '\n'
            << "size: " << var.size() << '\n'
            << "is static: " << std::boolalpha << var.isStatic() << '\n';
        if (var.typeGuid())
            s << "type GUID: " << *var.typeGuid() << '\n';
        return s;
    }
    
    void showSubs(const adsVarData& var, std::string prefix) {
        for (auto&& sub : var) {
            std::cout << "name: " << sub.name(prefix) << '\n' << sub << '\n';
            showSubs(sub, sub.name(prefix));
        }
    }
    
    int main() {
        std::string varNames[] = {
            "MAIN.myInt",
        };
        try {
            adsData plcData{};
            for (auto&& var : plcData) {
                std::cout << var << '\n';
                showSubs(var, var.shortName());
            }
            for (auto& name : varNames)
                try {
                std::cout << plcData[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;
        }
    }
    

    gibt einfach alles aus.


  • Mod

    Ich glaube, es wäre sinnvoll, die längeren Schnipsel zu verlinken.



  • @camper

    Wahnsinn. Vielen Dank.

    Bevor ich da jetzt erst mal inhaltlich was dazu sagen kann muss ich mich erst mal eine Weile durchkämpfen.

    Frage: Wieso hast du jetzt keine _impl.h ->

    Für mich war zwar klar dass man dies macht um bestimmte Implemetierungsdetails zu verstecken. Aber das hast du ja auch mit dem namenlosen namespace gemacht. Und wann man nun was wo hin packt war nicht ganz so ersichtlich.



  • Den Code habe ich nun mal in mein Programm genommen.
    Kompiliert aber noch nicht folgender Fehler:

    Error	C1083	Cannot open include file: 'VersionHelpers.h': No such file or directory	c:\twincat\3.1\sdk\include\TcDef.h
    

  • Mod

    booster schrieb:

    Den Code habe ich nun mal in mein Programm genommen.
    Kompiliert aber noch nicht folgender Fehler:

    Error	C1083	Cannot open include file: 'VersionHelpers.h': No such file or directory	c:\twincat\3.1\sdk\include\TcDef.h
    

    verweist bei mir auf
    c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren.


  • Mod

    Neue Version mit ein paar zusätzlichen Kommentaren. Normaler C++ Code ab Zeile 760.



  • c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    

    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren

    Geht das auch bei Windows 7?

    Der Code ist jetzt etwas übersichtlicher 🙂
    Aber nur etwas. Darf ich meine Meinung kund tun:
    Ich finde es einfach nur schrecklich die öffnende Klammer { ans ende zu stellen und nicht in eine eigene Zeile. Und die funktionen ohne Leerzeilen aneinander gereeiht. Das mag vieleicht kompakt sein aber übersichtlich und lesbar nicht.


  • Mod

    booster schrieb:

    c:\Program Files\Windows Kits\10\Include\10.0.16299.0\um\VersionHelpers.h
    

    Evtl. musst du das Windows SDK noch mit dem Visual C++ Installer nachinstallieren

    Geht das auch bei Windows 7?

    Wieso meinst du, das wäre nicht möglich? Wie erstelltst du sonst Programme für Windows? Ich finde es eher seltsam, dass das Problem überhaupt auftritt. Der Include-Name deutet ja eher auf eine Datei hin, die schon immer in allen SDK-Versionen existiert hat, und das Vorhandensein irgendeiner Version ist nun mal essentiell.
    Habe testweise mal Windows SDK deinstalliert (ist ja eh eine Wegwerf-VM): Effekt ist, das das Verzeichnis verschwindet und der Compiler gleich mit einem fatalen Fehler aussteigt. Reinstallation per VS-Installer (Einzelkomponenten/SDKs, Bibliotheken und Frameworks) und alles funktioniert wieder.

    booster schrieb:

    Darf ich meine Meinung kund tun:
    Ich finde es einfach nur schrecklich die öffnende Klammer { ans ende zu stellen und nicht in eine eigene Zeile. Und die funktionen ohne Leerzeilen aneinander gereeiht. Das mag vieleicht kompakt sein aber übersichtlich und lesbar nicht.

    Darfst du, allerdings werde ich darauf nicht eingehen.
    Wir wissen alle, wie solche Diskussionen enden.
    Oder, wenn ich es mir recht überlege: nur komische Leute (Windowsprogrammierer) haben vertikal zuviel Platz 🙂



  • Darfst du, allerdings werde ich darauf nicht eingehen.
    Wir wissen alle, wie solche Diskussionen enden.
    Oder, wenn ich es mir recht überlege: nur komische Leute (Windowsprogrammierer) haben vertikal zuviel Platz 🙂

    😃 Schon klar dass du darauf nicht eingehst. War auch nicht meine Absicht.
    Und ich weiß nur die anderen komischen Leute (Linux Programmierer) haben horizontal zu viel Platz. Die würden am liebsten das ganze Programm in einer Zeile programmieren.

    Zurück zum Thema:

    Wieso meinst du, das wäre nicht möglich? Wie erstelltst du sonst Programme für Windows? Ich finde es eher seltsam, dass das Problem überhaupt auftritt.

    Ich meine natürlich das Windows 10 SDK...

    Ich habe folgendes Plattformtoolset eingestellt:
    Visual Studio 2017 - Windows XP (v141_xp)
    Weil wir noch XP Rechner unterstützen müssen.

    Als Windows SDK Version wird dann 7.0 ausgewählt. Dachte vielleicht ist die VersionHelpers.h da noch nicht definiert. Internet sagt aber gibt es schon seit VS 2013.

    Ich habe jetzt mal den Installer von VS2017 nochmals angeworfen. Dort kann man nur verschiedene Windows SDK in der Version 10 und 8.1 auswählen.
    Die sind aber installiert.


  • Mod

    In VersionHelpers.h scheint nichts für uns relevantes zu sein.
    Habe zum Test mal meine Version entfernt und einfach einen leeren Header VersionHelpers.h zum Projekt hinzugefügt, Projektverzeichnis zu zusätzlichen Includepfaden hinzugefügt (weil #include per <>), et voilà - kompiliert wieder. Das wäre also auch eine Möglichkeit.



  • - Die VersionHelpers.h habe ich nun zum Test einfach mal auskommentiert. Wie du sagst die braucht man nicht. Muss es dann auch so machen wie du sagtest.

    - Zu der Formatierung: Es gibt ja Resharper für c++. Der formatiert mir den Code auf Knopfdruck 😉

    - Hatte dich noch gefragt wieso du diesesmal die _impl.h weg gelassen hast. Und wo der Unterschied ist, den Code in eine _impl.h zu packen oder in die cpp in den unbenannten Namespace?

    - Was mich auch noch interessiert ist wieso die Ads.h und die TcAdsDef und TcAdsAPI.h viele doppelte definitionen beinhaltet.

    Da habe ich jetzt mal bei Beckhoff nachgefragt. Die Ordnerstruktur in der TwinCat 3.1 Installation ist auch noch mehr als verwirrend.



  • und noch ein Kompilierungsfehler:

    if (s[0] < '0' || s[0] > '9' || (std::numeric_limits<sizeType>::max() - (s[0] - '0')) / 10 < result)
    

    das max kennt er nicht;

    '(': illegal token on right side of '::'


Anmelden zum Antworten