From c69686c99b49b13e4df0b28082229ddaac55552d Mon Sep 17 00:00:00 2001 From: tbrennan3 Date: Fri, 14 May 2010 14:44:27 +0000 Subject: [PATCH] Fixed 2 problems that Steve found: 1) Did not properly handle when RPM was not supported. 2) Did not handle small MAX_APDU values well. --- bacnet-stack/demo/epics/main.c | 157 +++++++++++++++++++++++---------- 1 file changed, 109 insertions(+), 48 deletions(-) diff --git a/bacnet-stack/demo/epics/main.c b/bacnet-stack/demo/epics/main.c index 04aba978..36ac816e 100644 --- a/bacnet-stack/demo/epics/main.c +++ b/bacnet-stack/demo/epics/main.c @@ -68,8 +68,13 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; /* target information converted from command line */ static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; static BACNET_ADDRESS Target_Address; +/* = { 6, { 127, 0, 0, 1, 0xBA, 0xC0, 0 }, 0 }; loopback address to talk to myself */ + /* any errors are picked up in main loop */ static bool Error_Detected = false; +static uint16_t Last_Error_Class = 0; +static uint16_t Last_Error_Code = 0; +static bool Has_RPM = true; /* Assume device can do RPM, to start */ static EPICS_STATES myState = INITIAL_BINDING; /* any valid RP or RPM data returned is put here */ @@ -104,6 +109,14 @@ static uint32_t Property_List_Index = 0; static int32_t Property_List[MAX_PROPS + 2]; /* This normally points to Property_List. */ static const int *pPropList = NULL; +#define MINIMAL_PROPLIST_SIZE 4 +static int32_t MinimalPropList[MINIMAL_PROPLIST_SIZE] = +{ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + -1 +}; /* When we have to walk through an array of things, like ObjectIDs or * Subordinate_Annotations, one RP call at a time, use these for indexing. @@ -132,14 +145,14 @@ static void MyErrorHandler( (void) src; (void) invoke_id; #if PRINT_ERRORS - printf("-- BACnet Error: %s: %s\r\n", - bactext_error_class_name(error_class), - bactext_error_code_name(error_code)); -#else - (void) error_class; - (void) error_code; + if ( ShowValues ) + fprintf(stderr, "-- BACnet Error: %s: %s\r\n", + bactext_error_class_name(error_class), + bactext_error_code_name(error_code)); #endif Error_Detected = true; + Last_Error_Class = error_class; + Last_Error_Code = error_code; } void MyAbortHandler( @@ -154,13 +167,16 @@ void MyAbortHandler( (void) server; #if PRINT_ERRORS /* It is normal for this to fail, so don't print. */ - if ((myState != GET_ALL_RESPONSE) && !IsLongArray) - printf("-- BACnet Abort: %s ", + if ((myState != GET_ALL_RESPONSE) && !IsLongArray && ShowValues ) + fprintf(stderr, "-- BACnet Abort: %s \r\n", bactext_abort_reason_name(abort_reason)); -#else - (void) abort_reason; #endif Error_Detected = true; + Last_Error_Class = ERROR_CLASS_SERVICES; + if ( abort_reason < MAX_BACNET_ABORT_REASON ) + Last_Error_Code = (ERROR_CODE_ABORT_BUFFER_OVERFLOW -1) + abort_reason; + else + Last_Error_Code = ERROR_CODE_ABORT_OTHER; } void MyRejectHandler( @@ -172,11 +188,15 @@ void MyRejectHandler( (void) src; (void) invoke_id; #if PRINT_ERRORS - printf("BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); -#else - (void) reject_reason; + if ( ShowValues ) + fprintf(stderr, "BACnet Reject: %s\r\n", bactext_reject_reason_name(reject_reason)); #endif Error_Detected = true; + Last_Error_Class = ERROR_CLASS_SERVICES; + if ( reject_reason < MAX_BACNET_REJECT_REASON ) + Last_Error_Code = (ERROR_CODE_REJECT_BUFFER_OVERFLOW -1) + reject_reason; + else + Last_Error_Code = ERROR_CODE_REJECT_OTHER; } void MyReadPropertyAckHandler( @@ -365,7 +385,7 @@ void PrintReadPropertyData( value = rpm_property->value; if (value == NULL) { /* Then we print the error information */ - fprintf(stdout, "? -- BACnet Error: %s: %s\r\n", + fprintf(stderr, "? -- BACnet Error: %s: %s\r\n", bactext_error_class_name((int) rpm_property->error.error_class), bactext_error_code_name((int) rpm_property->error.error_code)); return; @@ -546,8 +566,11 @@ static uint8_t Read_Properties( { uint8_t invoke_id = 0; struct special_property_list_t PropertyListStruct; + int i; - if (Property_List_Length == 0) { + if ( ( !Has_RPM && ( Property_List_Index == 0 ) ) || + ( Property_List_Length == 0) ) + { /* If we failed to get the Properties with RPM, just settle for what we * know is the fixed list of Required (only) properties. * In practice, this should only happen for simple devices that don't @@ -558,9 +581,14 @@ static uint8_t Read_Properties( if (pPropList != NULL) { Property_List_Length = PropertyListStruct.Required.count; } else { - fprintf(stdout, " -- No Properties available for %s \r\n", - bactext_object_type_name(pMyObject->type)); + fprintf(stdout, " -- Just Minimal Properties: \r\n" ); + pPropList = MinimalPropList; + Property_List_Length = MINIMAL_PROPLIST_SIZE -1; } + /* Copy this list for later one-by-one processing */ + for ( i = 0; i < Property_List_Length; i++ ) + Property_List[i] = pPropList[i]; + Property_List[i] = -1; /* Just to be sure we terminate */ } else pPropList = Property_List; @@ -576,7 +604,7 @@ static uint8_t Read_Properties( array_index = Walked_List_Index; } } else { - printf(" %s: ", bactext_property_name(prop)); + fprintf(stdout, " %s: ", bactext_property_name(prop)); array_index = BACNET_ARRAY_ALL; switch (prop) { @@ -651,7 +679,7 @@ EPICS_STATES ProcessRPMData( free(old_value); } } else { - printf(" %s: ", + fprintf(stdout, " %s: ", bactext_property_name(rpm_property->propertyIdentifier)); PrintReadPropertyData(rpm_property); } @@ -741,6 +769,22 @@ int CheckCommandLineArgs( return 0; /* All OK if we reach here */ } +/* Initialize fields for a new Object */ +void StartNextObject( BACNET_READ_ACCESS_DATA *rpm_object, BACNET_OBJECT_ID *pNewObject ) +{ + BACNET_PROPERTY_REFERENCE *rpm_property; + Error_Detected = false; + Property_List_Index = Property_List_Length = 0; + rpm_object->object_type = pNewObject->type; + rpm_object->object_instance = pNewObject->instance; + rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); + rpm_object->listOfProperties = rpm_property; + assert(rpm_property); + rpm_property->propertyIdentifier = PROP_ALL; + /* Start with a count of the array size */ + rpm_property->propertyArrayIndex = 0; +} + /** Main function of the bacepics program. * * @see Device_Set_Object_Instance_Number, Keylist_Create, address_init, @@ -773,7 +817,6 @@ int main( BACNET_OBJECT_ID myObject; uint8_t buffer[MAX_PDU] = { 0 }; BACNET_READ_ACCESS_DATA *rpm_object; - BACNET_PROPERTY_REFERENCE *rpm_property; KEY nextKey; CheckCommandLineArgs(argc, argv); /* Won't return if there is an issue. */ @@ -784,6 +827,7 @@ int main( address_init(); Init_Service_Handlers(); dlenv_init(); + /* bip_set_port( 0xBAC0 ); Set back to std BIP port */ /* configure the timeout values */ current_seconds = time(NULL); timeout_seconds = (apdu_timeout() / 1000) * apdu_retries(); @@ -794,6 +838,10 @@ int main( if (!found) { Send_WhoIs(Target_Device_Object_Instance, Target_Device_Object_Instance); + /* In an Alternate universe, where we talk to ourselves: + * address_add_binding( Target_Device_Object_Instance, max_apdu, + * &Target_Address); + */ } printf("List of Objects in test device:\r\n"); /* Print Opening brace, then kick off the Device Object */ @@ -830,36 +878,30 @@ int main( /* increment timer - exit if timed out */ elapsed_seconds += (current_seconds - last_seconds); if (elapsed_seconds > timeout_seconds) { - printf("\rError: APDU Timeout!\r\n"); + fprintf(stderr, "\rError: APDU Timeout!\r\n"); break; } /* else, loop back and try again */ continue; } else + { myState = GET_ALL_REQUEST; + rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); + assert(rpm_object); + } break; case GET_ALL_REQUEST: case GET_LIST_OF_ALL_REQUEST: /* differs in ArrayIndex only */ - Error_Detected = false; - Property_List_Index = Property_List_Length = 0; /* Update times; aids single-step debugging */ last_seconds = current_seconds; - rpm_object = calloc(1, sizeof(BACNET_READ_ACCESS_DATA)); - assert(rpm_object); - rpm_object->object_type = myObject.type; - rpm_object->object_instance = myObject.instance; - rpm_property = calloc(1, sizeof(BACNET_PROPERTY_REFERENCE)); - rpm_object->listOfProperties = rpm_property; - assert(rpm_property); - rpm_property->propertyIdentifier = PROP_ALL; - if (myState == GET_LIST_OF_ALL_REQUEST) - rpm_property->propertyArrayIndex = 0; /* Get count of arrays */ - else - rpm_property->propertyArrayIndex = -1; /* optional: None */ + StartNextObject( rpm_object, &myObject ); + /* Override the default and set the optional array index to "None" */ + if (myState == GET_ALL_REQUEST) + rpm_object->listOfProperties->propertyArrayIndex = -1; invoke_id = Send_Read_Property_Multiple_Request(buffer, MAX_PDU, - Target_Device_Object_Instance, rpm_object); + Target_Device_Object_Instance, rpm_object); if (invoke_id > 0) { if (myState == GET_LIST_OF_ALL_REQUEST) myState = GET_LIST_OF_ALL_RESPONSE; @@ -895,19 +937,31 @@ int main( } else if (tsm_invoke_id_free(invoke_id)) { invoke_id = 0; if (Error_Detected) { /* The normal case for Device Object */ - /* Try again, just to get a list of properties. */ - if (myState == GET_ALL_RESPONSE) - myState = GET_LIST_OF_ALL_REQUEST; - /* Else it may be that RPM is not implemented. */ - else + /* Was it because the Device can't do RPM? */ + if ( Last_Error_Code == ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE ) + { + Has_RPM = false; myState = GET_PROPERTY_REQUEST; - } else + } + /* Try again, just to get a list of properties. */ + else if (myState == GET_ALL_RESPONSE) + myState = GET_LIST_OF_ALL_REQUEST; + /* Else drop back to RP. */ + else + { + myState = GET_PROPERTY_REQUEST; + StartNextObject( rpm_object, &myObject ); + } + } + else if ( Has_RPM ) myState = GET_ALL_REQUEST; /* Let's try again */ + else + myState = GET_PROPERTY_REQUEST; } else if (tsm_invoke_id_failed(invoke_id)) { fprintf(stderr, "\rError: TSM Timeout!\r\n"); tsm_free_invoke_id(invoke_id); invoke_id = 0; - myState = GET_ALL_REQUEST; /* Let's try again */ + myState = GET_ALL_REQUEST; /* Let's try again */ } else if (Error_Detected) { /* Don't think we'll ever actually reach this point. */ invoke_id = 0; @@ -965,7 +1019,7 @@ int main( } /* if ( pPropList[Property_List_Index] == PROP_OBJECT_LIST ) */ /* { */ -/* if ( !Using_Walked_List ) +/* if ( !Using_Walked_List ) */ /* { */ /* Just switched */ /* Using_Walked_List = true; */ @@ -975,6 +1029,7 @@ int main( myState = GET_PROPERTY_REQUEST; /* Go fetch next Property */ } else if (tsm_invoke_id_free(invoke_id)) { invoke_id = 0; + myState = GET_PROPERTY_REQUEST; if (Error_Detected) { if (IsLongArray) { /* Change to using a Walked List and retry this property */ @@ -985,10 +1040,10 @@ int main( fprintf(stdout, " -- Failed to get %s \r\n", bactext_property_name(pPropList [Property_List_Index])); - Property_List_Index++; + if ( ++Property_List_Index >= Property_List_Length ) + myState = NEXT_OBJECT; /* Give up and move on to the next. */ } } - myState = GET_PROPERTY_REQUEST; } else if (tsm_invoke_id_failed(invoke_id)) { fprintf(stderr, "\rError: TSM Timeout!\r\n"); tsm_free_invoke_id(invoke_id); @@ -1029,7 +1084,13 @@ int main( printf( " -- Structured View %d \n", myObject.instance ); */ } - myState = GET_ALL_REQUEST; + if ( Has_RPM ) + myState = GET_ALL_REQUEST; + else + { + myState = GET_PROPERTY_REQUEST; + StartNextObject( rpm_object, &myObject ); + } } while (myObject.type == OBJECT_DEVICE); /* Else, don't re-do the Device Object; move to the next object. */ @@ -1045,7 +1106,7 @@ int main( /* increment timer - exit if timed out */ elapsed_seconds += (current_seconds - last_seconds); if (elapsed_seconds > timeout_seconds) { - printf("\rError: APDU Timeout!\r\n"); + fprintf(stderr, "\rError: APDU Timeout!\r\n"); break; } }