Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/docker-build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ jobs:
- uses: actions/checkout@v3

- name: Cache Docker layers
uses: actions/cache@v2
uses: actions/cache@v4
with:
path: /home/runner/docker
key: ${{ runner.os }}-docker-${{ github.sha }}
Expand Down
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# CHANGELOG


## [v1.0.3-rc.2](https://github.com/NubeIO/driver-bacnet/tree/v1.0.3-rc.2) (2025-12-15)
- Issue-138: Initial object instances based on the points from the config file
- Prerequisites:
- ROS >= 1.0.0

## [v1.0.3-rc.1](https://github.com/NubeIO/driver-bacnet/tree/v1.0.3-rc.1) (2025-08-28)
- Issue-137: Remove Multi-state points; Add an error message when the request resulted an error
- Issue-138: Support for points list in the config file
- Issue-139: Support for binding to specific IP address/interface
- Prerequisites:
- ROS >= 1.0.0

## [v1.0.2](https://github.com/NubeIO/driver-bacnet/tree/v1.0.2) (2024-11-08)
- Fixed device ID returned from reading property list.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.2-rc.4
1.0.3-rc.2
5 changes: 4 additions & 1 deletion bacnet-stack/apps/server/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,10 @@ int main(int argc, char *argv[])
#endif /* defined(BAC_UCI) */

#if defined(YAML_CONFIG)
yaml_config_init();
if (yaml_config_init()) {
return 1;
}

val_uint32 = strtoul(yaml_config_service_id(), NULL, 10);
if (val_uint32 > 0) {
Device_Set_Object_Instance_Number(val_uint32);
Expand Down
18 changes: 17 additions & 1 deletion bacnet-stack/apps/server/mqtt_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,10 @@ int process_bacnet_client_whois_command(bacnet_client_cmd_opts *opts)
}
}

if (mqtt_debug) {
printf("global_broadcast: %d\n", global_broadcast);
}

