Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
# The ARCH can be overridden by providing it a value in the command line
# with the "--build-arg=XXX" argument fed to 'docker build'.
ARG ARCH=aarch64
ARG VERSION=1.14
ARG UBUNTU_VERSION=22.04
ARG VERSION=12.7.0
ARG UBUNTU_VERSION=24.04
ARG REPO=axisecp
ARG SDK=acap-native-sdk
ARG BUILD_DIR=/opt/app
Expand Down
94 changes: 70 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
# OPC-UA Plugin Server ACAP application

This repository contains the source code for building an OPC-UA Server ACAP application,
along with a guide on developing and building custom modules/plugins. You can
create your own plugins to address specific use cases. Two example plugins
are provided and described below. Additional plugins will be published in
the future designed to enhance production value.
along with a guide on developing and building custom modules/plugins.
You can create your own plugins to address specific use cases. Example
plugins are provided and briefly described below, for more information about a
plugin see its readme.

The OPC-UA Server is based on the [**open62541**](https://www.open62541.org/)
library. For an introduction to OPC-UA, see
Expand All @@ -26,6 +26,7 @@ library. For an introduction to OPC-UA, see
- [Install](#install)
- [Setup](#setup)
- [Usage](#usage)
- [Available plugins](#available-plugins)
- [Create your own plugin](#create-your-own-plugin)
- [Limitations](#limitations)
- [Security](#security)
Expand Down Expand Up @@ -256,16 +257,21 @@ options are not available.

To connect to the server right click on the name and click **Connect** or click
on the **Connect Server** icon in the tool bar. If the connection is
established, the server will present its OPC-UA information as shown in the
established, the server will present its OPC-UA information similar to the
picture below.

![UAExpert](assets/UAExpert_InfoModel.png)

There is one *object node* called **BasicDeviceInfo** with *property nodes*
presenting different information about the active device. This is implemented in
the **plugins/bdi** module. The **plugins/hello_world** module is responsible
for creating a *variable node*, called **HelloWorldNode** with the string value
"Hello World!".
#### Available Plugins

This application includes the source code for several plugins, which serve both as functional components and as examples for custom development. The currently available plugins are:

- The `bdi` plugin (`plugins/bdi`) provides a **BasicDeviceInfo** object node with device-specific properties (See [readme](app/plugins/bdi/README.md) for details).
- The `hello_world` plugin (`plugins/hello_world`) creates a **HelloWorldNode** variable (See [readme](app/plugins/hello_world/README.md) for details).
- The `ioports` plugin (`plugins/ioports`) exposes I/O port status and control (See [readme](app/plugins/ioports/README.md) for details).
- The `simple_event` plugin (`plugins/simple_event`) demonstrates Axis event integration via OPC-UA events (See [readme](app/plugins/simple_event/README.md) for details).
- The `thermal` plugin (`plugins/thermal`) exposes thermal camera data and controls (See [readme](app/plugins/thermal/README.md) for details).
- The `vinput` plugin (`plugins/vinput`) provides control over virtual inputs (See [readme](app/plugins/vinput/README.md) for details).

## Create your own plugin

Expand All @@ -276,13 +282,18 @@ GModule APIs.
Create a new directory under *app/plugins/* and copy a Makefile from the example
plugin. Each plugin will need its own Makefile.

The directory structure looks like this,
where **`your_plugin`** represents the new plugin you would create:

```text
opc-ua-plugin-server
├── app
│   ├── include
│   │   ├── error.h
│   │   ├── log.h
│   │   └── plugin.h
│   │   ├── plugin.h
│   │   ├── ua_utils.h
│   │   └── vapix_utils.h
│   ├── LICENSE
│   ├── Makefile
│   ├── manifest.json
Expand All @@ -293,19 +304,54 @@ opc-ua-plugin-server
│   ├── opcua_server.c
│   ├── opcua_server.h
│   ├── plugin.c
│   └── plugins
│   ├── bdi
│   │   ├── bdi_plugin.c
│   │   ├── bdi_plugin.h
│   │   └── Makefile
│   ├── hello_world
│   │ ├── hello_world_plugin.c
│   │ ├── hello_world_plugin.h
│   │ └── Makefile
│   └── your_plugin
│   ├── your_plugin.c
│   ├── your_plugin.h
│   └── Makefile
│   ├── plugins
│   │   ├── bdi
│   │   │   ├── bdi_plugin.c
│   │   │   ├── bdi_plugin.h
│   │   │   ├── Makefile
│   │   │   └── README.md
│   │   ├── hello_world
│   │   │   ├── hello_world_plugin.c
│   │   │   ├── hello_world_plugin.h
│   │   │   ├── Makefile
│   │   │   └── README.md
│   │   ├── ioports
│   │   │   ├── ioports_nodeids.h
│   │   │   ├── ioports_ns.c
│   │   │   ├── ioports_ns.h
│   │   │   ├── ioports_plugin.c
│   │   │   ├── ioports_plugin.h
│   │   │   ├── ioports_types.c
│   │   │   ├── ioports_types.h
│   │   │   ├── ioports_vapix.c
│   │   │   ├── ioports_vapix.h
│   │   │   ├── Makefile
│   │   │   └── README.md
│   │   ├── simple_event
│   │   │   ├── Makefile
│   │   │   ├── README.md
│   │   │   ├── simple_event_plugin.c
│   │   │   └── simple_event_plugin.h
│   │   ├── thermal
│   │   │   ├── Makefile
│   │   │   ├── README.md
│   │   │   ├── thermal_plugin.c
│   │   │   ├── thermal_plugin.h
│   │   │   ├── thermal_vapix.c
│   │   │   └── thermal_vapix.h
│   │   ├── vinput
│   │   │   ├── Makefile
│   │   │   ├── README.md
│   │   │   ├── vinput_plugin.c
│   │   │   ├── vinput_plugin.h
│   │   │   ├── vinput_vapix.c
│   │   │   └── vinput_vapix.h
│   │   └── your_plugin
│   │   ├── Makefile
│   │   ├── your_plugin.c
│   │   └── your_plugin.h
│   ├── ua_utils.c
│   └── vapix_utils.c
├── assets
├── CODEOWNERS
├── CONTRIBUTING.md
Expand Down
2 changes: 1 addition & 1 deletion app/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
DEPS = $(patsubst %.c,%.d,$(SRCS))

PKGS = gio-2.0 glib-2.0 gio-unix-2.0 gmodule-2.0 axparameter
PKGS = gio-2.0 glib-2.0 gio-unix-2.0 gmodule-2.0 libcurl axparameter
CFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --cflags $(PKGS))
LDLIBS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --libs $(PKGS))

Expand Down
132 changes: 132 additions & 0 deletions app/include/ua_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/**
* MIT License
*
* Copyright (c) 2025 Axis Communications AB
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef __UA_UTILS_H__
#define __UA_UTILS_H__

#include <glib.h>
#include <open62541/types.h>

typedef struct rollback_data {
/* used to save the existing 'customDataTypes' of the server configuration
* (struct UA_ServerConfig) */
const UA_DataTypeArray *saved_cdt;

/* a list of 'struct UA_NodeId' that have been added to the server */
GList *node_ids;
} rollback_data_t;

/* Performs a 'deep' free() to deallocate the 'rollback_data_t' structure with
* its associated 'node_ids' list */
void
ua_utils_clear_rbd(rollback_data_t **rbd);

/* Loops over the rbd->node_ids list in reverse order and deletes the nodes
* from the information model.
* NOTE: the list is populated by pre-pending, traversing it in forward
* direction will visit the nodes in the reverse order of their addition.
* IMPORTANT: This can only be called before the server thread gets started
* as it can change the server configuration. */
gboolean
ua_utils_do_rollback(UA_Server *server, rollback_data_t *rbd, GError **err);

/* wrapper around the open62541 UA_Server_addObjectNode()
* If underlying UA_Server_addObjectNode() succeeds it also adds the nodeId to
* the rbd (rollback data) */
UA_StatusCode
UA_Server_addObjectNode_rb(UA_Server *server,
const UA_NodeId requestedNewNodeId,
const UA_NodeId parentNodeId,
const UA_NodeId referenceTypeId,
const UA_QualifiedName browseName,
const UA_NodeId typeDefinition,
const UA_ObjectAttributes attr,
void *nodeContext,
rollback_data_t *rbd,
UA_NodeId *outNewNodeId);

/* wrapper around the open62541 UA_Server_addDataTypeNode()
* If underlying UA_Server_addDataTypeNode() succeeds it also adds the nodeId to
* the rbd (rollback data) */
UA_StatusCode
UA_Server_addDataTypeNode_rb(UA_Server *server,
const UA_NodeId requestedNewNodeId,
const UA_NodeId parentNodeId,
const UA_NodeId referenceTypeId,
const UA_QualifiedName browseName,
const UA_DataTypeAttributes attr,
void *nodeContext,
rollback_data_t *rbd,
UA_NodeId *outNewNodeId);

/* wrapper around the open62541 UA_Server_addVariableNode()
* If underlying UA_Server_addVariableNode() succeeds it also adds the nodeId to
* the rbd (rollback data) */
UA_StatusCode
UA_Server_addVariableNode_rb(UA_Server *server,
const UA_NodeId requestedNewNodeId,
const UA_NodeId parentNodeId,
const UA_NodeId referenceTypeId,
const UA_QualifiedName browseName,
const UA_NodeId typeDefinition,
const UA_VariableAttributes attr,
void *nodeContext,
rollback_data_t *rbd,
UA_NodeId *outNewNodeId);

/* wrapper around the open62541 UA_Server_addObjectTypeNode()
* If underlying UA_Server_addObjectTypeNode() succeeds it also adds the nodeId
* to the rbd (rollback data) */
UA_StatusCode
UA_Server_addObjectTypeNode_rb(UA_Server *server,
const UA_NodeId requestedNewNodeId,
const UA_NodeId parentNodeId,
const UA_NodeId referenceTypeId,
const UA_QualifiedName browseName,
const UA_ObjectTypeAttributes attr,
void *nodeContext,
rollback_data_t *rbd,
UA_NodeId *outNewNodeId);

/* wrapper around the open62541 UA_Server_addMethodNode()
* If underlying UA_Server_addMethodNode() succeeds it also adds the nodeId
* to the rbd (rollback data) */
UA_StatusCode
UA_Server_addMethodNode_rb(UA_Server *server,
const UA_NodeId requestedNewNodeId,
const UA_NodeId parentNodeId,
const UA_NodeId referenceTypeId,
const UA_QualifiedName browseName,
const UA_MethodAttributes attr,
UA_MethodCallback method,
size_t inputArgumentsSize,
const UA_Argument *inputArguments,
size_t outputArgumentsSize,
const UA_Argument *outputArguments,
void *nodeContext,
rollback_data_t *rbd,
UA_NodeId *outNewNodeId);

#endif /* __UA_UTILS_H__ */
78 changes: 78 additions & 0 deletions app/include/vapix_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* MIT License
*
* Copyright (c) 2025 Axis Communications AB
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef __VAPIX_UTILS_H__
#define __VAPIX_UTILS_H__

#include <curl/curl.h>
#include <glib.h>

/* HTTP request methods we use for VAPIX APIs */
typedef enum {
HTTP_GET,
HTTP_POST,
} HTTP_req_method_t;

typedef enum { NONE_data, XML_data, JSON_data } HTTP_media_t;

/**
* vapix_get_credentials:
* @username: a user name which will be used to get credentials for to perform
* VAPIX calls
* @err: return location for a #GError
*
* Returns the required credentials for @username to perform VAPIX calls.
*
* Returns: a newly-allocated string holding the credentials on success, NULL
* if @err is set.
*/
gchar *
vapix_get_credentials(const gchar *username, GError **err);

/**
* vapix_request:
* @handle: a cURL handle obtained with curl_easy_init()
* @credentials: credentials string obtained via vapix_get_credentials()
* @endpoint: the endpoint part of the VAPIX API
* @req_type: HTTP_GET or HTTP_POST
* @post_req: NULL if @req_type is HTTP_GET or a string holding the POST data
* if @req_type is HTTP_POST
* @err: return location for a #GError
*
* Performs a VAPIX API request.
*
* Returns: a newly-allocated string holding the VAPIX response on success, NULL
* if @err is set.
*/
gchar *
vapix_request(CURL *handle,
const gchar *credentials,
const gchar *endpoint,
HTTP_req_method_t req_type,
HTTP_media_t media_type,
const gchar *post_req,
GError **err);

#endif /* __VAPIX_UTILS_H__ */
5 changes: 5 additions & 0 deletions app/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
"name": "Port",
"type": "int:min=1024,max=65535",
"default": "4840"
},
{
"name": "ExtendLogs",
"type": "bool:no,yes",
"default": "no"
}
]
}
Expand Down
4 changes: 3 additions & 1 deletion app/opcua_open62541.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ ua_server_init(app_context_t *ctx,
/* Adjust the logging level for the server thread to be in sync with the
* logging level set for the ACAP via the 'LogLevel' configuration parameter.
*/
config->logging->context = (void *) log_level;
if (ctx->extend_logs) {
config->logging->context = (void *) log_level;
}

/* Name of the server */
UA_String_clear(&config->applicationDescription.applicationName.text);
Expand Down
Loading