Added Time-Of-Device-Restart property to example device. (#860)

* Added Time-Of-Device-Restart property to example device.

* Added BACnetTimeStamp bacapp support for encode, decode, and parse.

* Added specific addresses handling to apps/writeprop bacwp command line.
This commit is contained in:
Steve Karg
2024-11-22 19:14:09 -06:00
committed by GitHub
parent 776b212995
commit cdda524afc
4 changed files with 242 additions and 159 deletions
+198 -136
View File
@@ -234,29 +234,37 @@ int main(int argc, char *argv[])
unsigned max_apdu = 0;
unsigned object_type = 0;
unsigned object_property = 0;
unsigned property_value_count = 0;
time_t elapsed_seconds = 0;
time_t last_seconds = 0;
time_t current_seconds = 0;
time_t timeout_seconds = 0;
bool found = false;
long dnet = -1;
BACNET_MAC_ADDRESS mac = { 0 };
BACNET_MAC_ADDRESS adr = { 0 };
BACNET_ADDRESS dest = { 0 };
bool specific_address = false;
char *value_string;
bool status = false;
int args_remaining = 0, tag_value_arg = 0, i = 0;
long property_tag;
long priority;
uint8_t context_tag = 0;
int argi = 0;
unsigned int target_args = 0;
bool debug_enabled = false;
const char *filename = NULL;
/* print help if requested */
filename = filename_remove_path(argv[0]);
for (argi = 1; argi < argc; argi++) {
if (strcmp(argv[argi], "--help") == 0) {
print_usage(filename_remove_path(argv[0]));
print_help(filename_remove_path(argv[0]));
/* print help if requested */
print_usage(filename);
print_help(filename);
return 0;
}
if (strcmp(argv[argi], "--version") == 0) {
printf(
"%s %s\n", filename_remove_path(argv[0]), BACNET_VERSION_TEXT);
printf("%s %s\n", filename, BACNET_VERSION_TEXT);
printf("Copyright (C) 2014 by Steve Karg\n"
"This is free software; see the source for copying "
"conditions.\n"
@@ -264,141 +272,195 @@ int main(int argc, char *argv[])
"FITNESS FOR A PARTICULAR PURPOSE.\n");
return 0;
}
if (strcmp(argv[argi], "--mac") == 0) {
if (++argi < argc) {
if (bacnet_address_mac_from_ascii(&mac, argv[argi])) {
specific_address = true;
}
}
} else if (strcmp(argv[argi], "--dnet") == 0) {
if (++argi < argc) {
dnet = strtol(argv[argi], NULL, 0);
if ((dnet >= 0) && (dnet <= BACNET_BROADCAST_NETWORK)) {
specific_address = true;
}
}
} else if (strcmp(argv[argi], "--dadr") == 0) {
if (++argi < argc) {
if (bacnet_address_mac_from_ascii(&adr, argv[argi])) {
specific_address = true;
}
}
} else if (strcmp(argv[argi], "--debug") == 0) {
debug_enabled = true;
} else {
if (target_args == 0) {
Target_Device_Object_Instance = strtol(argv[argi], NULL, 0);
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(
stderr, "device-instance=%u - not greater than %u\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
target_args++;
} else if (target_args == 1) {
if (bactext_object_type_strtol(argv[argi], &object_type) ==
false) {
fprintf(stderr, "object-type=%s invalid\n", argv[argi]);
return 1;
}
Target_Object_Type = object_type;
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
fprintf(
stderr, "object-type=%u - it must be less than %u\n",
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
return 1;
}
target_args++;
} else if (target_args == 2) {
Target_Object_Instance = strtol(argv[argi], NULL, 0);
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(
stderr, "object-instance=%u - not greater than %u\n",
Target_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
target_args++;
} else if (target_args == 3) {
if (bactext_property_strtol(argv[argi], &object_property) ==
false) {
fprintf(stderr, "property=%s invalid\n", argv[argi]);
return 1;
}
Target_Object_Property = object_property;
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
fprintf(
stderr, "property=%u - it must be less than %u\n",
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
return 1;
}
target_args++;
} else if (target_args == 4) {
priority = strtol(argv[argi], NULL, 0);
if ((priority < BACNET_MIN_PRIORITY) ||
(priority > BACNET_MAX_PRIORITY)) {
priority = BACNET_NO_PRIORITY;
}
Target_Object_Property_Priority = priority;
target_args++;
} else if (target_args == 5) {
Target_Object_Property_Index = strtol(argv[argi], NULL, 0);
if (Target_Object_Property_Index == -1) {
Target_Object_Property_Index = BACNET_ARRAY_ALL;
}
target_args++;
} else {
unsigned i;
for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
/* special case for context tagged values */
if (toupper(argv[argi][0]) == 'C') {
context_tag = (uint8_t)strtol(&argv[argi][1], NULL, 0);
argi++;
Target_Object_Property_Value[i].context_tag =
context_tag;
Target_Object_Property_Value[i].context_specific = true;
} else {
Target_Object_Property_Value[i].context_specific =
false;
}
if (argi >= argc) {
fprintf(stderr, "Error: not enough tag-value pairs\n");
return 1;
}
property_tag = strtol(argv[argi], NULL, 0);
argi++;
if (argi >= argc) {
fprintf(stderr, "Error: not enough tag-value pairs\n");
return 1;
}
value_string = argv[argi];
argi++;
if (property_tag < 0) {
property_tag = bacapp_known_property_tag(
Target_Object_Type, Target_Object_Property);
} else if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
fprintf(
stderr,
"Error: tag=%ld - it must be less than %u\n",
property_tag, MAX_BACNET_APPLICATION_TAG);
return 1;
}
if (property_tag >= 0) {
status = bacapp_parse_application_data(
property_tag, value_string,
&Target_Object_Property_Value[i]);
if (!status) {
/* FIXME: show the expected entry format for the tag
*/
fprintf(
stderr,
"Error: unable to parse the tag value\n");
return 1;
}
} else {
fprintf(
stderr,
"Error: parser for property %s is not "
"implemented\n",
bactext_property_name(Target_Object_Property));
return 1;
}
if (debug_enabled) {
BACNET_OBJECT_PROPERTY_VALUE debug_property;
uint8_t debug_apdu[MAX_APDU];
int debug_i, debug_len;
fprintf(
stderr, "Writing: %s=",
bactext_application_tag_name(property_tag));
debug_property.value = &Target_Object_Property_Value[i];
debug_property.array_index =
Target_Object_Property_Index;
bacapp_print_value(stderr, &debug_property);
fprintf(stderr, "\n");
debug_len = bacapp_encode_application_data(
debug_apdu, &Target_Object_Property_Value[i]);
fprintf(stderr, "APDU[%d]=", debug_len);
for (debug_i = 0; debug_i < debug_len; debug_i++) {
fprintf(stderr, "%02x ", debug_apdu[debug_i]);
}
fprintf(stderr, "\n");
}
Target_Object_Property_Value[i].next = NULL;
property_value_count++;
if (i > 0) {
/* more value pairs - link another value */
Target_Object_Property_Value[i - 1].next =
&Target_Object_Property_Value[i];
}
if (argi >= argc) {
break;
}
}
if (argi < argc) {
fprintf(
stderr, "Error: Exceeded %d tag-value pairs.\n",
MAX_PROPERTY_VALUES);
return 1;
}
}
}
}
if (argc < 9) {
print_usage(filename_remove_path(argv[0]));
if (property_value_count == 0) {
print_usage(filename);
return 0;
}
/* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
if (bactext_object_type_strtol(argv[2], &object_type) == false) {
fprintf(stderr, "object-type=%s invalid\n", argv[2]);
return 1;
}
Target_Object_Type = object_type;
Target_Object_Instance = strtol(argv[3], NULL, 0);
if (bactext_property_strtol(argv[4], &object_property) == false) {
fprintf(stderr, "property=%s invalid\n", argv[4]);
return 1;
}
Target_Object_Property = object_property;
priority = strtol(argv[5], NULL, 0);
if ((priority < BACNET_MIN_PRIORITY) || (priority > BACNET_MAX_PRIORITY)) {
priority = BACNET_NO_PRIORITY;
}
Target_Object_Property_Priority = priority;
Target_Object_Property_Index = strtol(argv[6], NULL, 0);
if (Target_Object_Property_Index == -1) {
Target_Object_Property_Index = BACNET_ARRAY_ALL;
}
if (Target_Device_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(
stderr, "device-instance=%u - not greater than %u\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) {
fprintf(
stderr, "object-type=%u - it must be less than %u\n",
Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1);
return 1;
}
if (Target_Object_Instance > BACNET_MAX_INSTANCE) {
fprintf(
stderr, "object-instance=%u - not greater than %u\n",
Target_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) {
fprintf(
stderr, "property=%u - it must be less than %u\n",
Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1);
return 1;
}
args_remaining = (argc - 7);
/* location of next arg in arg array */
tag_value_arg = 7;
for (i = 0; i < MAX_PROPERTY_VALUES; i++) {
/* special case for context tagged values */
if (toupper(argv[tag_value_arg][0]) == 'C') {
context_tag = (uint8_t)strtol(&argv[tag_value_arg][1], NULL, 0);
tag_value_arg++;
args_remaining--;
Target_Object_Property_Value[i].context_tag = context_tag;
Target_Object_Property_Value[i].context_specific = true;
} else {
Target_Object_Property_Value[i].context_specific = false;
}
property_tag = strtol(argv[tag_value_arg], NULL, 0);
tag_value_arg++;
args_remaining--;
if (args_remaining <= 0) {
fprintf(stderr, "Error: not enough tag-value pairs\n");
return 1;
}
value_string = argv[tag_value_arg];
tag_value_arg++;
args_remaining--;
if (property_tag < 0) {
property_tag = bacapp_known_property_tag(
Target_Object_Type, Target_Object_Property);
} else if (property_tag >= MAX_BACNET_APPLICATION_TAG) {
fprintf(
stderr, "Error: tag=%ld - it must be less than %u\n",
property_tag, MAX_BACNET_APPLICATION_TAG);
return 1;
}
if (property_tag >= 0) {
status = bacapp_parse_application_data(
property_tag, value_string, &Target_Object_Property_Value[i]);
if (!status) {
/* FIXME: show the expected entry format for the tag */
fprintf(stderr, "Error: unable to parse the tag value\n");
return 1;
}
} else {
fprintf(
stderr, "Error: parser for property %s is not implemented\n",
bactext_property_name(Target_Object_Property));
return 1;
}
/* Print the written value (for debug) */
#if 0
fprintf(stderr, "Writing: ");
BACNET_OBJECT_PROPERTY_VALUE dummy_opv = {
.value = &Target_Object_Property_Value[i],
.array_index = Target_Object_Property_Index,
};
bacapp_print_value(stderr, &dummy_opv);
fprintf(stderr, "\n");
uint8_t apdu[1000];
int len = bacapp_encode_application_data(apdu, &Target_Object_Property_Value[i]);
for(int q=0;q<len;q++) {
printf("%02x ", apdu[q]);
}
printf("\n");
#endif
Target_Object_Property_Value[i].next = NULL;
if (i > 0) {
Target_Object_Property_Value[i - 1].next =
&Target_Object_Property_Value[i];
}
if (args_remaining <= 0) {
break;
}
}
if (args_remaining > 0) {
fprintf(
stderr, "Error: Exceeded %d tag-value pairs.\n",
MAX_PROPERTY_VALUES);
return 1;
}
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
if (specific_address) {
bacnet_address_init(&dest, &mac, dnet, &adr);
address_add(Target_Device_Object_Instance, MAX_APDU, &dest);
}
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
Init_Service_Handlers();
dlenv_init();
atexit(datalink_cleanup);
+19 -6
View File
@@ -387,6 +387,12 @@ int bacapp_encode_application_data(
/* Empty data list */
apdu_len = 0; /* EMPTY */
break;
#if defined(BACAPP_TIMESTAMP)
case BACNET_APPLICATION_TAG_TIMESTAMP:
apdu_len =
bacapp_encode_timestamp(apdu, &value->type.Time_Stamp);
break;
#endif
#if defined(BACAPP_DATETIME)
case BACNET_APPLICATION_TAG_DATETIME:
apdu_len = bacapp_encode_datetime(apdu, &value->type.Date_Time);
@@ -1522,18 +1528,18 @@ int bacapp_decode_application_tag_value(
case BACNET_APPLICATION_TAG_EMPTYLIST:
apdu_len = 0;
break;
#if defined(BACAPP_DATETIME)
case BACNET_APPLICATION_TAG_DATETIME:
apdu_len =
bacnet_datetime_decode(apdu, apdu_size, &value->type.Date_Time);
break;
#endif
#if defined(BACAPP_TIMESTAMP)
case BACNET_APPLICATION_TAG_TIMESTAMP:
apdu_len = bacnet_timestamp_decode(
apdu, apdu_size, &value->type.Time_Stamp);
break;
#endif
#if defined(BACAPP_DATETIME)
case BACNET_APPLICATION_TAG_DATETIME:
apdu_len =
bacnet_datetime_decode(apdu, apdu_size, &value->type.Date_Time);
break;
#endif
#if defined(BACAPP_DATERANGE)
case BACNET_APPLICATION_TAG_DATERANGE:
apdu_len = bacnet_daterange_decode(
@@ -4339,6 +4345,13 @@ bool bacapp_parse_application_data(
}
break;
#endif
#if defined(BACAPP_TIMESTAMP)
case BACNET_APPLICATION_TAG_TIMESTAMP:
/* BACnetTimeStamp */
status =
bacapp_timestamp_init_ascii(&value->type.Time_Stamp, argv);
break;
#endif
#if defined(BACAPP_DATETIME)
case BACNET_APPLICATION_TAG_DATETIME:
/* BACnetDateTime */
+17 -3
View File
@@ -511,7 +511,7 @@ static const int Device_Properties_Optional[] = {
#endif
PROP_DESCRIPTION, PROP_LOCAL_TIME, PROP_UTC_OFFSET, PROP_LOCAL_DATE,
PROP_DAYLIGHT_SAVINGS_STATUS, PROP_LOCATION, PROP_ACTIVE_COV_SUBSCRIPTIONS,
PROP_SERIAL_NUMBER,
PROP_SERIAL_NUMBER, PROP_TIME_OF_DEVICE_RESTART,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS, PROP_TIME_SYNCHRONIZATION_INTERVAL,
PROP_ALIGN_INTERVALS, PROP_INTERVAL_OFFSET,
@@ -595,6 +595,7 @@ static char Serial_Number[MAX_DEV_DESC_LEN + 1] =
/* static uint8_t Max_Segments_Accepted = 0; */
/* VT_Classes_Supported */
/* Active_VT_Sessions */
static BACNET_TIMESTAMP Time_Of_Device_Restart;
static BACNET_TIME Local_Time; /* rely on OS, if there is one */
static BACNET_DATE Local_Date; /* rely on OS, if there is one */
/* NOTE: BACnet UTC Offset is inverse of common practice.
@@ -1565,6 +1566,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_TIME_OF_DEVICE_RESTART:
apdu_len =
bacapp_encode_timestamp(&apdu[0], &Time_Of_Device_Restart);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -1684,8 +1689,9 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
#endif
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
len = bacapp_decode_known_property(
wp_data->application_data, wp_data->application_data_len, &value,
wp_data->object_type, wp_data->object_property);
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
@@ -1892,6 +1898,14 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
break;
#endif
case PROP_TIME_OF_DEVICE_RESTART:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_TIMESTAMP);
if (status) {
bacapp_timestamp_copy(
&Time_Of_Device_Restart, &value.type.Time_Stamp);
}
break;
default:
if (property_lists_member(
Device_Properties_Required, Device_Properties_Optional,
+8 -14
View File
@@ -52,16 +52,15 @@ static void test_Device_Data_Sharing(void)
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len > 0) {
test_len = bacapp_decode_application_data(
test_len = bacapp_decode_known_property(
rpdata.application_data, (uint8_t)rpdata.application_data_len,
&value);
if ((rpdata.object_property == PROP_PRIORITY_ARRAY) ||
(rpdata.object_property == PROP_OBJECT_LIST)) {
/* FIXME: known fail to decode */
&value, rpdata.object_type, rpdata.object_property);
if ((test_len < len) && (rpdata.array_index == BACNET_ARRAY_ALL)) {
/* this is one element of an array - assume it is accurate */
len = test_len;
}
zassert_equal(
test_len, len, "property '%s': failed to decode!\n",
test_len, len, "property '%s': ReadProperty decode failure!\n",
bactext_property_name(rpdata.object_property));
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;
@@ -91,16 +90,11 @@ static void test_Device_Data_Sharing(void)
"property '%s': failed to ReadProperty!\n",
bactext_property_name(rpdata.object_property));
if (len > 0) {
test_len = bacapp_decode_application_data(
test_len = bacapp_decode_known_property(
rpdata.application_data, (uint8_t)rpdata.application_data_len,
&value);
if (len != test_len) {
printf(
"property '%s': failed to decode!\n",
bactext_property_name(rpdata.object_property));
}
&value, rpdata.object_type, rpdata.object_property);
zassert_equal(
test_len, len, "property '%s': failed to decode!\n",
test_len, len, "property '%s': ReadProperty decode failure!\n",
bactext_property_name(rpdata.object_property));
/* check WriteProperty properties */
wpdata.object_type = rpdata.object_type;