Skip to content

Commit

Permalink
Add functionality that adds value to dynamic string (#42)
Browse files Browse the repository at this point in the history
This means e.g. overlay widget gauges (in a camera with support for
that) can display the digital value on screen. Along with this update,
we also move to the current version of native ACAP 4 SDK for building
and hence drop support for legacy versions of Axis OS.

For legacy devices, the previous commit can still be successfully used,
only that it (at this point) will not have the data written to the
dynamic string.

Signed-off-by: Joakim Roubert <[email protected]>
  • Loading branch information
joakimr-axis authored Sep 10, 2024
1 parent a39d8b4 commit c0390e5
Show file tree
Hide file tree
Showing 15 changed files with 286 additions and 38 deletions.
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
ARG ARCH=aarch64
ARG ACAP_SDK_VERSION=3.5
ARG SDK_IMAGE=axisecp/acap-sdk
ARG SDK_VERSION=1.15
ARG SDK_IMAGE=axisecp/acap-native-sdk
ARG DEBUG_WRITE
ARG BUILD_DIR=/opt/build
ARG ACAP_BUILD_DIR="$BUILD_DIR"/app
ARG OPEN62541_VERSION=1.2.9
ARG OPENCV_VERSION=4.5.5

FROM $SDK_IMAGE:$ACAP_SDK_VERSION-$ARCH-ubuntu20.04 AS builder
FROM $SDK_IMAGE:$SDK_VERSION-$ARCH AS builder

# Set general arguments
ARG ARCH
Expand Down Expand Up @@ -105,7 +105,7 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -L https://github.com/open62541/open62541/archive/refs/tags/v$OPEN62541_VERSION.tar.gz | tar xz
WORKDIR "$OPEN62541_BUILD_DIR"
RUN . /opt/axis/acapsdk/environment-setup* && \
cmake -j \
cmake \
-DCMAKE_INSTALL_PREFIX="$SDKTARGETSYSROOT"/usr \
-DBUILD_BUILD_EXAMPLES=OFF \
-DBUILD_SHARED_LIBS=ON \
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [2022] [Axis Communications AB]
Copyright [2024] [Axis Communications AB]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ TARGET = opcuagaugereader
OBJECTS = $(wildcard $(CURDIR)/src/*.cpp)
RM ?= rm -f

PKGS = gio-2.0 gio-unix-2.0 vdostream open62541 axparameter
PKGS = gio-2.0 gio-unix-2.0 vdostream open62541 libcurl axparameter

CXXFLAGS += -Os -pipe -std=c++11 -Wall -Werror -Wextra
CXXFLAGS += $(shell PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) pkg-config --cflags-only-I $(PKGS))
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*Copyright (C) 2022, Axis Communications AB, Lund, Sweden. All Rights Reserved.*
*Copyright (C) 2024, Axis Communications AB, Lund, Sweden. All Rights Reserved.*

# OPC UA Gauge Reader ACAP

Expand Down Expand Up @@ -162,6 +162,7 @@ curl -k --anyauth -u root:<password> \
will list the current settings:

```sh
root.Opcuagaugereader.DynamicStringNumber=1
root.Opcuagaugereader.centerX=479
root.Opcuagaugereader.centerY=355
root.Opcuagaugereader.clockwise=1
Expand All @@ -187,6 +188,15 @@ to read the value (and its timestamp) from the application's OPC UA server.
> [!NOTE]
> The application will also log the gauge value in the camera's syslog.
### Bonus

In addition to the above, the application will write the extracted gauge
reading as a
[dynamic text overlay](https://www.axis.com/vapix-library/subjects/t10175981/section/t10007638/display?section=t10007638-t10003585)
string. It can then be displayed as camera text overlay&mdash;or used by graph
widgets&mdash;with the modifier **#D***N*, where *N* is set by the application
parameter `DynamicStringNumber`.

## License

[Apache 2.0](LICENSE)
2 changes: 1 addition & 1 deletion include/common.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
45 changes: 45 additions & 0 deletions include/dynstrhandler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#pragma once

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

/**
* brief A type for handling setting dynamic text overlay string via VAPIX.
*
* This is not needed for OPC UA, but enables the camera to use the extracted
* gauge reading in overlays, which can add value to the live view.
*/
class DynStrHandler
{
public:
DynStrHandler(const guint8 nbr);
~DynStrHandler();
void SetStrNumber(const guint8 newnbr);
void UpdateStr(const double value);

private:
std::string RetrieveVapixCredentials(const char &username) const;
gboolean VapixGet(const std::string &url);

CURL *curl;
guint8 nbr;
std::chrono::time_point<std::chrono::steady_clock> lastupdate;
};
2 changes: 1 addition & 1 deletion include/gauge.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
8 changes: 5 additions & 3 deletions include/imgprovider.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -24,8 +24,10 @@
#include <pthread.h>
#include <stdbool.h>

#include "vdo-stream.h"
#include "vdo-types.h"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#include <vdo-stream.h>
#include <vdo-types.h>
#pragma GCC diagnostic pop

#define _Atomic(X) std::atomic<X>
#define NUM_VDO_BUFFERS (8)
Expand Down
2 changes: 1 addition & 1 deletion include/opcuaserver.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
40 changes: 21 additions & 19 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
{
"schemaVersion": "1.3",
"schemaVersion": "1.7.0",
"acapPackageConf": {
"setup": {
"friendlyName": "OPC UA Gauge Reader",
"appName": "opcuagaugereader",
"vendor": "Axis Communications AB",
"embeddedSdkVersion": "3.0",
"user": {
"group": "sdk",
"username": "sdk"
},
"vendorUrl": "https://www.axis.com/",
"runMode": "respawn",
"version": "1.2.1"
"version": "2.0.0"
},
"configuration": {
"settingPage": "settings.html",
"paramConfig": [
{"name": "clockwise", "type": "bool:0,1", "default": "1"},
{"name": "maxX", "type": "int:min=0,max=639", "default": "150"},
{"name": "maxY", "type": "int:min=0,max=359", "default": "150"},
{"name": "centerX", "type": "int:min=0,max=639", "default": "100"},
{"name": "centerY", "type": "int:min=0,max=359", "default": "170"},
{"name": "minX", "type": "int:min=0,max=639", "default": "50"},
{"name": "minY", "type": "int:min=0,max=359", "default": "150"},
{"name": "port", "type": "int:min=1,max=65535", "default": "4840"}
]
}
"configuration": {
"settingPage": "settings.html",
"paramConfig": [
{"name": "DynamicStringNumber", "type": "int:min=1,max=16", "default": "1"},
{"name": "clockwise", "type": "bool:0,1", "default": "1"},
{"name": "maxX", "type": "int:min=0,max=639", "default": "150"},
{"name": "maxY", "type": "int:min=0,max=359", "default": "150"},
{"name": "centerX", "type": "int:min=0,max=639", "default": "100"},
{"name": "centerY", "type": "int:min=0,max=359", "default": "170"},
{"name": "minX", "type": "int:min=0,max=639", "default": "50"},
{"name": "minY", "type": "int:min=0,max=359", "default": "150"},
{"name": "port", "type": "int:min=1,max=65535", "default": "4840"}
]
}
},
"resources": {
"dbus": {
"requiredMethods": ["com.axis.HTTPConf1.VAPIXServiceAccounts1.GetCredentials"]
}
}
}
164 changes: 164 additions & 0 deletions src/dynstrhandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/**
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <assert.h>
#include <cstdlib>
#include <gio/gio.h>

#include "common.hpp"
#include "dynstrhandler.hpp"

using namespace std;
using namespace std::chrono;

static size_t append_to_string_callback(char *ptr, size_t size, size_t nmemb, string *response)
{
assert(nullptr != response);
auto totalsize = size * nmemb;
response->append(ptr, totalsize);

return totalsize;
}

DynStrHandler::DynStrHandler(const guint8 nbr) : curl(nullptr), nbr(nbr), lastupdate(steady_clock::now())
{
assert(1 <= nbr);
assert(16 >= nbr);

curl_global_init(CURL_GLOBAL_DEFAULT);
curl = curl_easy_init();
assert(nullptr != curl);

const gchar *user = "example-vapix-user";
auto credentials = RetrieveVapixCredentials(*user);

auto curl_init =
(CURLE_OK == curl_easy_setopt(curl, CURLOPT_HTTPAUTH, (long)(CURLAUTH_DIGEST | CURLAUTH_BASIC)) &&
CURLE_OK == curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 2L) &&
CURLE_OK == curl_easy_setopt(curl, CURLOPT_USERPWD, credentials.c_str()) &&
CURLE_OK == curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L) &&
CURLE_OK == curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1) &&
CURLE_OK == curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, append_to_string_callback));

assert(curl_init);
LOG_I("%s/%s: Dynamic string handler constructor is done!", __FILE__, __FUNCTION__);
}

DynStrHandler::~DynStrHandler()
{
assert(nullptr != curl);
curl_easy_cleanup(curl);
curl_global_cleanup();
}

void DynStrHandler::SetStrNumber(const guint8 newnbr)
{
nbr = newnbr;
LOG_I("Now using dynamic string number %u", newnbr);
}

void DynStrHandler::UpdateStr(const double value)
{
// We don't need to update too frequently
auto nowtime = steady_clock::now();
if (1 > duration_cast<seconds>(nowtime - lastupdate).count())
{
return;
}

auto url = "http://127.0.0.12/axis-cgi/dynamicoverlay.cgi?action=settext&text_index=" + to_string(nbr) +
"&text=" + to_string(value);
if (!VapixGet(url))
{
LOG_E("%s/%s: Failed to update dynamic string", __FILE__, __FUNCTION__);
}
lastupdate = nowtime;
}

string DynStrHandler::RetrieveVapixCredentials(const char &username) const
{
GError *error = nullptr;
auto connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
if (nullptr == connection)
{
LOG_E("Error connecting to D-Bus: %s", error->message);
g_error_free(error);
return nullptr;
}

const char *bus_name = "com.axis.HTTPConf1";
const char *object_path = "/com/axis/HTTPConf1/VAPIXServiceAccounts1";
const char *interface_name = "com.axis.HTTPConf1.VAPIXServiceAccounts1";
const char *method_name = "GetCredentials";

auto result = g_dbus_connection_call_sync(
connection,
bus_name,
object_path,
interface_name,
method_name,
g_variant_new("(s)", &username),
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (nullptr == result)
{
LOG_E("Error invoking D-Bus method: %s", error->message);
g_error_free(error);
return "";
}

// Extract credentials string
const char *credentials_string = nullptr;
g_variant_get(result, "(&s)", &credentials_string);
string credentials(credentials_string);
g_variant_unref(result);

return credentials;
}

gboolean DynStrHandler::VapixGet(const string &url)
{
assert(nullptr != curl);

string response;

if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_URL, url.c_str()) ||
CURLE_OK != curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response))
{
LOG_E("%s/%s: Failed to set up cURL options", __FILE__, __FUNCTION__);
return FALSE;
}

auto res = curl_easy_perform(curl);
if (res != CURLE_OK)
{
LOG_E("%s/%s: curl fail %d '%s''", __FILE__, __FUNCTION__, res, curl_easy_strerror(res));
return FALSE;
}

long response_code;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
if (200 != response_code)
{
LOG_E("Got response code %ld with response '%s'", response_code, response.c_str());
return FALSE;
}

return TRUE;
}
2 changes: 1 addition & 1 deletion src/gauge.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2022, Axis Communications AB, Lund, Sweden
* Copyright (C) 2024, Axis Communications AB, Lund, Sweden
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Loading

0 comments on commit c0390e5

Please sign in to comment.