void handler_i_have( uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src) { int len = 0; BACNET_I_HAVE_DATA data; (void) service_len; (void) src; len = ihave_decode_service_request(service_request, service_len, &data); if (len != -1) { #if PRINT_ENABLED fprintf(stderr, "I-Have: %s %d from %s %u!\r\n", bactext_object_type_name(data.object_id.type), data.object_id.instance, bactext_object_type_name(data.device_id.type), data.device_id.instance); #endif } else { #if PRINT_ENABLED fprintf(stderr, "I-Have: received, but unable to decode!\n"); #endif } return; }
static void cov_lifetime_expiration_handler( unsigned index, uint32_t elapsed_seconds, uint32_t lifetime_seconds) { if (index < MAX_COV_SUBCRIPTIONS) { /* handle lifetime expiration */ if (lifetime_seconds >= elapsed_seconds) { COV_Subscriptions[index].lifetime -= elapsed_seconds; #if 0 fprintf(stderr, "COVtimer: subscription[%d].lifetime=%lu\n", index, (unsigned long) COV_Subscriptions[index].lifetime); #endif } else { COV_Subscriptions[index].lifetime = 0; } if (COV_Subscriptions[index].lifetime == 0) { /* expire the subscription */ #if PRINT_ENABLED fprintf(stderr, "COVtimer: PID=%u ", COV_Subscriptions[index].subscriberProcessIdentifier); fprintf(stderr, "%s %u ", bactext_object_type_name(COV_Subscriptions[index]. monitoredObjectIdentifier.type), COV_Subscriptions[index].monitoredObjectIdentifier.instance); fprintf(stderr, "time remaining=%u seconds ", COV_Subscriptions[index].lifetime); fprintf(stderr, "\n"); #endif COV_Subscriptions[index].flag.valid = false; COV_Subscriptions[index].dest_index = -1; cov_address_remove_unused(); if (COV_Subscriptions[index].flag.issueConfirmedNotifications) { if (COV_Subscriptions[index].invokeID) { tsm_free_invoke_id(COV_Subscriptions[index].invokeID); COV_Subscriptions[index].invokeID = 0; } } } } }
/** Handler for an Unconfirmed COV Notification. * @ingroup DSCOV * Decodes the received list of Properties to update, * and print them out with the subscription information. * @note Nothing is specified in BACnet about what to do with the * information received from Unconfirmed COV Notifications. * * @param service_request [in] The contents of the service request. * @param service_len [in] The length of the service_request. * @param src [in] BACNET_ADDRESS of the source of the message (unused) */ void handler_ucov_notification( uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src) { BACNET_COV_DATA cov_data; BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES]; BACNET_PROPERTY_VALUE *pProperty_value = NULL; int len = 0; unsigned index = 0; /* src not needed for this application */ src = src; /* create linked list to store data if more than one property value is expected */ pProperty_value = &property_value[0]; while (pProperty_value) { index++; if (index < MAX_COV_PROPERTIES) { pProperty_value->next = &property_value[index]; } else { pProperty_value->next = NULL; } pProperty_value = pProperty_value->next; } cov_data.listOfValues = &property_value[0]; #if PRINT_ENABLED fprintf(stderr, "UCOV: Received Notification!\n"); #endif /* decode the service request only */ len = cov_notify_decode_service_request(service_request, service_len, &cov_data); #if PRINT_ENABLED if (len > 0) { fprintf(stderr, "UCOV: PID=%u ", cov_data.subscriberProcessIdentifier); fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier); fprintf(stderr, "%s %u ", bactext_object_type_name(cov_data.monitoredObjectIdentifier.type), cov_data.monitoredObjectIdentifier.instance); fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining); fprintf(stderr, "\n"); pProperty_value = &property_value[0]; while (pProperty_value) { fprintf(stderr, "UCOV: "); if (pProperty_value->propertyIdentifier < 512) { fprintf(stderr, "%s ", bactext_property_name (pProperty_value->propertyIdentifier)); } else { fprintf(stderr, "proprietary %u ", pProperty_value->propertyIdentifier); } if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) { fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex); } fprintf(stderr, "\n"); pProperty_value = pProperty_value->next; } } else { fprintf(stderr, "UCOV: Unable to decode service request!\n"); } #endif }
void Analog_Value_Intrinsic_Reporting( uint32_t object_instance) { #if defined(INTRINSIC_REPORTING) BACNET_EVENT_NOTIFICATION_DATA event_data; BACNET_CHARACTER_STRING msgText; ANALOG_VALUE_DESCR *CurrentAV; unsigned int object_index; uint8_t FromState = 0; uint8_t ToState; float ExceededLimit = 0.0f; float PresentVal = 0.0f; bool SendNotify = false; object_index = Analog_Value_Instance_To_Index(object_instance); if (object_index < MAX_ANALOG_VALUES) CurrentAV = &AV_Descr[object_index]; else return; /* check limits */ if (!CurrentAV->Limit_Enable) return; /* limits are not configured */ if (CurrentAV->Ack_notify_data.bSendAckNotify) { /* clean bSendAckNotify flag */ CurrentAV->Ack_notify_data.bSendAckNotify = false; /* copy toState */ ToState = CurrentAV->Ack_notify_data.EventState; #if PRINT_ENABLED fprintf(stderr, "Send Acknotification for (%s,%d).\n", bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance); #endif /* PRINT_ENABLED */ characterstring_init_ansi(&msgText, "AckNotification"); /* Notify Type */ event_data.notifyType = NOTIFY_ACK_NOTIFICATION; /* Send EventNotification. */ SendNotify = true; } else { /* actual Present_Value */ PresentVal = Analog_Value_Present_Value(object_instance); FromState = CurrentAV->Event_State; switch (CurrentAV->Event_State) { case EVENT_STATE_NORMAL: /* A TO-OFFNORMAL event is generated under these conditions: (a) the Present_Value must exceed the High_Limit for a minimum period of time, specified in the Time_Delay property, and (b) the HighLimitEnable flag must be set in the Limit_Enable property, and (c) the TO-OFFNORMAL flag must be set in the Event_Enable property. */ if ((PresentVal > CurrentAV->High_Limit) && ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) && ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL)) { if (!CurrentAV->Remaining_Time_Delay) CurrentAV->Event_State = EVENT_STATE_HIGH_LIMIT; else CurrentAV->Remaining_Time_Delay--; break; } /* A TO-OFFNORMAL event is generated under these conditions: (a) the Present_Value must exceed the Low_Limit plus the Deadband for a minimum period of time, specified in the Time_Delay property, and (b) the LowLimitEnable flag must be set in the Limit_Enable property, and (c) the TO-NORMAL flag must be set in the Event_Enable property. */ if ((PresentVal < CurrentAV->Low_Limit) && ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) && ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL)) { if (!CurrentAV->Remaining_Time_Delay) CurrentAV->Event_State = EVENT_STATE_LOW_LIMIT; else CurrentAV->Remaining_Time_Delay--; break; } /* value of the object is still in the same event state */ CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; break; case EVENT_STATE_HIGH_LIMIT: /* Once exceeded, the Present_Value must fall below the High_Limit minus the Deadband before a TO-NORMAL event is generated under these conditions: (a) the Present_Value must fall below the High_Limit minus the Deadband for a minimum period of time, specified in the Time_Delay property, and (b) the HighLimitEnable flag must be set in the Limit_Enable property, and (c) the TO-NORMAL flag must be set in the Event_Enable property. */ if ((PresentVal < CurrentAV->High_Limit - CurrentAV->Deadband) && ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) && ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL)) { if (!CurrentAV->Remaining_Time_Delay) CurrentAV->Event_State = EVENT_STATE_NORMAL; else CurrentAV->Remaining_Time_Delay--; break; } /* value of the object is still in the same event state */ CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; break; case EVENT_STATE_LOW_LIMIT: /* Once the Present_Value has fallen below the Low_Limit, the Present_Value must exceed the Low_Limit plus the Deadband before a TO-NORMAL event is generated under these conditions: (a) the Present_Value must exceed the Low_Limit plus the Deadband for a minimum period of time, specified in the Time_Delay property, and (b) the LowLimitEnable flag must be set in the Limit_Enable property, and (c) the TO-NORMAL flag must be set in the Event_Enable property. */ if ((PresentVal > CurrentAV->Low_Limit + CurrentAV->Deadband) && ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) && ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL)) { if (!CurrentAV->Remaining_Time_Delay) CurrentAV->Event_State = EVENT_STATE_NORMAL; else CurrentAV->Remaining_Time_Delay--; break; } /* value of the object is still in the same event state */ CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; break; default: return; /* shouldn't happen */ } /* switch (FromState) */ ToState = CurrentAV->Event_State; if (FromState != ToState) { /* Event_State has changed. Need to fill only the basic parameters of this type of event. Other parameters will be filled in common function. */ switch (ToState) { case EVENT_STATE_HIGH_LIMIT: ExceededLimit = CurrentAV->High_Limit; characterstring_init_ansi(&msgText, "Goes to high limit"); break; case EVENT_STATE_LOW_LIMIT: ExceededLimit = CurrentAV->Low_Limit; characterstring_init_ansi(&msgText, "Goes to low limit"); break; case EVENT_STATE_NORMAL: if (FromState == EVENT_STATE_HIGH_LIMIT) { ExceededLimit = CurrentAV->High_Limit; characterstring_init_ansi(&msgText, "Back to normal state from high limit"); } else { ExceededLimit = CurrentAV->Low_Limit; characterstring_init_ansi(&msgText, "Back to normal state from low limit"); } break; default: ExceededLimit = 0; break; } /* switch (ToState) */ #if PRINT_ENABLED fprintf(stderr, "Event_State for (%s,%d) goes from %s to %s.\n", bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance, bactext_event_state_name(FromState), bactext_event_state_name(ToState)); #endif /* PRINT_ENABLED */ /* Notify Type */ event_data.notifyType = CurrentAV->Notify_Type; /* Send EventNotification. */ SendNotify = true; } } if (SendNotify) { /* Event Object Identifier */ event_data.eventObjectIdentifier.type = OBJECT_ANALOG_VALUE; event_data.eventObjectIdentifier.instance = object_instance; /* Time Stamp */ event_data.timeStamp.tag = TIME_STAMP_DATETIME; Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { /* fill Event_Time_Stamps */ switch (ToState) { case EVENT_STATE_HIGH_LIMIT: case EVENT_STATE_LOW_LIMIT: CurrentAV->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL] = event_data.timeStamp.value.dateTime; break; case EVENT_STATE_FAULT: CurrentAV->Event_Time_Stamps[TRANSITION_TO_FAULT] = event_data.timeStamp.value.dateTime; break; case EVENT_STATE_NORMAL: CurrentAV->Event_Time_Stamps[TRANSITION_TO_NORMAL] = event_data.timeStamp.value.dateTime; break; } } /* Notification Class */ event_data.notificationClass = CurrentAV->Notification_Class; /* Event Type */ event_data.eventType = EVENT_OUT_OF_RANGE; /* Message Text */ event_data.messageText = &msgText; /* Notify Type */ /* filled before */ /* From State */ if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) event_data.fromState = FromState; /* To State */ event_data.toState = CurrentAV->Event_State; /* Event Values */ if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { /* Value that exceeded a limit. */ event_data.notificationParams.outOfRange.exceedingValue = PresentVal; /* Status_Flags of the referenced object. */ bitstring_init(&event_data.notificationParams.outOfRange. statusFlags); bitstring_set_bit(&event_data.notificationParams.outOfRange. statusFlags, STATUS_FLAG_IN_ALARM, CurrentAV->Event_State ? true : false); bitstring_set_bit(&event_data.notificationParams.outOfRange. statusFlags, STATUS_FLAG_FAULT, false); bitstring_set_bit(&event_data.notificationParams.outOfRange. statusFlags, STATUS_FLAG_OVERRIDDEN, false); bitstring_set_bit(&event_data.notificationParams.outOfRange. statusFlags, STATUS_FLAG_OUT_OF_SERVICE, CurrentAV->Out_Of_Service); /* Deadband used for limit checking. */ event_data.notificationParams.outOfRange.deadband = CurrentAV->Deadband; /* Limit that was exceeded. */ event_data.notificationParams.outOfRange.exceededLimit = ExceededLimit; } /* add data from notification class */ Notification_Class_common_reporting_function(&event_data); /* Ack required */ if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && (event_data.ackRequired == true)) { switch (event_data.toState) { case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: case EVENT_STATE_LOW_LIMIT: CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. bIsAcked = false; CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL]. Time_Stamp = event_data.timeStamp.value.dateTime; break; case EVENT_STATE_FAULT: CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT]. bIsAcked = false; CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT]. Time_Stamp = event_data.timeStamp.value.dateTime; break; case EVENT_STATE_NORMAL: CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL]. bIsAcked = false; CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL]. Time_Stamp = event_data.timeStamp.value.dateTime; break; } } } #endif /* defined(INTRINSIC_REPORTING) */ }
/* for debugging... */ void rpm_ack_print_data( BACNET_READ_ACCESS_DATA * rpm_data) { BACNET_OBJECT_PROPERTY_VALUE object_value; /* for bacapp printing */ BACNET_PROPERTY_REFERENCE *listOfProperties; BACNET_APPLICATION_DATA_VALUE *value; bool array_value = false; if (rpm_data) { #if PRINT_ENABLED fprintf(stdout, "%s #%lu\r\n", bactext_object_type_name(rpm_data->object_type), (unsigned long) rpm_data->object_instance); fprintf(stdout, "{\r\n"); #endif listOfProperties = rpm_data->listOfProperties; while (listOfProperties) { #if PRINT_ENABLED if (listOfProperties->propertyIdentifier < 512) { fprintf(stdout, " %s: ", bactext_property_name(listOfProperties-> propertyIdentifier)); } else { fprintf(stdout, " proprietary %u: ", (unsigned) listOfProperties->propertyIdentifier); } #endif if (listOfProperties->propertyArrayIndex != BACNET_ARRAY_ALL) { #if PRINT_ENABLED fprintf(stdout, "[%d]", listOfProperties->propertyArrayIndex); #endif } value = listOfProperties->value; if (value) { #if PRINT_ENABLED if (value->next) { fprintf(stdout, "{"); array_value = true; } else { array_value = false; } #endif object_value.object_type = rpm_data->object_type; object_value.object_instance = rpm_data->object_instance; while (value) { object_value.object_property = listOfProperties->propertyIdentifier; object_value.array_index = listOfProperties->propertyArrayIndex; object_value.value = value; bacapp_print_value(stdout, &object_value); #if PRINT_ENABLED if (value->next) { fprintf(stdout, ",\r\n "); } else { if (array_value) { fprintf(stdout, "}\r\n"); } else { fprintf(stdout, "\r\n"); } } #endif value = value->next; } } else { #if PRINT_ENABLED /* AccessError */ fprintf(stdout, "BACnet Error: %s: %s\r\n", bactext_error_class_name((int) listOfProperties-> error.error_class), bactext_error_code_name((int) listOfProperties-> error.error_code)); #endif } listOfProperties = listOfProperties->next; } #if PRINT_ENABLED fprintf(stdout, "}\r\n"); #endif } }
/** Handler for an Confirmed COV Notification. * @ingroup DSCOV * Decodes the received list of Properties to update, * and print them out with the subscription information. * @note Nothing is specified in BACnet about what to do with the * information received from Confirmed COV Notifications. * * @param service_request [in] The contents of the service request. * @param service_len [in] The length of the service_request. * @param src [in] BACNET_ADDRESS of the source of the message * @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information * decoded from the APDU header of this message. */ void handler_ccov_notification( uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) { BACNET_NPDU_DATA npdu_data; BACNET_COV_DATA cov_data; BACNET_PROPERTY_VALUE property_value[MAX_COV_PROPERTIES]; BACNET_PROPERTY_VALUE *pProperty_value = NULL; unsigned index = 0; int len = 0; int pdu_len = 0; int bytes_sent = 0; BACNET_ADDRESS my_address; /* create linked list to store data if more than one property value is expected */ pProperty_value = &property_value[0]; while (pProperty_value) { index++; if (index < MAX_COV_PROPERTIES) { pProperty_value->next = &property_value[index]; } else { pProperty_value->next = NULL; } pProperty_value = pProperty_value->next; } cov_data.listOfValues = &property_value[0]; /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data); #if PRINT_ENABLED fprintf(stderr, "CCOV: Received Notification!\n"); #endif if (service_data->segmented_message) { len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); #if PRINT_ENABLED fprintf(stderr, "CCOV: Segmented message. Sending Abort!\n"); #endif goto CCOV_ABORT; } /* decode the service request only */ len = cov_notify_decode_service_request(service_request, service_len, &cov_data); #if PRINT_ENABLED if (len > 0) { fprintf(stderr, "CCOV: PID=%u ", cov_data.subscriberProcessIdentifier); fprintf(stderr, "instance=%u ", cov_data.initiatingDeviceIdentifier); fprintf(stderr, "%s %u ", bactext_object_type_name(cov_data.monitoredObjectIdentifier.type), cov_data.monitoredObjectIdentifier.instance); fprintf(stderr, "time remaining=%u seconds ", cov_data.timeRemaining); fprintf(stderr, "\n"); pProperty_value = &property_value[0]; while (pProperty_value) { fprintf(stderr, "CCOV: "); if (pProperty_value->propertyIdentifier < 512) { fprintf(stderr, "%s ", bactext_property_name (pProperty_value->propertyIdentifier)); } else { fprintf(stderr, "proprietary %u ", pProperty_value->propertyIdentifier); } if (pProperty_value->propertyArrayIndex != BACNET_ARRAY_ALL) { fprintf(stderr, "%u ", pProperty_value->propertyArrayIndex); } fprintf(stderr, "\n"); pProperty_value = pProperty_value->next; } } #endif /* bad decoding or something we didn't understand - send an abort */ if (len <= 0) { len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, ABORT_REASON_OTHER, true); #if PRINT_ENABLED fprintf(stderr, "CCOV: Bad Encoding. Sending Abort!\n"); #endif goto CCOV_ABORT; } else { len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id, SERVICE_CONFIRMED_COV_NOTIFICATION); #if PRINT_ENABLED fprintf(stderr, "CCOV: Sending Simple Ack!\n"); #endif } CCOV_ABORT: pdu_len += len; bytes_sent = datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); #if PRINT_ENABLED if (bytes_sent <= 0) { fprintf(stderr, "CCOV: Failed to send PDU (%s)!\n", strerror(errno)); } #else bytes_sent = bytes_sent; #endif return; }