High Performance OPC UA Server SDK  1.5.0.296
File Transfer

The SDK implements the UA FileType as defined by "Annex C File Transfer" - "OPC UA Specification - Part 5: Information Model".

Background

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.

Creating File Instances

The SDK provides two default implementations of a FileType.

  1. ua_filesystem_implementation : This uses real files and implements access to those files using the platform layer (PL) file API. This means, if your PL provides a working file API, then also ua_filesystem_implementation is working. Because this API depends on the option SUPPORT_FILE_IO, also the ua_filesystem_implementation SUPPORT_FILE_IO depends on this option and is unavailable if this option was disabled.
  2. ua_memoryfile_implementation : Represents an in-memory file based on a ua_bytestring.

Creating a FileSystem File Instance

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.

  1. Create a ua_filetype_ctx instance using ua_filetype_ctx_create, which is used to control the instance creation. Therefor you can register callbacks at this context (ua_filetype_ctx_set*cb), and you can set the file's MIME type using ua_filetype_ctx_set_mime_type.
  2. You call ua_filetype_create_ex and specify the file's new NodeId, its type and its implementation calltable. Depending on the used implementation you must specify additional data as the last argument. For the filesystem implementation this is the filename (const char*).
  3. You can get the node handle of the new instance by calling ua_filetype_ctx_get_new_node
  4. You cleanup the ua_filetype_ctx by calling ua_filetype_ctx_delete.
  5. You can add a reference to this node by using ua_reference_add.
  6. Optionally you can register callbacks to get notifications when a file is opened or closed. This may be useful if you need to do something after a file was updated.

Code snippet from the demoprovider:

/* create nodeid for file instance */
ret = ua_nodeid_attach_string_const(&id, g_uaprovider_demo_dynamic_nsidx, "testfile");
if (ret != UA_EGOOD) goto error;
/* create filetype context for ua_filetype_create_ex invocation */
if (ctx == NULL) goto error;
/* optionally, set the file's MIME type */
ua_filetype_ctx_set_mime_type(ctx, "application/text");
/* Create the file instance. */
ret = ua_filetype_create_ex(ctx, &id, UA_NODE_FILETYPE, 0, NULL, "Testfile", &ua_filesystem_implementation, UA_DEMOPROVIDER_TESTFILE);
if (ret != UA_EGOOD) {
TRACE_ERROR(TRACE_FAC_PROVIDER, "Creation of filetype for Testfile failed.\n");
goto error;
}
/* get the newly create node handle of the file */
/* cleanup */
/* reference node from existing folde node */
ref = ua_reference_add(folder_node, g_file, UA_NODE_ORGANIZES);
if (ref == UA_REF_INVALID) {
ret = UA_EBAD;
TRACE_ERROR(TRACE_FAC_PROVIDER, "Creation of reference to 014_Files folder failed.\n");
goto error;
}
/* register callbacks to get notifications for file changes */
ret = ua_filetype_set_callbacks(g_file, &cb);
if (ret != 0) {
TRACE_ERROR(TRACE_FAC_PROVIDER, "ua_filetype_set_callbacks failed.\n");
goto error;
}

Creating a Memory File Instance

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.

/* Create bytestring for in-memory file. */
ret = ua_bytestring_create(&g_memoryfiledata, 1024);
if (ret < 0) {
TRACE_ERROR(TRACE_FAC_PROVIDER, "Bytestring generation for memory file failed.\n");
goto error;
}
/* initialize bytestring with zeros */
ua_bytestring_clear_data(&g_memoryfiledata);
/* add some test data */
ua_memcpy(g_memoryfiledata.data, "Hello World", 11);
/* Set the string node id for "memoryfile". */
ret = ua_nodeid_attach_string_const(&id, g_uaprovider_demo_dynamic_nsidx, "memoryfile");
if (ret != UA_EGOOD) {
goto error;
}
/* Create the memory file. */
g_memoryfile = ua_filetype_create(&id, UA_NODE_FILETYPE, "Memoryfile", &ua_memoryfile_implementation, &g_memoryfiledata);
if (g_memoryfile == UA_NODE_INVALID) {
ret = UA_EBAD;
TRACE_ERROR(TRACE_FAC_PROVIDER, "Creation memory file failed.\n");
goto error;
}
/* cleanup */
ref = ua_reference_add(folder_node, g_memoryfile, UA_NODE_ORGANIZES);
if (ref == UA_REF_INVALID) {
ret = UA_EBAD;
TRACE_ERROR(TRACE_FAC_PROVIDER, "Creation of reference to 014_Files folder failed.\n");
goto error;
}
ret = ua_filetype_set_callbacks(g_memoryfile, &cb);