if (global_broadcast) {
datalink_get_broadcast_address(&dest);
} else {
Expand Down Expand Up @@ -1493,6 +1497,13 @@ int process_bacnet_client_whois_command(bacnet_client_cmd_opts *opts)

add_bacnet_client_whois(&dest, opts);

if (mqtt_debug) {
printf("Sending WHOIS to: %u.%u.%u.%u %02X%02X\n", (unsigned)dest.mac[0],
(unsigned)dest.mac[1], (unsigned)dest.mac[2],
(unsigned)dest.mac[3], (unsigned)dest.mac[4],
(unsigned)dest.mac[5]);
}

Send_WhoIs_To_Network(
&dest, opts->device_instance_min, opts->device_instance_max);

Expand Down Expand Up @@ -3990,6 +4001,11 @@ static void bacnet_client_error_handler(BACNET_ADDRESS *src,
if (!tmp->data.obj_data.dont_publish_on_success) {
init_cmd_opts_from_list_cb(&opts, &tmp->data.obj_data);
sprintf(err_msg, "%s", bactext_error_code_name((int)error_code));
if ((!strcasecmp(err_msg, "unknown-object")) &&
tmp->data.obj_data.topic_id == MQTT_WRITE_VALUE_CMD_RESULT_TOPIC) {
sprintf(err_msg, "failed to write");
}

mqtt_publish_command_error(err_msg, &opts, tmp->data.obj_data.topic_id);
}

Expand Down Expand Up @@ -4691,7 +4707,7 @@ int mqtt_publish_command_error(char *err_msg, bacnet_client_cmd_opts *opts, int
mqtt_opts.onFailure = mqtt_on_send_failure;
mqtt_opts.context = mqtt_client;

snprintf(topic_value, sizeof(topic_value), "{ \"error\" : \"%s\" ", err_msg);
snprintf(topic_value, sizeof(topic_value), "{ \"fault\" : \"true\", \"error\" : \"%s\" ", err_msg);
snprintf(&topic_value[strlen(topic_value)], sizeof(topic_value) - strlen(topic_value), ", \"objectType\" : \"%d\" ",
opts->object_type);
snprintf(&topic_value[strlen(topic_value)], sizeof(topic_value) - strlen(topic_value), ", \"objectInstance\" : \"%d\" ",
Expand Down
192 changes: 187 additions & 5 deletions bacnet-stack/apps/server/yaml_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
#include "yaml_config.h"

#include "bacnet/basic/object/device.h"
#include "bacnet/bactext.h"

#define DEFAULT_YAML_CONFIG "/data/bacnet-server-driver/config/config.yml"
#define MAX_YAML_STR_VALUE_LENGTH 51

/* mqtt struct */
struct _mqtt {
Expand Down Expand Up @@ -65,6 +65,9 @@ struct _bacnet_config {
char **properties;
unsigned n_properties;
struct _bacnet_client *bacnet_client;
char *use_points_list;
unsigned int n_points;
struct _point *points;
};

/* forward decls */
Expand Down Expand Up @@ -151,6 +154,27 @@ static const cyaml_schema_field_t bacnet_client_fields_schema[] = {
CYAML_FIELD_END
};

static const cyaml_schema_field_t point_fields_schema[] = {
CYAML_FIELD_STRING_PTR(
"point", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _point, label, 0, CYAML_UNLIMITED),

CYAML_FIELD_STRING_PTR(
"name", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _point, name, 0, CYAML_UNLIMITED),

CYAML_FIELD_STRING_PTR(
"units", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _point, units, 0, CYAML_UNLIMITED),

CYAML_FIELD_END
};

static const cyaml_schema_value_t point_schema = {
CYAML_VALUE_MAPPING(CYAML_FLAG_DEFAULT,
struct _point, point_fields_schema),
};

static const cyaml_schema_field_t config_fields_schema[] = {
CYAML_FIELD_STRING_PTR(
"server_name", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
Expand Down Expand Up @@ -230,6 +254,15 @@ static const cyaml_schema_field_t config_fields_schema[] = {
"bacnet_client", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _bacnet_config, bacnet_client, bacnet_client_fields_schema),

CYAML_FIELD_STRING_PTR(
"use_points_list", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _bacnet_config, use_points_list, 0, CYAML_UNLIMITED),

CYAML_FIELD_SEQUENCE_COUNT(
"points", CYAML_FLAG_POINTER | CYAML_FLAG_OPTIONAL,
struct _bacnet_config, points, n_points,
&point_schema, 0, CYAML_UNLIMITED),

CYAML_FIELD_END
};

Expand Down Expand Up @@ -414,7 +447,77 @@ void load_default_settings(void)
bacnet_config->bacnet_client->filter_objects = malloc(sizeof(char) * MAX_YAML_STR_VALUE_LENGTH);
strcpy(bacnet_config->bacnet_client->filter_objects, "true");
}

if (!bacnet_config->use_points_list) {
bacnet_config->use_points_list = malloc(sizeof(char) * MAX_YAML_STR_VALUE_LENGTH);
strcpy(bacnet_config->use_points_list, "false");
}
}
}


/*
* Check if recognized object name.
*/
static bool is_valid_object_name(const char *name)
{
int i;
bool found = false;
char *object_names[] = {
"ai",
"ao",
"av",
"bi",
"bo",
"bv",
NULL
};

for (i = 0; object_names[i] != NULL; i++) {
if (!strcasecmp(object_names[i], name)) {
found = true;
break;
}
}

return(found);
}


/*
* Normalize config.
*/
static int normalize_config(void)
{
char buf[1024] = {0};
char *ptr;
unsigned int i, idx;

if (bacnet_config->n_points > 0) {
for(i = 0; i < bacnet_config->n_points; i++) {
strncpy(buf, bacnet_config->points[i].label, sizeof(buf) - 1);
ptr = &buf[0];
if (!strsep(&ptr, "-")) {
printf("- Invalid Point name: [%s]\n", buf);
return(1);
}

if (!is_valid_object_name(buf)) {
printf("- Invalid Object name: [%s]\n", buf);
return(1);
}

strcpy(bacnet_config->points[i].object_name, buf);
bacnet_config->points[i].object_instance = atoi(ptr);

if (!bactext_engineering_unit_index(bacnet_config->points[i].units, &idx)) {
printf("- Invalid Units: [%s]\n", bacnet_config->points[i].units);
return(1);
}
}
}

return(0);
}


Expand Down Expand Up @@ -448,15 +551,15 @@ int yaml_config_init(void)
sprintf(config_file, "%s/config/", pEnv);
} else {
printf("Global configuration directory not specified. Exiting!\n");
exit(0);
return(1);
}

pEnv = getenv("s");
if (pEnv) {
sprintf(&config_file[strlen(config_file)], "%s", pEnv);
} else {
printf("Configuration file not specified. Exiting!\n");
exit(0);
return(1);
}

if (yaml_config_debug) {
Expand All @@ -465,18 +568,22 @@ int yaml_config_init(void)

if (stat(config_file, &statbuf) < 0) {
printf("Configuration file (%s) not found. Exiting!\n", config_file);
exit(0);
return(1);
}

err = cyaml_load_file(config_file, &yaml_config,
&config_schema, (void **)&bacnet_config, NULL);
if (err != CYAML_OK) {
fprintf(stderr, "ERROR: %s\n", cyaml_strerror(err));
exit(1);
return(1);
}

load_default_settings();

if (normalize_config()) {
return(1);
}

if (yaml_config_debug) {
yaml_config_dump();
}
Expand Down Expand Up @@ -571,6 +678,21 @@ void yaml_config_dump(void)
printf("YAML Config: bacnet_client->filter_objects: %s\n", (bacnet_config->bacnet_client->filter_objects) ?
bacnet_config->bacnet_client->filter_objects: "null");
}

printf("YAML Config: use_points_list: %s\n", (bacnet_config->use_points_list) ?
bacnet_config->use_points_list : "null");

if (bacnet_config->n_points > 0) {
printf("YAML Config: Points\n");

for(i = 0; i < bacnet_config->n_points; i++) {
printf("[%d]: label => [%s]\n", i, bacnet_config->points[i].label);
printf("[%d]: name => [%s]\n", i, bacnet_config->points[i].name);
printf("[%d]: units => [%s]\n", i, bacnet_config->points[i].units);
printf("[%d]: object_name => [%s]\n", i, bacnet_config->points[i].object_name);
printf("[%d]: object_instance => %d\n", i, bacnet_config->points[i].object_instance);
}
}
}


Expand Down Expand Up @@ -958,3 +1080,63 @@ int yaml_config_cleanup(void)
return(0);
}



