High Performance OPC UA Server SDK
1.7.1.383
|
The SDK implements the UA FileType as defined by "Annex C File Transfer" - "OPC UA Specification - Part 5: Information Model".
The purpose of the FileType is to be able to transfer file contents, that might be exceeding the maximum message size of UA by adding methods which allow to read and write parts of files. In addition it provides functionality for locking files and read file meta data. See specification for more details on the FileType itself.
To understand the SDK architecture of the FileType you must first understand that the FileType that is visible via OPC UA address space is independent from the technical filetype used in the code. So it is possible that you have multiple instances of the same UA FileType, which use different file implementations. So one instance might be connected with a real filesystem file, another instance might be an in-memory file.
To achieve this the SDK defines a ua_filetype_interface, which is a calltable to the actual file implementation, and a ua_file_context, which represents one instance of a real file. This ua_file_context holds file meta data as well as the ua_filetype_interface to use for this instance. The UA node in the address space, which represents a file, is connected to such a ua_file_context. The type ua_file_handle represents and opened file with its meta data like e.g. the file position.
The SDK infrastructure takes care of handling UA file method invocations, locking and cleanup of file resources. E.g. if a session gets deleted or a file is not used for a configurable time (file_timeout), then the file gets closed automatically and the associated file handle gets removed.
The SDK provides two default implementations of a FileType.
SUPPORT_FILE_IO
, also the ua_filesystem_implementation SUPPORT_FILE_IO depends on this option and is unavailable if this option was disabled.The function ua_filetype_create_ex does two things in one step. It creates the instance of the specified file type (UA_NODE_FILETYPE
in this example), and it creates a underlying file context and connects both.
Creating the instance in address space works the same way is ua_instance_new.
Code snippet from the demoprovider:
Creating an in-memory file works essentially the same as the example above, with the following differences:
The following example uses ua_filetype_create instead of ua_filetype_create_ex to demonstrate this. This is just a convenience wrapper function, so that you don't need to create the ua_filetype_ctx yourself. This function is sufficient for most cases, but if you need control over the created NodeIds you must use ua_filetype_create_ex and register the according callbacks.
The previous examples have shown how to create FileType instances dynamically. But it may be necessary to connect existing nodes with a ua_file_context. This is the case when loading information models from file which contain already such FileType nodes.
In this case you can use the function ua_filetype_create_with_existing_node as shown in the following example.
The full example code can be found in examples/server_demo/demoprovider/uaprovider_demo_file.c
.
Using File Transfer with nodes from static address spaces is possible too, but a little bit more complicated. You need to know some implementation details of FileTransfer to fully understand how this works.
Issues:
Luckily, we have solutions for these issues as well:
src/uaserver/filetransfer/assoarray.h
and change the value of the define UA_MAX_FILETYPE_ASSO_ENTRIES
. Normally, you don't need many FileType instances so the default value is 10.For this to work you need to know that the ua_filestore implementation uses the reserved store-id 244 (see define UA_VALUESTORE_IDX_FILETYPESTORE
in src/uaserver/valuestore/valuestore.h
). An example of such an address mapping can be found in the file examples/server_demo/addrmapping.txt
and is shown below:
With this in mind the same code of the example above works also for static address spaces as demonstrated by our DemoServer.
If you need other kinds of files you can add your own implementation of the file interface. All you need to do is creating an instance of ua_filetype_interface and set the function pointers of your implementation. Then use this calltable in ua_filetype_create like for the examples above.
The args
argument of ua_filetype_create_ex will be forwarded to the create
function of ua_filetype_interface. This way you can pass implementation specific data to your implementation. This private implementation details can be stored in the pimpl
field of the ua_file_context. So this is available in all the function calls.
To understand this it's best to look at the existing two implementations which are shown in the UML diagrams below.