首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用自定义数据类型输入参数调用方法时,open62541客户端将失败

使用自定义数据类型输入参数调用方法时,open62541客户端将失败
EN

Stack Overflow用户
提问于 2022-10-06 15:22:19
回答 1查看 84关注 0票数 1

我使用open62541连接到OPC/UA服务器,并试图调用该服务器上某个对象提供的方法。这些方法具有自定义类型作为输入参数;例如,下面的方法采用三个布尔结构:

代码语言:javascript
运行
复制
    <opc:Method SymbolicName="SetStatusMethodType" ModellingRule="Mandatory">
      <opc:InputArguments>
        <opc:Argument Name="Status" DataType="VisionStatusDataType" ValueRank="Scalar"/>
      </opc:InputArguments>
      <opc:OutputArguments />
    </opc:Method>

在这里,VisionStatusDataType是以下结构:

代码语言:javascript
运行
复制
  <opc:DataType SymbolicName="VisionStatusDataType" BaseType="ua:Structure">
    <opc:ClassName>VisionStatus</opc:ClassName>
    <opc:Fields>
      <opc:Field Name="Camera" DataType="ua:Boolean" ValueRank="Scalar"/>
      <opc:Field Name="StrobeController" DataType="ua:Boolean" ValueRank="Scalar"/>
      <opc:Field Name="Server" DataType="ua:Boolean" ValueRank="Scalar"/>
    </opc:Fields>
  </opc:DataType>

现在,在调用该方法时,我将数据编码为一个UA_ExtensionObject,并将该数据包装为一个UA_Variant,以便将其提供给UA_Client_call。编码方式如下:

代码语言:javascript
运行
复制
void encode(const QVariantList& vecqVar, size_t& nIdx, const DataType& dt, std::back_insert_iterator<std::vector<UAptr<UA_ByteString>>> itOut)
{
    if (dt.isSimple())
    {
        auto&& qVar = vecqVar.at(nIdx++);
        auto&& uaVar = convertToUaVar(qVar, dt.uaType());
        auto pOutBuf = create<UA_ByteString>();
        auto nStatus = UA_encodeBinary(uaVar.data, dt.uaType(), pOutBuf.get());
        statusCheck(nStatus);
        itOut = std::move(pOutBuf);
    }
    else
    {
        for (auto&& dtMember : dt.members())
            encode(vecqVar, nIdx, dtMember, itOut);
    }
}

UA_Variant ToUAVariant(const QVariant& qVar, const DataType& dt)
{
    if (dt.isSimple())
        return convertToUaVar(qVar, dt.uaType());
    else
    {
        std::vector<UAptr<UA_ByteString>> vecByteStr;
        auto&& qVarList = qVar.toList();
        size_t nIdx = 0UL;
        encode(qVarList, nIdx, dt, std::back_inserter(vecByteStr));

        auto pExtObj = UA_ExtensionObject_new();
        pExtObj->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
        auto nSizeAll = std::accumulate(vecByteStr.cbegin(), vecByteStr.cend(), 0ULL, [](size_t nSize, const UAptr<UA_ByteString>& pByteStr) {
            return nSize + pByteStr->length;
        });
        auto&& uaEncoded = pExtObj->content.encoded;
        uaEncoded.typeId = dt.uaType()->typeId;
        uaEncoded.body.length = nSizeAll;
        auto pData = uaEncoded.body.data = new UA_Byte[nSizeAll];
        nIdx = 0UL;
        for (auto&& pByteStr : vecByteStr)
        {
            memcpy_s(pData + nIdx, nSizeAll - nIdx, pByteStr->data, pByteStr->length);
            nIdx += pByteStr->length;
        }

        UA_Variant uaVar;
        UA_Variant_init(&uaVar);
        UA_Variant_setScalar(&uaVar, pExtObj, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
        return uaVar;
    }
}

DataType类是UA_DataType结构的包装器;可以通过DataType::uaType()访问原始open62541类型。

现在,一旦有了变量(包含扩展对象),方法调用如下所示:

代码语言:javascript
运行
复制
    auto uavarInput = ToUAVariant(qvarArg, dtInput);
    UA_Variant* pvarOut;
    size_t nOutSize = 0UL;
    auto nStatus = UA_Client_call(m_pClient, objNode.nodeId(), m_uaNodeId, 1UL, &uavarInput, &nOutSize, &pvarOut);

状态为2158690304,即根据BadInvalidArgumentUA_StatusCode_name

方法论证真的有什么问题吗?我们应该发送ExtensionObjects,还是应该包含哪些数据类型?服务器本身(使用.NET OPC/UA堆栈创建)是否可能没有正确配置?

注意,这里的类型是自定义类型;也就是说,编码是手动完成的(参见上文),方法是在UA_ByteString中存储彼此相邻的所有成员的字节表示形式--这与我读取变量或输出参数时所做的正好相反,后者工作得很好。

EN

Stack Overflow用户

回答已采纳

发布于 2022-10-07 14:35:17

问题是编码对象的typeId。对于服务器来说,为了理解接收到的数据,它需要知道编码的NodeId,而不是类型本身的实际NodeId。可以通过以下类型的HasEncoding引用(名为“默认二进制”)找到这种编码:

代码语言:javascript
运行
复制
        auto pRequest = create<UA_BrowseRequest>();
        auto pDescr = pRequest->nodesToBrowse = UA_BrowseDescription_new();
        pRequest->nodesToBrowseSize = 1UL;
        pDescr->nodeId = m_uaNodeId;
        pDescr->resultMask = UA_BROWSERESULTMASK_ALL;
        pDescr->browseDirection = UA_BROWSEDIRECTION_BOTH;
        pDescr->referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASENCODING);

        auto response = UA_Client_Service_browse(m_pClient, *pRequest);
        for (auto k = 0UL; k < response.resultsSize; ++k)
        {
            auto browseRes = response.results[k];
            for (auto n = 0UL; n < browseRes.referencesSize; ++n)
            {
                auto browseRef = browseRes.references[n];
                if (ToQString(browseRef.browseName.name).contains("Binary"))
                {
                    m_nodeBinaryEnc = browseRef.nodeId.nodeId;
                    break;
                }
            }
        }

一旦你有了那个NodeId,你就把它传递给UA_ExtensionObject::content::encoded::typeId

代码语言:javascript
运行
复制
        auto pExtObj = UA_ExtensionObject_new();
        pExtObj->encoding = UA_EXTENSIONOBJECT_ENCODED_BYTESTRING;
        auto nSizeAll = std::accumulate(vecByteStr.cbegin(), vecByteStr.cend(), 0ULL, [](size_t nSize, const UAptr<UA_ByteString>& pByteStr) {
            return nSize + pByteStr->length;
        });
        auto&& uaEncoded = pExtObj->content.encoded;
        uaEncoded.typeId = dt.encoding();
        uaEncoded.body.length = nSizeAll;
        auto pData = uaEncoded.body.data = new UA_Byte[nSizeAll];
        nIdx = 0UL;
        for (auto&& pByteStr : vecByteStr)
        {
            memcpy_s(pData + nIdx, nSizeAll - nIdx, pByteStr->data, pByteStr->length);
            nIdx += pByteStr->length;
        }
票数 1
EN
查看全部 1 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73976312

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档