cccs: add ConnectCore Cloud Services examples
https://onedigi.atlassian.net/browse/DEL-8628 Signed-off-by: Tatiana Leon <Tatiana.Leon@digi.com>
This commit is contained in:
parent
79aa8410c2
commit
82ee13f97e
|
|
@ -0,0 +1,40 @@
|
||||||
|
#
|
||||||
|
# Copyright 2023, Digi International Inc.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice and this permission notice appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
BINARY := cccs-data-request-example
|
||||||
|
|
||||||
|
CFLAGS += -Wall
|
||||||
|
|
||||||
|
CFLAGS += $(shell pkg-config --cflags cccs)
|
||||||
|
LDLIBS += $(shell pkg-config --libs --static cccs)
|
||||||
|
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
OBJS := $(SRCS:.c=.o)
|
||||||
|
|
||||||
|
$(BINARY): $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: $(BINARY)
|
||||||
|
install -d $(DESTDIR)/usr/bin
|
||||||
|
install -m 0755 $^ $(DESTDIR)/usr/bin/
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(BINARY)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
-rm -f *.o $(BINARY)
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
Digi ConnectCore Cloud Services Data Request Example Application
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Example application to listen to data requests from Remote Manager using
|
||||||
|
ConnectCore Cloud Services.
|
||||||
|
|
||||||
|
This application registers a `get_time` data request.
|
||||||
|
When this request arrives the application sends back the current time.
|
||||||
|
|
||||||
|
Running the application
|
||||||
|
-----------------------
|
||||||
|
This application requires `cccsd` (ConnectCore Cloud Services daemon) running
|
||||||
|
on the device.
|
||||||
|
|
||||||
|
Once the binary is in the target, launch the application:
|
||||||
|
|
||||||
|
```
|
||||||
|
# ./cccs-data-request-example
|
||||||
|
cccs-data-request-example[1011]: [DEBUG] CCCSD: Connected to CCCSD (s=4)
|
||||||
|
cccs-data-request-example[1011]: [DEBUG] CCCS daemon ready
|
||||||
|
cccs-data-request-example[1011]: [INFO] DREQ: Registering 'get_time' data request
|
||||||
|
cccs-data-request-example[1011]: [DEBUG] CCCSD: Connected to CCCSD (s=7)
|
||||||
|
cccs-data-request-example[1011]: [DEBUG] CCCSD: Success from CCCSD
|
||||||
|
Waiting for Remote Manager request...
|
||||||
|
Press 'q' and 'Enter' to exit
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Send a `get_time` data request to your device.
|
||||||
|
To do so:
|
||||||
|
|
||||||
|
1. Go to https://remotemanager.digi.com/.
|
||||||
|
2. Login with your credentials.
|
||||||
|
3. Get your device identifier from the **Devices** page:
|
||||||
|
a. Go to the **Management > Devices** tab.
|
||||||
|
b. If you have more than one device, you can filter using the **MAC**
|
||||||
|
address of your device:
|
||||||
|
1. Click on the filter text box and select **MAC**.
|
||||||
|
2. Type the MAC address of your device and press `Enter`.
|
||||||
|
3. Copy the **Device ID** of your device from the table by using the **Copy Device ID** button that appears next to it when you hover over the item.
|
||||||
|
4. Go to the **System > API Explorer** tab. From here you can test the Web
|
||||||
|
Services API.
|
||||||
|
5. Click the **Examples** combo.
|
||||||
|
6. Select **SCI > Data Service > Send Request**.
|
||||||
|
A request appears inside the Body text box.
|
||||||
|
7. Modify the request to stop the sampling process on your device:
|
||||||
|
a. Replace the `device id` value with the copied one.
|
||||||
|
b. Replace the `target_name` value with `get_time`.
|
||||||
|
c. Remove the payload of the request.
|
||||||
|
The request should be similar to the following:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<sci_request version="1.0">
|
||||||
|
<data_service>
|
||||||
|
<targets>
|
||||||
|
<device id="00000000-00000000-00XXXXXX-XXXXXXXX" />
|
||||||
|
</targets>
|
||||||
|
<requests>
|
||||||
|
<device_request target_name="get_time"></device_request>
|
||||||
|
</requests>
|
||||||
|
</data_service>
|
||||||
|
</sci_request>
|
||||||
|
```
|
||||||
|
Where `00000000-00000000-00XXXXXX-XXXXXXXX` is the Device ID.
|
||||||
|
8. Click **Send** to send the request to your device.
|
||||||
|
In the **Response** text box you can review the answer from the device:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<sci_reply version="1.0">
|
||||||
|
<data_service>
|
||||||
|
<device id="00000000-00000000-00XXXXXX-XXXXXXXX">
|
||||||
|
<requests>
|
||||||
|
<device_request target_name="get_time" status="0">Time: Fri Sep 1 11:36:33 2023</device_request>
|
||||||
|
</requests>
|
||||||
|
</device>
|
||||||
|
</data_service>
|
||||||
|
</sci_reply>
|
||||||
|
```
|
||||||
|
Where `00000000-00000000-00XXXXXX-XXXXXXXX` is the Device ID.
|
||||||
|
|
||||||
|
The application prints out the received request:
|
||||||
|
|
||||||
|
```
|
||||||
|
[...]
|
||||||
|
Waiting for Remote Manager request...
|
||||||
|
Press 'q' and 'Enter' to exit
|
||||||
|
cccs-data-request-example[1163]: [DEBUG] get_time_cb: target='get_time'
|
||||||
|
cccs-data-request-example[1163]: [DEBUG] get_time_status_cb: target='get_time' - error='0' - error-hint='Success'
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiling the application
|
||||||
|
-------------------------
|
||||||
|
This example can be compiled using a Digi Embedded Yocto based toolchain. Make
|
||||||
|
sure to source the corresponding toolchain of the platform you are using,
|
||||||
|
for example, for ConnectCore 6UL:
|
||||||
|
|
||||||
|
```
|
||||||
|
~$ . <DEY-toolchain-path>/environment-setup-cortexa7t2hf-neon-dey-linux-gnueabi
|
||||||
|
~$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, see the [Digi Embedded Yocto online documentation](https://github.com/digi-embedded/meta-digi).
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Copyright 2023, Digi International Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023, Digi International Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cccs_services.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if !(defined UNUSED_ARGUMENT)
|
||||||
|
#define UNUSED_ARGUMENT(a) (void)(a)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TARGET_GET_TIME "get_time"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigaction_handler() - Handler to execute after receiving a signal
|
||||||
|
*
|
||||||
|
* @signum: Received signal.
|
||||||
|
*/
|
||||||
|
static void sigaction_handler(int signum)
|
||||||
|
{
|
||||||
|
log_debug("%s: received signal %d", __func__, signum);
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cleanup() - Frees all the allocated memory before exiting
|
||||||
|
*/
|
||||||
|
static void cleanup(void)
|
||||||
|
{
|
||||||
|
cccs_resp_t resp;
|
||||||
|
cccs_comm_error_t ret;
|
||||||
|
|
||||||
|
ret = cccs_remove_request_target(TARGET_GET_TIME, &resp);
|
||||||
|
if (ret != CCCS_SEND_ERROR_NONE) {
|
||||||
|
log_error("%s: Cannot unregister target '%s': CCCSD error %d",
|
||||||
|
__func__, TARGET_GET_TIME, ret);
|
||||||
|
} else if (resp.code != 0) {
|
||||||
|
if (resp.hint)
|
||||||
|
log_error("%s: Cannot unregister target '%s': CCCSD error, %s (%d)",
|
||||||
|
__func__, TARGET_GET_TIME, resp.hint, resp.code);
|
||||||
|
else
|
||||||
|
log_error("%s: Cannot unregister target '%s': CCCSD error, %d",
|
||||||
|
__func__, TARGET_GET_TIME, resp.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resp.hint);
|
||||||
|
|
||||||
|
deinit_logger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* register_signals() - Registers program signals
|
||||||
|
*/
|
||||||
|
static void register_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction new_action;
|
||||||
|
struct sigaction old_action;
|
||||||
|
|
||||||
|
atexit(cleanup);
|
||||||
|
|
||||||
|
new_action.sa_handler = sigaction_handler;
|
||||||
|
new_action.sa_flags = 0;
|
||||||
|
sigemptyset(&new_action.sa_mask);
|
||||||
|
|
||||||
|
sigaction(SIGINT, NULL, &old_action);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGINT, &new_action, NULL);
|
||||||
|
|
||||||
|
sigaction(SIGHUP, &old_action, NULL);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGHUP, &new_action, NULL);
|
||||||
|
|
||||||
|
sigaction(SIGTERM, &old_action, NULL);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGTERM, &new_action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_time_cb() - Data callback for 'get_time' data requests
|
||||||
|
*
|
||||||
|
* @target: Target ID of the data request (get_time).
|
||||||
|
* @req_buf_info: Buffer containing the data request.
|
||||||
|
* @resp_buf_info: Buffer to store the answer of the request.
|
||||||
|
*
|
||||||
|
* Logs information about the received request and executes the corresponding
|
||||||
|
* command.
|
||||||
|
*
|
||||||
|
* Return: 'CCCS_RECEIVE_ERROR_NONE' if success, any other value on failure.
|
||||||
|
*/
|
||||||
|
static cccs_receive_error_t get_time_cb(char const *const target,
|
||||||
|
cccs_buffer_info_t const *const req_buf_info,
|
||||||
|
cccs_buffer_info_t *const resp_buf_info)
|
||||||
|
{
|
||||||
|
time_t t = time(NULL);
|
||||||
|
char *time_str = ctime(&t);
|
||||||
|
|
||||||
|
UNUSED_ARGUMENT(req_buf_info);
|
||||||
|
log_debug("%s: target='%s'", __func__, target);
|
||||||
|
|
||||||
|
resp_buf_info->length = snprintf(NULL, 0, "Time: %s", time_str);
|
||||||
|
resp_buf_info->buffer = calloc(resp_buf_info->length + 1, sizeof(char));
|
||||||
|
if (resp_buf_info->buffer == NULL) {
|
||||||
|
log_error("%s: resp_buf_info calloc error", __func__);
|
||||||
|
|
||||||
|
return CCCS_RECEIVE_ERROR_INSUFFICIENT_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
resp_buf_info->length = sprintf(resp_buf_info->buffer, "Time: %s", time_str);
|
||||||
|
|
||||||
|
return CCCS_RECEIVE_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_time_status_cb() - Status callback for 'get_time' data requests
|
||||||
|
*
|
||||||
|
* @target: Target ID of the data request (get_time)
|
||||||
|
* @resp_buf_info: Buffer containing the response data.
|
||||||
|
* @receive_error: The error status of the receive process.
|
||||||
|
* @receive_error_hint: The error hint from the connector service.
|
||||||
|
*
|
||||||
|
* This callback is executed when the response process has finished. It doesn't
|
||||||
|
* matter if everything worked or there was an error during the process.
|
||||||
|
*
|
||||||
|
* Cleans and frees the response buffer.
|
||||||
|
*/
|
||||||
|
static void get_time_status_cb(char const *const target,
|
||||||
|
cccs_buffer_info_t *const resp_buf_info,
|
||||||
|
int receive_error,
|
||||||
|
const char *const receive_error_hint)
|
||||||
|
{
|
||||||
|
log_debug("%s: target='%s' - error='%d' - error-hint='%s'",
|
||||||
|
__func__, target, receive_error, receive_error_hint);
|
||||||
|
|
||||||
|
/* Free the response buffer */
|
||||||
|
if (resp_buf_info != NULL)
|
||||||
|
free(resp_buf_info->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the following SCI request to test this example (insert your Device ID):
|
||||||
|
*
|
||||||
|
* <sci_request version="1.0">
|
||||||
|
* <data_service>
|
||||||
|
* <targets>
|
||||||
|
* <device id="00000000-00000000-XXXXXXXX-XXXXXXXX"/>
|
||||||
|
* </targets>
|
||||||
|
* <requests>
|
||||||
|
* <device_request target_name="get_time"/>
|
||||||
|
* </requests>
|
||||||
|
* </data_service>
|
||||||
|
* </sci_request>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
cccs_comm_error_t ret;
|
||||||
|
char *name = basename(argv[0]);
|
||||||
|
cccs_resp_t resp;
|
||||||
|
int read_char;
|
||||||
|
|
||||||
|
init_logger(LOG_DEBUG, LOG_CONS | LOG_NDELAY | LOG_PID | LOG_PERROR, name);
|
||||||
|
|
||||||
|
register_signals();
|
||||||
|
|
||||||
|
if (!cccs_is_daemon_ready(CCCSD_NO_WAIT)) {
|
||||||
|
log_error("%s: CCCS daemon not ready... exiting", __func__);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cccs_add_request_target(TARGET_GET_TIME, get_time_cb,
|
||||||
|
get_time_status_cb, &resp);
|
||||||
|
if (ret != CCCS_SEND_ERROR_NONE) {
|
||||||
|
log_error("%s: Cannot register target '%s': CCCSD error %d",
|
||||||
|
__func__, TARGET_GET_TIME, ret);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (resp.code != 0) {
|
||||||
|
if (resp.hint)
|
||||||
|
log_error("%s: Cannot register target '%s': CCCSD error, %s (%d)",
|
||||||
|
__func__, TARGET_GET_TIME, resp.hint, resp.code);
|
||||||
|
else
|
||||||
|
log_error("%s: Cannot register target '%s': CCCSD error, %d",
|
||||||
|
__func__, TARGET_GET_TIME, resp.code);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resp.hint);
|
||||||
|
|
||||||
|
printf("Waiting for Remote Manager request...\n");
|
||||||
|
printf("Press 'q' and 'Enter' to exit\n");
|
||||||
|
do {
|
||||||
|
read_char = getchar();
|
||||||
|
} while (read_char != 'q');
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2023 Digi International Inc.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice and this permission notice appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
# OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
SUBDIRS := \
|
||||||
|
cccs-data-request-example \
|
||||||
|
cccs-upload-data-points-example
|
||||||
|
|
||||||
|
all: $(SUBDIRS)
|
||||||
|
|
||||||
|
.PHONY: $(SUBDIRS)
|
||||||
|
$(SUBDIRS):
|
||||||
|
$(MAKE) -C $@
|
||||||
|
|
||||||
|
install clean:
|
||||||
|
for a in $(SUBDIRS); do $(MAKE) -C $$a $@; done
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
#
|
||||||
|
# Copyright 2023, Digi International Inc.
|
||||||
|
#
|
||||||
|
# Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
# purpose with or without fee is hereby granted, provided that the above
|
||||||
|
# copyright notice and this permission notice appear in all copies.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
# AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
# PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
#
|
||||||
|
|
||||||
|
BINARY := cccs-upload-data-points-example
|
||||||
|
|
||||||
|
CFLAGS += -Wall
|
||||||
|
|
||||||
|
CFLAGS += $(shell pkg-config --cflags cccs)
|
||||||
|
LDLIBS += $(shell pkg-config --libs --static cccs)
|
||||||
|
|
||||||
|
SRCS := $(wildcard *.c)
|
||||||
|
OBJS := $(SRCS:.c=.o)
|
||||||
|
|
||||||
|
$(BINARY): $(OBJS)
|
||||||
|
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: $(BINARY)
|
||||||
|
install -d $(DESTDIR)/usr/bin
|
||||||
|
install -m 0755 $^ $(DESTDIR)/usr/bin/
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: $(BINARY)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
-rm -f *.o $(BINARY)
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
Digi ConnectCore Cloud Services Upload Data Points Example Application
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
Example application to upload data points to Remote Manager using ConnectCore
|
||||||
|
Cloud Services.
|
||||||
|
|
||||||
|
This application uploads an integer value with an incremented counter to a data
|
||||||
|
stream called 'incremental'. The counter value is incremented every 5 seconds.
|
||||||
|
The uploads takes place every 10 new samples, that is every 50 seconds.
|
||||||
|
|
||||||
|
Running the application
|
||||||
|
-----------------------
|
||||||
|
This application requires `cccsd` (ConnectCore Cloud Services daemon) running
|
||||||
|
on the device.
|
||||||
|
|
||||||
|
Once the binary is in the target, launch the application:
|
||||||
|
|
||||||
|
```
|
||||||
|
# ./cccs-upload-data-points-example
|
||||||
|
cccs-upload-data-points-example[1010]: [DEBUG] CCCSD: Connected to CCCSD (s=4)
|
||||||
|
cccs-upload-data-points-example[1010]: [DEBUG] CCCS daemon ready
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 0
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 1
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 2
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 3
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 4
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 5
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 6
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 7
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 8
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 9
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Sending data sream with new incremental value
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] DP: Sending data points to CCCSD
|
||||||
|
cccs-upload-data-points-example[1090]: [DEBUG] CCCSD: Connected to CCCSD (s=4)
|
||||||
|
cccs-upload-data-points-example[1090]: [DEBUG] CCCSD: Success from CCCSD
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 10
|
||||||
|
cccs-upload-data-points-example[1090]: [INFO] Counter = 11
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiling the application
|
||||||
|
-------------------------
|
||||||
|
This example can be compiled using a Digi Embedded Yocto based toolchain. Make
|
||||||
|
sure to source the corresponding toolchain of the platform you are using,
|
||||||
|
for example, for ConnectCore 6UL:
|
||||||
|
|
||||||
|
```
|
||||||
|
~$ . <DEY-toolchain-path>/environment-setup-cortexa7t2hf-neon-dey-linux-gnueabi
|
||||||
|
~$ make
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information, see the [Digi Embedded Yocto online documentation](https://github.com/digi-embedded/meta-digi).
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Copyright 2023, Digi International Inc.
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||||
|
with or without fee is hereby granted, provided that the above copyright notice
|
||||||
|
and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||||||
|
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||||
|
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||||||
|
THIS SOFTWARE.
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2023, Digi International Inc.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cccs_services.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define STREAM_NAME "incremental"
|
||||||
|
|
||||||
|
#define DP_SLEEP_TIME 5
|
||||||
|
#define DP_NUMBER 10
|
||||||
|
|
||||||
|
static int stop_requested = 0;
|
||||||
|
static cccs_dp_collection_handle_t dp_collection;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* destroy_data_stream() - Destroy the data collection with the included data streams
|
||||||
|
*
|
||||||
|
* @collection: Data point collection to destroy.
|
||||||
|
*
|
||||||
|
* Return: 'CCCS_DP_ERROR_NONE' if success, any other value on failure.
|
||||||
|
*/
|
||||||
|
static cccs_dp_error_t destroy_data_stream(cccs_dp_collection_handle_t collection)
|
||||||
|
{
|
||||||
|
log_debug("%s", "Destroying data collection");
|
||||||
|
|
||||||
|
return cccs_dp_destroy_collection(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sigaction_handler() - Handler to execute after receiving a signal
|
||||||
|
*
|
||||||
|
* @signum: Received signal.
|
||||||
|
*/
|
||||||
|
static void sigaction_handler(int signum)
|
||||||
|
{
|
||||||
|
log_debug("%s: received signal %d", __func__, signum);
|
||||||
|
|
||||||
|
stop_requested = 1;
|
||||||
|
/* 'atexit' executes the cleanup function */
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cleanup() - Frees all the allocated memory before exiting
|
||||||
|
*/
|
||||||
|
static void cleanup(void)
|
||||||
|
{
|
||||||
|
if (stop_requested)
|
||||||
|
destroy_data_stream(dp_collection);
|
||||||
|
|
||||||
|
deinit_logger();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* register_signals() - Registers program signals
|
||||||
|
*/
|
||||||
|
static void register_signals(void)
|
||||||
|
{
|
||||||
|
struct sigaction new_action;
|
||||||
|
struct sigaction old_action;
|
||||||
|
|
||||||
|
atexit(cleanup);
|
||||||
|
|
||||||
|
new_action.sa_handler = sigaction_handler;
|
||||||
|
new_action.sa_flags = 0;
|
||||||
|
sigemptyset(&new_action.sa_mask);
|
||||||
|
|
||||||
|
sigaction(SIGINT, NULL, &old_action);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGINT, &new_action, NULL);
|
||||||
|
|
||||||
|
sigaction(SIGHUP, &old_action, NULL);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGHUP, &new_action, NULL);
|
||||||
|
|
||||||
|
sigaction(SIGTERM, &old_action, NULL);
|
||||||
|
if (old_action.sa_handler != SIG_IGN)
|
||||||
|
sigaction(SIGTERM, &new_action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get_incremental_value() - Retrieves an incremental value each time
|
||||||
|
*
|
||||||
|
* Return: The new counter value.
|
||||||
|
*/
|
||||||
|
static int get_incremental_value(void)
|
||||||
|
{
|
||||||
|
static int counter = -1;
|
||||||
|
|
||||||
|
if (counter == INT_MAX)
|
||||||
|
counter = 0;
|
||||||
|
else
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
log_info("Counter = %d", counter);
|
||||||
|
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init_data_stream() - Initialize a data point collection
|
||||||
|
*
|
||||||
|
* @collection: Data point collection to initialize
|
||||||
|
*
|
||||||
|
* This function creates a data point collection and adds the 'incremental'
|
||||||
|
* data stream to it.
|
||||||
|
*
|
||||||
|
* Return: 'CCCS_DP_ERROR_NONE' if success, any other value on failure.
|
||||||
|
*/
|
||||||
|
static cccs_dp_error_t init_data_stream(cccs_dp_collection_handle_t *collection)
|
||||||
|
{
|
||||||
|
cccs_dp_collection_handle_t c;
|
||||||
|
cccs_dp_error_t dp_error;
|
||||||
|
|
||||||
|
dp_error = cccs_dp_create_collection(&c);
|
||||||
|
if (dp_error != CCCS_DP_ERROR_NONE) {
|
||||||
|
log_error("%s: error %d", __func__, dp_error);
|
||||||
|
|
||||||
|
return dp_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*collection = c;
|
||||||
|
|
||||||
|
dp_error = cccs_dp_add_data_stream_to_collection_extra(c,
|
||||||
|
STREAM_NAME, CCCS_DP_KEY_DATA_INT32, true, "counts", NULL);
|
||||||
|
if (dp_error != CCCS_DP_ERROR_NONE) {
|
||||||
|
log_error("%s: error %d", __func__, dp_error);
|
||||||
|
cccs_dp_destroy_collection(c);
|
||||||
|
*collection = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dp_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* add_incremental_data_point() - Add a new incremental data point to the collection
|
||||||
|
*
|
||||||
|
* @collection: Data point collection to add the new data point.
|
||||||
|
*
|
||||||
|
* Return: 'CCCS_DP_ERROR_NONE' if success, any other value on failure.
|
||||||
|
*/
|
||||||
|
static cccs_dp_error_t add_incremental_data_point(cccs_dp_collection_handle_t collection)
|
||||||
|
{
|
||||||
|
cccs_dp_error_t dp_error;
|
||||||
|
|
||||||
|
dp_error = cccs_dp_add(collection, STREAM_NAME, get_incremental_value());
|
||||||
|
if (dp_error != CCCS_DP_ERROR_NONE)
|
||||||
|
log_error("%s: failed with error: %d", __func__, dp_error);
|
||||||
|
|
||||||
|
return dp_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* send_data() - Send collected data to ConnectCore Cloud Services daemon
|
||||||
|
*
|
||||||
|
* @collection: Data point collection to with the data to send.
|
||||||
|
*
|
||||||
|
* Return: 0 if success, any other value on failure.
|
||||||
|
*/
|
||||||
|
static int send_data(cccs_dp_collection_handle_t collection)
|
||||||
|
{
|
||||||
|
cccs_comm_error_t ret;
|
||||||
|
cccs_resp_t resp;
|
||||||
|
|
||||||
|
log_info("%s", "Sending data sream with new incremental value");
|
||||||
|
|
||||||
|
ret = cccs_send_dp_collection_tout(collection, 5, &resp);
|
||||||
|
if (ret != CCCS_SEND_ERROR_NONE) {
|
||||||
|
log_error("%s: error sending data points: CCCSD error %d", __func__, ret);
|
||||||
|
} else if (resp.code != 0) {
|
||||||
|
if (resp.hint)
|
||||||
|
log_error("%s: error sending data points: CCCSD error %s (%d)", __func__, resp.hint, resp.code);
|
||||||
|
else
|
||||||
|
log_error("%s: error sending data points: CCCSD error %d", __func__, resp.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resp.hint);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *name = basename(argv[0]);
|
||||||
|
cccs_dp_error_t dp_error;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
init_logger(LOG_DEBUG, LOG_CONS | LOG_NDELAY | LOG_PID | LOG_PERROR, name);
|
||||||
|
|
||||||
|
register_signals();
|
||||||
|
|
||||||
|
if (!cccs_is_daemon_ready(CCCSD_NO_WAIT)) {
|
||||||
|
log_error("%s: CCCS daemon not ready... exiting", __func__);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dp_error = init_data_stream(&dp_collection);
|
||||||
|
if (dp_error != CCCS_DP_ERROR_NONE) {
|
||||||
|
log_error("%s: Cannot initialize data stream, error %d",
|
||||||
|
__func__, dp_error);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_requested = 0;
|
||||||
|
while (!stop_requested) {
|
||||||
|
/* Collect DP_NUMBER data points sampled each DP_SLEEP_TIME seconds */
|
||||||
|
for (i = 0; i < DP_NUMBER; i++) {
|
||||||
|
dp_error = add_incremental_data_point(dp_collection);
|
||||||
|
|
||||||
|
if (dp_error != CCCS_DP_ERROR_NONE) {
|
||||||
|
log_error("%s: Cannot add data point, error %d",
|
||||||
|
__func__, dp_error);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 1 < DP_NUMBER)
|
||||||
|
sleep(DP_SLEEP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the block of collected data points */
|
||||||
|
send_data(dp_collection);
|
||||||
|
|
||||||
|
if (i == DP_NUMBER)
|
||||||
|
sleep(DP_SLEEP_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
@ -223,4 +223,51 @@ A key modifier may be used to further differentiate the key used in a particular
|
||||||
<platform>ccimx8mm-dvk</platform>
|
<platform>ccimx8mm-dvk</platform>
|
||||||
</platforms>
|
</platforms>
|
||||||
</sample>
|
</sample>
|
||||||
|
<sample id="cccs-data-request-example">
|
||||||
|
<name>Digi CCCS data request Example</name>
|
||||||
|
<description>
|
||||||
|
Example application to listen to data requests from Remote Manager using
|
||||||
|
ConnectCore Cloud Services.
|
||||||
|
This application registers a `get_time` data request.
|
||||||
|
When this request arrives the application sends back the current time.
|
||||||
|
</description>
|
||||||
|
<path>cccs-data-request-example</path>
|
||||||
|
<platforms>
|
||||||
|
<platform>ccimx6sbc</platform>
|
||||||
|
<platform>ccimx6qpsbc</platform>
|
||||||
|
<platform>ccimx6ulstarter</platform>
|
||||||
|
<platform>ccimx6ulsbc</platform>
|
||||||
|
<platform>ccimx8x-sbc-express</platform>
|
||||||
|
<platform>ccimx8x-sbc-pro</platform>
|
||||||
|
<platform>ccimx8mn-dvk</platform>
|
||||||
|
<platform>ccimx8mm-dvk</platform>
|
||||||
|
<platform>ccmp15-dvk</platform>
|
||||||
|
<platform>ccmp13-dvk</platform>
|
||||||
|
<platform>ccimx93-dvk</platform>
|
||||||
|
</platforms>
|
||||||
|
</sample>
|
||||||
|
<sample id="cccs-upload-data-points-example">
|
||||||
|
<name>Digi CCCS upload data points Example</name>
|
||||||
|
<description>
|
||||||
|
Example application to upload data points to Remote Manager using ConnectCore
|
||||||
|
Cloud Services.
|
||||||
|
This application uploads an integer value with an incremented counter to a data
|
||||||
|
stream called 'incremental'. The counter value is incremented every 5 seconds.
|
||||||
|
The uploads takes place every 10 new samples, that is every 50 seconds.
|
||||||
|
</description>
|
||||||
|
<path>cccs-upload-data-points-example</path>
|
||||||
|
<platforms>
|
||||||
|
<platform>ccimx6sbc</platform>
|
||||||
|
<platform>ccimx6qpsbc</platform>
|
||||||
|
<platform>ccimx6ulstarter</platform>
|
||||||
|
<platform>ccimx6ulsbc</platform>
|
||||||
|
<platform>ccimx8x-sbc-express</platform>
|
||||||
|
<platform>ccimx8x-sbc-pro</platform>
|
||||||
|
<platform>ccimx8mn-dvk</platform>
|
||||||
|
<platform>ccimx8mm-dvk</platform>
|
||||||
|
<platform>ccmp15-dvk</platform>
|
||||||
|
<platform>ccmp13-dvk</platform>
|
||||||
|
<platform>ccimx93-dvk</platform>
|
||||||
|
</platforms>
|
||||||
|
</sample>
|
||||||
</samples>
|
</samples>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue