diff --git a/apps/writeprop/main.c b/apps/writeprop/main.c index 5269f1bc..df9a8741 100644 --- a/apps/writeprop/main.c +++ b/apps/writeprop/main.c @@ -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 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); diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 03a0bba5..898cb0b2 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -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 */ diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index 3fd6abac..c655e435 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -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, diff --git a/test/bacnet/basic/object/device/src/main.c b/test/bacnet/basic/object/device/src/main.c index 2046bf18..cd52b1a3 100644 --- a/test/bacnet/basic/object/device/src/main.c +++ b/test/bacnet/basic/object/device/src/main.c @@ -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;