/*
* Get use point list enable flag.
*/
int yaml_use_point_list_enable(void)
{
return (!strcmp(bacnet_config->use_points_list, "true") ? true : false);
}


/*
* Get points length.
*/
point_cb *yaml_get_points_by_name(char *name, int *length)
{
point_cb *points = NULL;
int i, ii, len;

for (i = 0, len = 0; i < bacnet_config->n_points; i++) {
if (!strcasecmp(bacnet_config->points[i].object_name, name)) {
len++;
}
}

if (len > 0) {
points = (point_cb*)malloc(sizeof(point_cb) * len);
if (!points) {
return(NULL);
}

*length = len;
for (i = 0, ii = 0; i < bacnet_config->n_points; i++) {
if (!strcasecmp(bacnet_config->points[i].object_name, name)) {
memcpy(&points[ii], &bacnet_config->points[i], sizeof(point_cb));
ii++;
}
}
}

return(points);
}


/*
* Get points max object_instance_id.
*/
int yaml_get_points_max_object_instance_id(point_cb *points, int n_points)
{
int i, max = 0;

for (i = 0; i < n_points; i++) {
if (max < points[i].object_instance) {
max = points[i].object_instance;
}
}

return(max);
}

20 changes: 20 additions & 0 deletions bacnet-stack/apps/server/yaml_config.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
#ifndef YAML_CONFIG_H
#define YAML_CONFIG_H

#define MAX_YAML_STR_VALUE_LENGTH 51


/* point */
typedef struct _point {
char *label;
char *name;
char *units;
char object_name[MAX_YAML_STR_VALUE_LENGTH];
unsigned int object_type;
unsigned int object_instance;
} point_cb;


#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
Expand Down Expand Up @@ -83,6 +97,12 @@ extern "C" {

int yaml_config_cleanup(void);

int yaml_use_point_list_enable(void);

point_cb *yaml_get_points_by_name(char *name, int *length);

int yaml_get_points_max_object_instance_id(point_cb *points, int n_points);

#ifdef __cplusplus
}
#endif /* __cplusplus */
Expand Down
Loading
Loading