Adding new File Implementations

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.

ua_nodeid_attach_string_const
int ua_nodeid_attach_string_const(struct ua_nodeid *id, uint16_t nsindex, const char *value)
Attach a constant string value and set a namespace index for a nodeid.
Definition: nodeid.c:192
ua_reference_add
ua_ref_t ua_reference_add(ua_node_t src, ua_node_t dst, ua_node_t reftype)
Adds a new reference to the given src node.
Definition: reference.c:139
ua_filetype_ctx_set_mime_type
void ua_filetype_ctx_set_mime_type(struct ua_filetype_ctx *ctx, const char *mimetype)
Sets the optional MIME type of the file that should be created.
Definition: filetype.c:97
ua_memcpy
#define ua_memcpy(dest, src, n)
Copy n bytes from the memory area src to dest.
Definition: memory.h:97
TRACE_ERROR
#define TRACE_ERROR(facility, format,...)
Convenience define for tracing with error level.
Definition: trace.h:329
ua_bytestring::ua_bytestring_clear_data
void ua_bytestring_clear_data(struct ua_bytestring *bs)
Clears the contained data by setting all bytes to zero.
Definition: bytestring.c:83
ua_filetype_ctx_create
struct ua_filetype_ctx * ua_filetype_ctx_create(void)
Creates new instance of a ua_filetype_ctx, which servers the same purpose as ua_instance_ctx,...
Definition: filetype.c:73
UA_NODE_INVALID
#define UA_NODE_INVALID
Value of an invalid node handle.
Definition: node.h:55
ua_filetype_create
ua_node_t ua_filetype_create(const struct ua_nodeid *nodeid, ua_node_t type, const char *displayname, struct ua_filetype_interface *ftif, const void *args)
Creates an instance of a file type.
Definition: filetype.c:305
ua_memoryfile_implementation
struct ua_filetype_interface ua_memoryfile_implementation
filetype interface table for in-memory files.
Definition: memoryfileimpl.c:36
ua_filetype_set_callbacks
int ua_filetype_set_callbacks(ua_node_t file, struct ua_filetype_callbacks *callbacks)
Sets user callbacks for the given filetype instance.
Definition: filetype.c:1155
UA_REF_INVALID
#define UA_REF_INVALID
Value of an invalid reference handle.
Definition: reference.h:44
ua_filetype_ctx_get_new_node
ua_node_t ua_filetype_ctx_get_new_node(struct ua_filetype_ctx *ctx)
Returns the newly create filetype node.
Definition: filetype.c:111
ua_filetype_create_ex
int ua_filetype_create_ex(struct ua_filetype_ctx *ctx, const struct ua_nodeid *nodeid, ua_node_t type, uint16_t browsename_idx, const char *browsename, const char *displayname, struct ua_filetype_interface *ftif, const void *args)
Creates an instance of a file type.
Definition: filetype.c:347
ua_nodeid_clear
void ua_nodeid_clear(struct ua_nodeid *id)
Cleans all resources in id and makes it a null nodeid.
Definition: nodeid.c:51
ua_filesystem_implementation
struct ua_filetype_interface ua_filesystem_implementation
filetype interface table for real filesystem files.
Definition: filesystemimpl.c:51
ua_filetype_ctx_delete
void ua_filetype_ctx_delete(struct ua_filetype_ctx *ctx)
Deletes the context created with ua_filetype_ctx_create().
Definition: filetype.c:105
TRACE_FAC_PROVIDER
#define TRACE_FAC_PROVIDER
Identifier for provider facility.
Definition: trace.h:109