nodeid.cpp
Go to the documentation of this file.
00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 #include "binary_serialization.h"
00012 
00013 #include <opc/ua/protocol/binary/stream.h>
00014 #include <opc/ua/protocol/nodeid.h>
00015 
00016 #include <algorithm>
00017 #include <stdexcept>
00018 #include <iostream>
00019 
00020 namespace OpcUa
00021 {
00022 
00023 
00024   NodeId::NodeId(uint32_t integerId, uint16_t index)
00025   {
00026     Encoding = EV_NUMERIC;
00027     NumericData.Identifier = integerId;
00028     NumericData.NamespaceIndex = index;
00029   }
00030 
00031   NodeId::NodeId(std::string stringId, uint16_t index)
00032   {
00033     Encoding = EV_STRING;
00034     StringData.Identifier = stringId;
00035     StringData.NamespaceIndex = index;
00036   }
00037 
00038   bool NodeId::IsInteger() const
00039   {
00040     const NodeIdEncoding enc = GetEncodingValue();
00041     return enc == EV_TWO_BYTE || enc == EV_FOUR_BYTE || enc == EV_NUMERIC;
00042   }
00043 
00044   bool NodeId::IsString() const
00045   {
00046     const NodeIdEncoding enc = GetEncodingValue();
00047     return enc == EV_STRING;
00048   }
00049 
00050   bool NodeId::IsBinary() const
00051   {
00052     const NodeIdEncoding enc = GetEncodingValue();
00053     return enc == EV_BYTE_STRING;
00054   }
00055 
00056   bool NodeId::IsGuid() const
00057   {
00058     const NodeIdEncoding enc = GetEncodingValue();
00059     return enc == EV_GUId;
00060   }
00061 
00062   std::string NodeId::GetStringIdentifier() const
00063   {
00064     if (IsString())
00065     {
00066       return StringData.Identifier;
00067     }
00068     throw std::logic_error("Node id is not in String format.");
00069   }
00070 
00071   std::vector<uint8_t> NodeId::GetBinaryIdentifier() const
00072   {
00073     if (IsBinary())
00074     {
00075       return BinaryData.Identifier;
00076     }
00077     throw std::logic_error("Node id is not in String format.");
00078   }
00079 
00080   Guid NodeId::GetGuidIdentifier() const
00081   {
00082     if (IsGuid())
00083     {
00084       return GuidData.Identifier;
00085     }
00086     throw std::logic_error("Node id is not in String format.");
00087   }
00088 
00089   uint32_t NodeId::GetIntegerIdentifier() const
00090   {
00091     switch (GetEncodingValue())
00092     {
00093       case EV_TWO_BYTE:
00094       {
00095         return TwoByteData.Identifier;
00096       }
00097       case EV_FOUR_BYTE:
00098       {
00099         return FourByteData.Identifier;
00100       }
00101       case EV_NUMERIC:
00102       {
00103         return NumericData.Identifier;
00104       }
00105       default:
00106       {
00107         throw std::logic_error("Cannot get integer identifier from NodeId - it is not in numeric format.");
00108       }
00109     }
00110   }
00111 
00112   uint32_t NodeId::GetNamespaceIndex() const
00113   {
00114     switch (GetEncodingValue())
00115     {
00116       case EV_FOUR_BYTE:
00117         return FourByteData.NamespaceIndex;
00118       case EV_NUMERIC:
00119         return NumericData.NamespaceIndex;
00120       case EV_STRING:
00121         return StringData.NamespaceIndex;
00122       case EV_GUId:
00123         return GuidData.NamespaceIndex;
00124       case EV_BYTE_STRING:
00125         return BinaryData.NamespaceIndex;
00126       default:
00127         return 0;
00128     }
00129   }
00130 
00131   void NodeId::SetNamespaceIndex(uint32_t ns)
00132   {
00133     switch (GetEncodingValue())
00134     {
00135       case EV_FOUR_BYTE:
00136         FourByteData.NamespaceIndex = ns;
00137         return;
00138       case EV_NUMERIC:
00139         NumericData.NamespaceIndex = ns;
00140         return;
00141       case EV_STRING:
00142         StringData.NamespaceIndex = ns;
00143         return;
00144       case EV_GUId:
00145         GuidData.NamespaceIndex = ns;
00146         return;
00147       case EV_BYTE_STRING:
00148         BinaryData.NamespaceIndex = ns;
00149         return;
00150       default:
00151         return;
00152     }
00153   }
00154 
00155   NodeId::NodeId()
00156     : Encoding(EV_TWO_BYTE)
00157     , ServerIndex(0)
00158   {
00159   }
00160 
00161   void NodeId::CopyNodeId(const NodeId& node)
00162   {
00163     Encoding = node.Encoding;
00164     const NodeIdEncoding enc = node.GetEncodingValue();
00165     switch (enc)
00166     {
00167       case EV_TWO_BYTE:
00168       {
00169         TwoByteData.Identifier = node.TwoByteData.Identifier;
00170         break;
00171       }
00172       case EV_FOUR_BYTE:
00173       {
00174         FourByteData.NamespaceIndex = node.FourByteData.NamespaceIndex;
00175         FourByteData.Identifier = node.FourByteData.Identifier;
00176         break;
00177       }
00178       case EV_NUMERIC:
00179       {
00180         NumericData.NamespaceIndex = node.NumericData.NamespaceIndex;
00181         NumericData.Identifier = node.NumericData.Identifier;
00182         break;
00183       }
00184       case EV_STRING:
00185       {
00186         StringData.NamespaceIndex = node.StringData.NamespaceIndex;
00187         StringData.Identifier = node.StringData.Identifier;
00188         break;
00189       }
00190       case EV_GUId:
00191       {
00192         GuidData.NamespaceIndex = node.GuidData.NamespaceIndex;
00193         GuidData.Identifier = node.GuidData.Identifier;
00194         break;
00195       }
00196       case EV_BYTE_STRING:
00197       {
00198         BinaryData.NamespaceIndex = node.BinaryData.NamespaceIndex;
00199         BinaryData.Identifier = node.BinaryData.Identifier;
00200         break;
00201       }
00202       default:
00203       {
00204         throw std::logic_error("Invalid Node Id encoding value.");
00205       }
00206     }
00207 
00208     if (node.HasServerIndex())
00209     {
00210       ServerIndex = node.ServerIndex;
00211     }
00212     if (node.HasNamespaceURI())
00213     {
00214       NamespaceURI = node.NamespaceURI;
00215     }
00216 
00217   }
00218 
00219   NodeId::NodeId(const NodeId& node)
00220   {
00221     CopyNodeId(node);
00222   }
00223 
00224   NodeId::NodeId(const ExpandedNodeId& node)
00225   {
00226     CopyNodeId(node);
00227   }
00228 
00229   NodeId::operator ExpandedNodeId()
00230   {
00231     ExpandedNodeId node;
00232     node.CopyNodeId(*this);
00233 
00234     return node;
00235   }
00236 
00237   NodeId& NodeId::operator=(const NodeId& node)
00238   {
00239     CopyNodeId(node);
00240     return *this;
00241   }
00242 
00243   NodeId& NodeId::operator=(const ExpandedNodeId& node)
00244   {
00245     CopyNodeId(node);
00246     return *this;
00247   }
00248 
00249   NodeId::NodeId(MessageId messageId)
00250     : Encoding(EV_FOUR_BYTE)
00251     , ServerIndex(0)
00252   {
00253     FourByteData.Identifier = messageId;
00254   }
00255 
00256   NodeId::NodeId(ReferenceId referenceId)
00257     : Encoding(EV_NUMERIC)
00258     , ServerIndex(0)
00259   {
00260     NumericData.Identifier = static_cast<uint32_t>(referenceId);
00261   }
00262 
00263   NodeId::NodeId(ObjectId objectId)
00264     : Encoding(EV_NUMERIC)
00265     , ServerIndex(0)
00266   {
00267     NumericData.Identifier = static_cast<uint32_t>(objectId);
00268   }
00269 
00270   NodeId::NodeId(ExpandedObjectId objectId)
00271     : Encoding(EV_FOUR_BYTE)
00272     , ServerIndex(0)
00273   {
00274     FourByteData.Identifier = static_cast<uint32_t>(objectId);
00275   }
00276 
00277   MessageId GetMessageId(const NodeId& id)
00278   {
00279     return static_cast<MessageId>(id.GetIntegerIdentifier());
00280   }
00281 
00282 
00283   bool NodeId::operator== (const NodeId& node) const
00284   {
00285     if (GetNamespaceIndex() != node.GetNamespaceIndex())
00286     {
00287       return false;
00288     }
00289     if (IsInteger() && node.IsInteger())
00290     {
00291       return GetIntegerIdentifier() == node.GetIntegerIdentifier();
00292     }
00293     if (IsString() && node.IsString())
00294     {
00295       return GetStringIdentifier() == node.GetStringIdentifier();
00296     }
00297     if (IsBinary() && node.IsBinary())
00298     {
00299       return GetBinaryIdentifier() == node.GetBinaryIdentifier();
00300     }
00301     if (IsGuid() && node.IsGuid())
00302     {
00303       return GetGuidIdentifier() == node.GetGuidIdentifier();
00304     }
00305     return false;
00306   }
00307 
00308   bool NodeId::operator < (const NodeId& node) const
00309   {
00310     if (GetNamespaceIndex() != node.GetNamespaceIndex())
00311     {
00312       return GetNamespaceIndex() < node.GetNamespaceIndex();
00313     }
00314     if (IsInteger() && node.IsInteger())
00315     {
00316       return GetIntegerIdentifier() < node.GetIntegerIdentifier();
00317     }
00318     if (IsString() && node.IsString())
00319     {
00320       return GetStringIdentifier() < node.GetStringIdentifier();
00321     }
00322     if (IsBinary() && node.IsBinary())
00323     {
00324       const std::vector<uint8_t>& l = GetBinaryIdentifier();
00325       const std::vector<uint8_t>& r = node.GetBinaryIdentifier();
00326       return std::lexicographical_compare(l.cbegin(), l.cend(), r.cbegin(), r.cend());
00327     }
00328     if (IsGuid() && node.IsGuid())
00329     {
00330       return GetGuidIdentifier() < node.GetGuidIdentifier();
00331     }
00332     return Encoding < node.Encoding; //FIXME Can we get there? and should we?
00333 
00334   }
00335 
00336   NodeIdEncoding NodeId::GetEncodingValue() const
00337   {
00338     return static_cast<NodeIdEncoding>(Encoding & EV_VALUE_MASK);
00339   }
00340 
00341   bool NodeId::IsNull() const
00342   {
00343     switch (GetEncodingValue())
00344     {
00345       case EV_FOUR_BYTE:
00346         if (FourByteData.NamespaceIndex != 0)
00347           return false;
00348         break;
00349       case EV_NUMERIC:
00350         if (NumericData.NamespaceIndex != 0)
00351           return false;
00352         break;
00353       case EV_STRING:
00354         if (StringData.NamespaceIndex != 0)
00355           return false;
00356         break;
00357       case EV_GUId:
00358         if (GuidData.NamespaceIndex != 0)
00359           return false;
00360         break;
00361       case EV_BYTE_STRING:
00362         if (BinaryData.NamespaceIndex != 0)
00363           return false;
00364         break;
00365       default:
00366       {
00367         throw std::logic_error("Invalid Node Id encoding value.");
00368       }
00369     }
00370     return HasNullIdentifier();
00371   }
00372 
00373   bool NodeId::HasNullIdentifier() const
00374   {
00375     switch (GetEncodingValue())
00376     {
00377       case EV_FOUR_BYTE:
00378         if (FourByteData.Identifier != 0)
00379           return false;
00380         break;
00381       case EV_NUMERIC:
00382         if (NumericData.Identifier != 0)
00383           return false;
00384         break;
00385       case EV_STRING:
00386         if (not StringData.Identifier.empty())
00387           return false;
00388         break;
00389       case EV_GUId:
00390         if (not (GuidData.Identifier == Guid()))
00391           return false;
00392         break;
00393       case EV_BYTE_STRING:
00394         if (not BinaryData.Identifier.empty())
00395           return false;
00396         break;
00397       default:
00398       {
00399         throw std::logic_error("Invalid Node Id encoding value.");
00400       }
00401     }
00402     return true;
00403   }
00404 
00405   bool NodeId::HasNamespaceURI() const
00406   {
00407     return (Encoding & EV_NAMESPACE_URI_FLAG) != 0;
00408   }
00409 
00410   bool NodeId::HasServerIndex() const
00411   {
00412     return (Encoding & EV_Server_INDEX_FLAG) != 0;
00413   }
00414 
00415   void NodeId::SetNamespaceURI(const std::string& uri)
00416   {
00417     Encoding = static_cast<NodeIdEncoding>(Encoding | EV_NAMESPACE_URI_FLAG);
00418     NamespaceURI = uri;
00419   }
00420 
00421   void NodeId::SetServerIndex(uint32_t index)
00422   {
00423     Encoding = static_cast<NodeIdEncoding>(Encoding | EV_Server_INDEX_FLAG);
00424     ServerIndex = index;
00425   }
00426 
00427   bool NodeId::operator!= (const NodeId& node) const
00428   {
00429     return !(*this == node);
00430   }
00431 
00432   bool NodeId::operator!= (MessageId messageId) const
00433   {
00434     return !(*this == messageId);
00435   }
00436 
00437   bool NodeId::operator!= (ReferenceId referenceId) const
00438   {
00439     return !(*this == referenceId);
00440   }
00441 
00442   bool NodeId::operator!= (ObjectId objectId) const
00443   {
00444     return !(*this == objectId);
00445   }
00446 
00447   bool NodeId::operator!= (ExpandedObjectId objectId) const
00448   {
00449     return !(*this == objectId);
00450   }
00451 
00452 
00453 
00454   bool NodeId::operator== (MessageId messageId) const
00455   {
00456     return *this == NodeId(messageId);
00457   }
00458 
00459   bool NodeId::operator== (ReferenceId referenceId) const
00460   {
00461     return *this == NodeId(referenceId);
00462   }
00463 
00464   bool NodeId::operator== (ObjectId messageId) const
00465   {
00466     return *this == NodeId(messageId);
00467   }
00468 
00469   bool NodeId::operator== (ExpandedObjectId messageId) const
00470   {
00471     return *this == NodeId(messageId);
00472   }
00473 
00474 
00476   ExpandedNodeId::ExpandedNodeId()
00477   {
00478     Encoding = EV_TWO_BYTE;
00479     ServerIndex = 0;
00480   }
00481 
00482   ExpandedNodeId::ExpandedNodeId(uint32_t integerId, uint16_t index)
00483   {
00484     Encoding = EV_NUMERIC;
00485     NumericData.Identifier = integerId;
00486     NumericData.NamespaceIndex = index;
00487   }
00488 
00489   ExpandedNodeId::ExpandedNodeId(std::string stringId, uint16_t index)
00490   {
00491     Encoding = EV_STRING;
00492     StringData.Identifier = stringId;
00493     StringData.NamespaceIndex = index;
00494   }
00495 
00496   ExpandedNodeId::ExpandedNodeId(const NodeId& node)
00497   {
00498     CopyNodeId(node);
00499   }
00500 
00501   ExpandedNodeId::ExpandedNodeId(const ExpandedNodeId& node)
00502   {
00503     CopyNodeId(node);
00504   }
00505 
00506 
00507   ExpandedNodeId::ExpandedNodeId(MessageId messageId)
00508   {
00509     Encoding = EV_FOUR_BYTE;
00510     ServerIndex = 0;
00511     FourByteData.Identifier = messageId;
00512   }
00513 
00514   ExpandedNodeId::ExpandedNodeId(ReferenceId referenceId)
00515   {
00516     Encoding = EV_NUMERIC;
00517     ServerIndex = 0;
00518     NumericData.Identifier = static_cast<uint32_t>(referenceId);
00519   }
00520 
00521   ExpandedNodeId::ExpandedNodeId(ObjectId objectId)
00522   {
00523     Encoding = EV_NUMERIC;
00524     ServerIndex = 0;
00525     NumericData.Identifier = static_cast<uint32_t>(objectId);
00526   }
00527 
00528   ExpandedNodeId::ExpandedNodeId(ExpandedObjectId objectId)
00529   {
00530     Encoding = EV_FOUR_BYTE;
00531     ServerIndex = 0;
00532     FourByteData.Identifier = static_cast<uint32_t>(objectId);
00533   }
00534 
00535 
00536   namespace Binary
00537   {
00538     template<>
00539     std::size_t RawSize<NodeIdEncoding>(const NodeIdEncoding&)
00540     {
00541       return 1;
00542     }
00543 
00544     template<>
00545     void DataSerializer::Serialize<NodeIdEncoding>(const NodeIdEncoding& value)
00546     {
00547       *this << static_cast<uint8_t>(value);
00548     }
00549 
00550     template<>
00551     void DataDeserializer::Deserialize<NodeIdEncoding>(NodeIdEncoding& value)
00552     {
00553       uint8_t tmp = 0;
00554       *this >> tmp;
00555       value = static_cast<NodeIdEncoding>(tmp);
00556     }
00557 
00558     template<>
00559     std::size_t RawSize<NodeId>(const NodeId& id)
00560     {
00561       std::size_t size = 0;
00562 
00563       switch (id.GetEncodingValue())
00564       {
00565         case EV_TWO_BYTE:
00566         {
00567           size = 2;
00568           break;
00569         }
00570         case EV_FOUR_BYTE:
00571         {
00572           size = 4;
00573           break;
00574         }
00575         case EV_NUMERIC:
00576         {
00577           const std::size_t sizeofEncoding = 1;
00578           const std::size_t sizeofNamespace = 2;
00579           const std::size_t sizeofIdentifier = 4;
00580           size = sizeofEncoding + sizeofNamespace + sizeofIdentifier;
00581           break;
00582         }
00583         case EV_STRING:
00584         {
00585           const std::size_t sizeofEncoding = 1;
00586           const std::size_t sizeofSize = 4;
00587           const std::size_t sizeofNamespace = 2;
00588           size = sizeofEncoding + sizeofNamespace + sizeofSize + id.StringData.Identifier.size();
00589           break;
00590         }
00591         case EV_BYTE_STRING:
00592         {
00593           const std::size_t sizeofEncoding = 1;
00594           const std::size_t sizeofSize = 4;
00595           const std::size_t sizeofNamespace = 2;
00596           size = sizeofEncoding + sizeofNamespace + sizeofSize + id.BinaryData.Identifier.size();
00597           break;
00598         }
00599         case EV_GUId:
00600         {
00601           const std::size_t sizeofEncoding = 1;
00602           const std::size_t sizeofNamespace = 2;
00603           const std::size_t sizeofGuid = 16;
00604           size = sizeofEncoding + sizeofNamespace + sizeofGuid;
00605           break;
00606         }
00607 
00608       default:
00609         throw std::logic_error("Unable serialize NodeId. Unknown encoding type.");
00610       };
00611 
00612       return size;
00613     }
00614 
00615     template<>
00616     void DataSerializer::Serialize<OpcUa::NodeId>(const OpcUa::NodeId& id)
00617     {
00618       //unset server and namespace flags in encoding, they should only be used in ExpandedNode Id
00619       uint8_t nodeid_encoding = id.Encoding;
00620       nodeid_encoding &= ~EV_Server_INDEX_FLAG;
00621       nodeid_encoding &= ~EV_NAMESPACE_URI_FLAG;
00622 
00623       *this << nodeid_encoding;
00624 
00625       switch (id.GetEncodingValue())
00626       {
00627         case EV_TWO_BYTE:
00628         {
00629           *this << id.TwoByteData.Identifier;
00630           break;
00631         }
00632         case EV_FOUR_BYTE:
00633         {
00634           *this << id.FourByteData.NamespaceIndex;
00635           *this << id.FourByteData.Identifier;
00636           break;
00637         }
00638         case EV_NUMERIC:
00639         {
00640           *this << id.NumericData.NamespaceIndex;
00641           *this << id.NumericData.Identifier;
00642           break;
00643         }
00644         case EV_STRING:
00645         {
00646           *this << id.StringData.NamespaceIndex;
00647           *this << id.StringData.Identifier;
00648           break;
00649         }
00650         case EV_BYTE_STRING:
00651         {
00652           *this << id.BinaryData.NamespaceIndex;
00653           *this << id.BinaryData.Identifier;
00654           break;
00655         }
00656         case EV_GUId:
00657         {
00658           *this << id.GuidData.NamespaceIndex;
00659           *this << id.GuidData.Identifier;
00660           break;
00661         }
00662 
00663       default:
00664         throw std::logic_error("Unable serialize NodeId. Unknown encoding type.");
00665       };
00666 
00667     }
00668 
00669     template<>
00670     void DataDeserializer::Deserialize<OpcUa::NodeId>(OpcUa::NodeId& id)
00671     {
00672       *this >> id.Encoding;
00673 
00674       switch (id.GetEncodingValue())
00675       {
00676         case EV_TWO_BYTE:
00677         {
00678           *this >> id.TwoByteData.Identifier;
00679           break;
00680         }
00681         case EV_FOUR_BYTE:
00682         {
00683           *this >> id.FourByteData.NamespaceIndex;
00684           *this >> id.FourByteData.Identifier;
00685           break;
00686         }
00687         case EV_NUMERIC:
00688         {
00689           *this >> id.NumericData.NamespaceIndex;
00690           *this >> id.NumericData.Identifier;
00691           break;
00692         }
00693         case EV_STRING:
00694         {
00695           *this >> id.StringData.NamespaceIndex;
00696           *this >> id.StringData.Identifier;
00697           break;
00698         }
00699         case EV_BYTE_STRING:
00700         {
00701           *this >> id.BinaryData.NamespaceIndex;
00702           *this >> id.BinaryData.Identifier;
00703           break;
00704         }
00705         case EV_GUId:
00706         {
00707           *this >> id.GuidData.NamespaceIndex;
00708           *this >> id.GuidData.Identifier;
00709           break;
00710         }
00711 
00712         default:
00713         {
00714           throw std::logic_error("Unable to deserialize NodeId. Unknown encoding type received.");
00715         }
00716       };
00717 
00718       if (id.HasNamespaceURI())
00719       {
00720         *this >> id.NamespaceURI;
00721       }
00722       if (id.HasServerIndex())
00723       {
00724         *this >> id.ServerIndex;
00725       }
00726     };
00727 
00728     template<>
00729     std::size_t RawSize<ExpandedNodeId>(const ExpandedNodeId& id)
00730     {
00731       std::size_t size = RawSize((NodeId)id);
00732 
00733       if (id.HasNamespaceURI())
00734       {
00735         const std::size_t sizeofSize = 4;
00736         size += sizeofSize + id.NamespaceURI.size();
00737       }
00738       if (id.HasServerIndex())
00739       {
00740         const std::size_t sizeofServerIndex = 4;
00741         size += sizeofServerIndex;
00742       }
00743       return size;
00744     }
00745 
00746     template<>
00747     void DataSerializer::Serialize<OpcUa::ExpandedNodeId>(const OpcUa::ExpandedNodeId& id)
00748     {
00749       *this << id.Encoding;
00750 
00751       switch (id.GetEncodingValue())
00752       {
00753         case EV_TWO_BYTE:
00754         {
00755           *this << id.TwoByteData.Identifier;
00756           break;
00757         }
00758         case EV_FOUR_BYTE:
00759         {
00760           *this << id.FourByteData.NamespaceIndex;
00761           *this << id.FourByteData.Identifier;
00762           break;
00763         }
00764         case EV_NUMERIC:
00765         {
00766           *this << id.NumericData.NamespaceIndex;
00767           *this << id.NumericData.Identifier;
00768           break;
00769         }
00770         case EV_STRING:
00771         {
00772           *this << id.StringData.NamespaceIndex;
00773           *this << id.StringData.Identifier;
00774           break;
00775         }
00776         case EV_BYTE_STRING:
00777         {
00778           *this << id.BinaryData.NamespaceIndex;
00779           *this << id.BinaryData.Identifier;
00780           break;
00781         }
00782         case EV_GUId:
00783         {
00784           *this << id.GuidData.NamespaceIndex;
00785           *this << id.GuidData.Identifier;
00786           break;
00787         }
00788 
00789       default:
00790         throw std::logic_error("Unable serialize ExpandedNodeId. Unknown encoding type.");
00791       };
00792 
00793       if (id.HasNamespaceURI())
00794       {
00795         *this << id.NamespaceURI;
00796       }
00797       if (id.HasServerIndex())
00798       {
00799         *this << id.ServerIndex;
00800       }
00801     }
00802 
00803     template<>
00804     void DataDeserializer::Deserialize<OpcUa::ExpandedNodeId>(OpcUa::ExpandedNodeId& id)
00805     {
00806       *this >> *(NodeId*) &id;
00807     };
00808 
00809 
00810   } // namespace Binary
00811 } // namespace OpcUa
00812 


ros_opcua_impl_freeopcua
Author(s): Denis Štogl
autogenerated on Sat Jun 8 2019 18:24:56