Speicherverbrauch shared_ptr und normaler *
-
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
-
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.
-
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ündeHier trotzdem die beiden Besipiele.
das ist das "statische" Beipiel:
https://infosys.beckhoff.de/content/1031/tcsample_vc/html/tcadsdll_api_cpp_sample10.htm?id=3746003215985854275und das hier das "dynamische"
https://infosys.beckhoff.de/content/1031/tcsample_vc/html/tcadsdll_api_cpp_sample16.htm?id=3653485731247715096
-
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.
-
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.
-
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?
-
booster schrieb:
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'
Die Präprozessorsymbole müssen bereits existieren, bevor irgendwelche Standardheader eingebunden werden. Also am Besten am Anfang eines vorkompilierten Headers, wenn dein Projekt so etwas benutzt, oder gleich als Kommandozeilenparameter beim Compileraufruf.
booster schrieb:
AdsSymbolUploadInfo2
Bei mir bereits in TcAdsDEF.h definiert.
booster schrieb:
und wieso liegt die class declaration "adsData" im Header nicht im namespace ads?
Warum sollte sie?
-
->
also in die stdafx.h?
oder bei Visual Studio bei den Eigenschaften Preprocessor Definitions.
Da habe ich es auf jeden Fall hin gepackt.->
AdsSymbolUploadInfo2 in TcAdsDEF.h ? dann hast du das aber gemacht. Bei mir ist das nicht drin.->
ach stimmt dachte deine implementierungs sei im namespace ads nur der header nicht. Aber ist ja beides nicht im namespace ads.
-
booster schrieb:
AdsSymbolUploadInfo2 in TcAdsDEF.h ? dann hast du das aber gemacht. Bei mir ist das nicht drin.
Dann ist deine TwinCAT Version vermutlich veraltet. In 2.11.2300 und 3.1.4022.16 ist es jedenfalls drin (Dateidatum 30.05.2016 10:25)
-
Mir ist da nun noch was anderes aufgefallen. Wenn ich mit TwinCat2 teste oder auch mit einer TwincatVersion 3.1.4020.x steigt mein Speicher auch mit dem Code von Beckhoff selber auf maximal 400k
Also irgendwas muss sich in der neusten TwinCatVersion geändert haben.
Aktuell versuche ich aber noch deinen Code in meine Klassenstruktur zu bringen und meinen Code c++ 17 fähig zu machen. Da klemmts aber noch an ein paar stellen und stelle hier im Forum die ein oder andere Frage!
-
Nun bin ich endlich dazu gekommen dein Beispiel bei mir einzubinden und zu kompilieren.
Nun habe ich folgendes festgestellt. Und zwar liefert mir adsData::operator[](std::string name) das objekt adsVarData.
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; };
das stellt aber nur die Basisinformationen der Variablen bereit.
Ich benötige hier aber natürlich auch die Informationen zu den SubSymbols.
Mit den Informationen die ich hier erhalte füttere ich eine FactoryKlasse die in Abhängikeit von adsDatatypeId und typename objekte erzeugt.
class VarFactory { shared_ptr<AdsVariable> CreateInstance(const string& name) { shared_ptr<AdsVariable> var; auto data = _adsData[name]; switch (data.type) { case AdsDatatypeId::ADST_BIT: var = make_shared<AdsBooleanVar>(data); break; case AdsVariable::ADST_STRUCT: // der einfach halber hier so -> struct selber nicht direkt über AdsDatatypeId identifizierbar var = make_shared<AdsStructVar>(data); break; } } } class AdsStructVar { explicit AdsStructVar(adsVarData) { CreateSubVars(adsVarData.SubVars()); } }
erzeuge ich hier nur eine "Boolean" Variable genügt es nur die Basisinformationen zu haben. (hier gibt es auch nicht mehr)
erzeuge ich aber eine Structur Variable. muss ich hierzu auch alle Subvariablen erzeugen. Die Information hatte ich zuvor zur Verfügung in deiner adsVarData fehlt diese Information.