Skip to content

NPE when processing open62541.h on Windows 7 (32 bit) #569

Open

Description

@point85

There is an open source implementation of OPC UA in written C (http://open62541.org). The single header file called open62541.h (version 0.1 is available for download from their website) throws an NPE in Result.java (215) - see attached console output. The UA_WorkItem struct was being processed.
jnaerator_npe

I believe that the NPE is related to the empty UA_Server struct, because when I provide a dummy implementation of it (struct UA_Server { int i; };) in a header file that includes open62541.h, the NPE disappears.

Here's my config.jnaerator file:

-library Open62541
open62541.h
-mode Directory
-o .
-v
-runtime JNA
-arch win32
-emptyStructsAsForwardDecls

Note that I am using Microsoft Visual Studio 12 and Windows SDK version 7.1A.

Activity

  1. bcopy commented on Nov 4, 2015

    @bcopy

    @billran : Hi, I'm curious to know whether your JNA mappings are working out for you. You can look me up on Linkedin or at CERN.

  2. point85 commented on Nov 4, 2015

    @point85
    Author

    Yes, I do have a java client working with release 0.1.1 for read/write and browse. The design involved working around restrictions imposed by jnaerator, primarily that structs
    cannot be passed by value, so all wrapper functions use pointers for arguments and return values.

    The major steps are to:
    (1) Write wrapper functions in C that expose just the functionality of interest (e.g. connect, disconnect, read, write, browse). These functions follow a design that the side that
    allocates memory is responsible for freeing it (which might require copying the data to pass to java). These C functions are in wrapper62541.h and wrapper62541.c. For example, to connect to
    the server, first create the client, connect and then read, write or browse:

    UA_EXPORT UA_Client *createClient();
    UA_EXPORT UA_StatusCode connectClient(UA_Client *client, char *endpointUrl);
    UA_EXPORT UA_ReadResponse *synchRead(UA_Client *client, UA_NodeId *nodeId);
    UA_EXPORT UA_WriteResponse *synchWrite(UA_Client *client, UA_NodeId *nodeId, UA_Variant *value);
    UA_EXPORT UA_BrowseResponse *browseNode(UA_Client *client, UA_NodeId *nodeId);

    UA_Client *createClient() {
    UA_Client *client = UA_Client_new(UA_ClientConfig_standard,
    Logger_Stdout_new());
    return client;
    }

    UA_StatusCode connectClient(UA_Client *client, char *endpointUrl) {
    UA_StatusCode retval = UA_Client_connect(client,
    ClientNetworkLayerTCP_connect, endpointUrl);
    return retval;
    }

    UA_ReadResponse *synchRead(UA_Client *client, UA_NodeId *nodeId) {
    UA_ReadRequest rReq;
    UA_ReadRequest_init(&rReq);
    rReq.nodesToRead = UA_ReadValueId_new();
    rReq.nodesToReadSize = 1;
    rReq.nodesToRead[0].nodeId = *nodeId;
    rReq.nodesToRead[0].attributeId = UA_ATTRIBUTEID_VALUE;

    UA_ReadResponse rResp = UA_Client_read(client, &rReq);
    
    UA_ReadResponse *reply = UA_ReadResponse_new();
    UA_ReadResponse_copy(&rResp, reply);
    
    UA_ReadRequest_deleteMembers(&rReq);
    UA_ReadResponse_deleteMembers(&rResp);
    
    return reply;
    

    }

    UA_WriteResponse *synchWrite(UA_Client *client, UA_NodeId *nodeId,
    UA_Variant *value) {

    UA_UInt16 typeIndex = value->type->typeIndex;
    void *pValue = value->data;
    
    UA_WriteRequest wReq;
    UA_WriteRequest_init(&wReq);
    wReq.nodesToWrite = UA_WriteValue_new();
    wReq.nodesToWriteSize = 1;
    wReq.nodesToWrite[0].nodeId = *nodeId;
    wReq.nodesToWrite[0].attributeId = UA_ATTRIBUTEID_VALUE;
    
    wReq.nodesToWrite[0].value.hasValue = UA_TRUE;
    wReq.nodesToWrite[0].value.value.type = &UA_TYPES[typeIndex];
    wReq.nodesToWrite[0].value.value.storageType = UA_VARIANT_DATA_NODELETE; //do not free the integer on deletion
    wReq.nodesToWrite[0].value.value.data = pValue;
    
    UA_WriteResponse wResp = UA_Client_write(client, &wReq);
    
    UA_WriteResponse *reply = UA_WriteResponse_new();
    UA_WriteResponse_copy(&wResp, reply);
    
    UA_WriteRequest_deleteMembers(&wReq);
    UA_WriteResponse_deleteMembers(&wResp);
    
    return reply;
    

    }

    UA_BrowseResponse *browseNode(UA_Client *client, UA_NodeId *nodeId) {

    UA_BrowseRequest bReq;
    UA_BrowseRequest_init(&bReq);
    bReq.requestedMaxReferencesPerNode = 0;
    bReq.nodesToBrowse = UA_BrowseDescription_new();
    bReq.nodesToBrowseSize = 1;
    bReq.nodesToBrowse[0].nodeId = *nodeId;
    bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
    
    UA_BrowseResponse bResp = UA_Client_browse(client, &bReq);
    
    UA_BrowseResponse *reply = UA_BrowseResponse_new();
    UA_BrowseResponse_copy(&bResp, reply);
    
    UA_BrowseRequest_deleteMembers(&bReq);
    UA_BrowseResponse_deleteMembers(&bResp);
    
    return reply;
    

    }

    (2) Build a dll from the Open62541 source that also includes these functions (I used Eclipse CDT IDE and MinGW C tool chain).

    (3) Run jnaerator with Bridj interface with the wrapper62541.h input file (which also includes open62541.h). Here's the input file:

    // library name to match dll name
    -library Open62541Ext
    // source header file
    wrapper62541.h
    // output folder
    -o .
    // verbose
    -v
    // individual output files
    -mode Directory
    // Windows 32 bit
    -arch win32
    // use JNA or BridJ interface
    -runtime BridJ
    // skip deprecated functions
    -skipDeprecated
    // WIN32 pre-processor flag
    -D_WIN32=
    // force file overwrite
    -f
    // java package name
    -package

    (4) In java I wrote UaClient, UaNode, UaNodeId and UaUtils classes that call into these JNA generated classes. If interested, I can attach the entire source as well as some testing code.
    For example, some UaClient's methods are:

    public class UaClient {

    // wrapped client
    private Pointer<UA_Client> client;
    
    // connection flag
    boolean isConnected = false;
    
    public UaClient() {
        // create client
        client = Open62541ExtLibrary.createClient();
    }
    
    public synchronized void connect(String serverUrl) throws Exception {
        Pointer<Byte> endpointUrl = Pointer
                .pointerToBytes(serverUrl.getBytes());
    
        IntValuedEnum<UA_StatusCode> status = Open62541ExtLibrary
                .connectClient(client, endpointUrl);
    
        if (status != UA_StatusCode.UA_STATUSCODE_GOOD) {
            throw new Exception("Bad connect status: "
                    + UaUtils.convertStatusToHexCode(status));
        }
    
        isConnected = true;
    }
    
        // node must have name space index and identifier set
    public synchronized void read(UaNode node) throws Exception {
    
        if (node == null) {
            return;
        }
        Pointer<UA_NodeId> nodeToRead = UaUtils.uaNodeIdFromNode(node);
    
        Pointer<UA_ReadResponse> response = Open62541ExtLibrary.synchRead(
                client, nodeToRead);
    
        UA_Variant variant = getVariant(response);
        node.setVariantData(variant);
    
        // TODO: variant should be deleted too
        Open62541ExtLibrary.deleteReadResponse(response);
    }
    
        public synchronized void browse(UaNode parentNode) throws Exception {
        if (parentNode == null) {
            return;
        }
    
        short nodeNSIndex = parentNode.getMainNodeId().getNamespaceIndex();
        IntValuedEnum<identifierType_enum> typeEnum = parentNode
                .getMainNodeId().getTypeEnum();
    
        Pointer<UA_NodeId> parentNodeId = null;
        Pointer<UA_String> pStringId = null;
    
        // numeric node
        if (typeEnum
                .equals(UA_NodeId.identifierType_enum.UA_NODEIDTYPE_NUMERIC)) {
            int numericId = parentNode.getMainNodeId().getNumericIdentifier();
            parentNodeId = Open62541ExtLibrary.createNumericNodeId(nodeNSIndex,
                    numericId);
        }
    
        // string node
        else if (typeEnum
                .equals(UA_NodeId.identifierType_enum.UA_NODEIDTYPE_STRING)) {
            String stringId = parentNode.getMainNodeId().getStringIdentifier();
            pStringId = UaUtils.uaStringFromString(stringId);
            parentNodeId = Open62541ExtLibrary.createStringNodeId(nodeNSIndex,
                    pStringId);
    
        } else {
            throw new Exception("Unsupported node type: " + typeEnum);
        }
    
        Pointer<UA_BrowseResponse> browseResponse = Open62541ExtLibrary
                .browseNode(client, parentNodeId);
        UA_BrowseResponse response = browseResponse.get();
        UA_ResponseHeader responseHeader = response.responseHeader();
    
        if (responseHeader.serviceResult() != UA_StatusCode.UA_STATUSCODE_GOOD) {
            throw new Exception("Bad browsed status code: "
                    + responseHeader.serviceResult());
        }
    
        Pointer<UA_BrowseResult> pResult = response.results();
        int bound = response.resultsSize();
    
        // iterator has an unknown bound, so keep a counter
        int loopCount = 0;
    
        for (UA_BrowseResult browseResult : pResult) {
            if (loopCount == bound) {
                break;
            }
    
            loopCount++;
    
            int innerBound = browseResult.referencesSize();
            Pointer<UA_ReferenceDescription> pRef = browseResult.references();
    
            if (pRef == null) {
                continue;
            }
    
            inspectReferenceDescription(pRef, innerBound, parentNode);
        }
    
        if (pStringId != null) {
            pStringId.release();
        }
    
        return;
    }
    
        public synchronized void write(UaNode node, Object value) throws Exception {
        if (node == null || value == null) {
            return;
        }
        Pointer<UA_Variant> valueToWrite = null;
    
        if (value instanceof Boolean) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_BOOLEAN,
                    Pointer.pointerToBoolean((Boolean) value));
    
        } else if (value instanceof Byte) {
    
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_BYTE,
                    Pointer.pointerToByte((Byte) value));
    
        } else if (value instanceof Short) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_INT16,
                    Pointer.pointerToShort((Short) value));
    
        } else if (value instanceof Integer) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_INT32,
                    Pointer.pointerToInt((Integer) value));
    
        } else if (value instanceof Long) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_INT64,
                    Pointer.pointerToLong((Long) value));
    
        } else if (value instanceof Float) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_FLOAT,
                    Pointer.pointerToFloat((Float) value));
    
        } else if (value instanceof Double) {
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_DOUBLE,
                    Pointer.pointerToDouble((Double) value));
    
        } else if (value instanceof String) {
            Pointer<UA_String> uaString = UaUtils
                    .uaStringFromString((String) value);
    
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_STRING, uaString);
    
        } else if (value instanceof ZonedDateTime) {
            long uaDateTime = UaUtils.uaDateTimeFromTime((ZonedDateTime) value);
            valueToWrite = Open62541ExtLibrary.createScalarVariant(
                    (short) Open62541ExtLibrary.UA_TYPES_DATETIME,
                    Pointer.pointerToLong(uaDateTime));
        } else {
            System.out.println("Unrecognized object class"
                    + value.getClass().getSimpleName());
            return;
        }
    
        Pointer<UA_NodeId> nodeToWrite = UaUtils.uaNodeIdFromNode(node);
    
        Pointer<UA_WriteResponse> response = Open62541ExtLibrary.synchWrite(
                client, nodeToWrite, valueToWrite);
    
        UA_ResponseHeader responseHeader = response.get().responseHeader();
    
        if (responseHeader.serviceResult() != UA_StatusCode.UA_STATUSCODE_GOOD) {
            throw new Exception("Bad status code on write: "
                    + UaUtils.convertStatusToHexCode(responseHeader
                            .serviceResult()));
        }
    
        node.setVariantData(valueToWrite.get());
    
        Open62541ExtLibrary.deleteWriteResponse(response);
        Open62541ExtLibrary.deleteVariant(valueToWrite);
    }
    ...
    }
    
    These methods are called like this:
    
        UaClient uaClient = new UaClient();
        String serverUrl = "opc.tcp://localhost:16664";
        uaClient.connect(serverUrl);
    
        short nsIndex = 1;
        String id = "the.answer";
    
        // write
        UaNode node = new UaNode(nsIndex, id);
        uaClient.write(node, value);
        System.out.println("Wrote to node: " + node);
    
        // read
        uaClient.read(node);
        System.out.println("Read node: " + node);
    
        // browse single node
        short nsIndex = 1;
        int numericId = -1;
        String stringId = "the.answer";
    
        UaNode topNode = null;
    
        // read it first to get the value and data type
        if (numericId != -1) {
            topNode = new UaNode(nsIndex, numericId);
        } else {
            topNode = new UaNode(nsIndex, stringId);
        }
    
        uaClient.read(topNode);
        System.out.println("Browsing parent node:" + topNode);
        uaClient.browse(topNode);
    
        List<UaNode> childNodes = topNode.getChildren();
    
        for (UaNode childNode : childNodes) {
            System.out.println("   Browsed child node: " + childNode);
        }
    
  3. bcopy commented on Nov 5, 2015

    @bcopy
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions