diff --git a/bacnet-stack/demo/handler/h_rp.c b/bacnet-stack/demo/handler/h_rp.c index 3aff64b7..282b89b0 100644 --- a/bacnet-stack/demo/handler/h_rp.c +++ b/bacnet-stack/demo/handler/h_rp.c @@ -32,6 +32,7 @@ #include "bacdef.h" #include "bacdcode.h" #include "bacerror.h" +#include "bacdevobjpropref.h" #include "apdu.h" #include "npdu.h" #include "abort.h" @@ -59,7 +60,7 @@ void handler_read_property_object_set( int Encode_Property_APDU( uint8_t * apdu, BACNET_OBJECT_TYPE object_type, - uint32_t object_instance, + uint32_t object_instance, BACNET_PROPERTY_ID property, int32_t array_index, BACNET_ERROR_CLASS * error_class, @@ -190,3 +191,29 @@ void handler_read_property( return; } + + +int local_read_property( + uint8_t * value, + uint8_t * status, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *Source, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) + +{ + int len = 0; + + /* Try to fetch the required property */ + len = Encode_Property_APDU(value, Source->objectIdentifier.type, + Source->objectIdentifier.instance, Source->propertyIdentifier, + Source->arrayIndex, error_class, error_code); + + if((len >= 0) && (status != NULL)){ + /* Fetch the status flags if required */ + Encode_Property_APDU(status, Source->objectIdentifier.type, + Source->objectIdentifier.instance, PROP_STATUS_FLAGS, + 0, error_class, error_code); + } + + return(len); +} diff --git a/bacnet-stack/demo/object/ai.c b/bacnet-stack/demo/object/ai.c index a85a63d5..8eae0023 100644 --- a/bacnet-stack/demo/object/ai.c +++ b/bacnet-stack/demo/object/ai.c @@ -32,10 +32,8 @@ #include "bacdcode.h" #include "bacenum.h" #include "config.h" /* the custom stuff */ +#include "ai.h" -#ifndef MAX_ANALOG_INPUTS -#define MAX_ANALOG_INPUTS 7 -#endif static float Present_Value[MAX_ANALOG_INPUTS]; diff --git a/bacnet-stack/demo/object/trendlog.c b/bacnet-stack/demo/object/trendlog.c index 56bd9ca5..05d801e2 100644 --- a/bacnet-stack/demo/object/trendlog.c +++ b/bacnet-stack/demo/object/trendlog.c @@ -1,6 +1,6 @@ /************************************************************************** * -* Copyright (C) Peter Mc Shane +* Copyright (C) 2009 Peter Mc Shane * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -113,7 +113,7 @@ typedef struct tl_log_info { uint32_t ulIntervalOffset; /* Offset from start of period for taking reading in seconds */ bool bTrigger; /* Set to 1 to cause a reading to be taken */ int iIndex; /* Current insertion point */ - bool bClocked; /* True if ulTotalRecordCount has wrapped around */ + time_t tLastDataTime; } TL_LOG_INFO; @@ -256,7 +256,7 @@ void Trend_Log_Init( int iLog; int iEntry; struct tm TempTime; - time_t Clock; + time_t tClock; if (!initialized) { initialized = true; @@ -285,10 +285,10 @@ void Trend_Log_Init( TempTime.tm_hour = 0; TempTime.tm_min = 0; TempTime.tm_sec = 0; - Clock = mktime(&TempTime); + tClock = mktime(&TempTime); for(iEntry = 0; iEntry < TL_MAX_ENTRIES; iEntry++) { - Logs[iLog][iEntry].tTimeStamp = Clock; + Logs[iLog][iEntry].tTimeStamp = tClock; Logs[iLog][iEntry].ucRecType = TL_TYPE_REAL; Logs[iLog][iEntry].Datum.fReal = (float)(iEntry + (iLog * TL_MAX_ENTRIES)); /* Put status flags with every second log */ @@ -296,9 +296,10 @@ void Trend_Log_Init( Logs[iLog][iEntry].ucStatus = 128; else Logs[iLog][iEntry].ucStatus = 0; - Clock += 900; /* advance 15 minutes */ + tClock += 900; /* advance 15 minutes */ } - + + LogInfo[iLog].tLastDataTime = tClock - 900; LogInfo[iLog].bAlignIntervals = true; LogInfo[iLog].bEnable = true; LogInfo[iLog].bStopWhenFull = false; @@ -311,12 +312,12 @@ void Trend_Log_Init( LogInfo[iLog].ulLogInterval = 900; LogInfo[iLog].ulRecordCount = 1000; LogInfo[iLog].ulTotalRecordCount = 10000; - LogInfo[iLog].bClocked = false; LogInfo[iLog].Source.deviceIndentifier.instance = Device_Object_Instance_Number(); LogInfo[iLog].Source.deviceIndentifier.type = OBJECT_DEVICE; LogInfo[iLog].Source.objectIdentifier.instance = iLog; LogInfo[iLog].Source.objectIdentifier.type = OBJECT_ANALOG_INPUT; + LogInfo[iLog].Source.arrayIndex = BACNET_ARRAY_ALL; LogInfo[iLog].Source.propertyIdentifier = PROP_PRESENT_VALUE; datetime_set_values(&LogInfo[iLog].StartTime, 2009, 1, 1, 0, 0, 0, 0); @@ -740,6 +741,7 @@ bool Trend_Log_Write_Property( case PROP_LOG_DEVICE_OBJECT_PROPERTY: memset(&TempSource, 0, sizeof(TempSource)); /* Start with clean sheet */ + TempSource.arrayIndex = BACNET_ARRAY_ALL; /* Need this so if no array index set we read properties in full */ /* First up is the object ID */ len = bacapp_decode_context_data(wp_data->application_data, wp_data->application_data_len, &value, PROP_LOG_DEVICE_OBJECT_PROPERTY); @@ -863,12 +865,19 @@ bool Trend_Log_Write_Property( break; case PROP_TRIGGER: - /* To Do - implement trigger logic as per - * 12.25.30 - */ if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { - CurrentLog->bTrigger = value.type.Boolean; - status = true; + /* We will not allow triggered operation if polling with aligning + * to the clock as that will produce non aligned readings which + * goes against the reason for selscting this mode + */ + if((CurrentLog->LoggingType == LOGGING_TYPE_POLLED) && + (CurrentLog->bAlignIntervals == true)) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NOT_CONFIGURED_FOR_TRIGGERED_LOGGING; + } else { + CurrentLog->bTrigger = value.type.Boolean; + status = true; + } } else { *error_class = ERROR_CLASS_PROPERTY; *error_code = ERROR_CODE_INVALID_DATA_TYPE; @@ -953,8 +962,6 @@ void TL_Insert_Status_Rec(int iLog, BACNET_LOG_STATUS eStatus, bool bState) CurrentLog->iIndex = 0; CurrentLog->ulTotalRecordCount++; - if(CurrentLog->ulTotalRecordCount == 0) - CurrentLog->bClocked = true; if(CurrentLog->ulRecordCount < TL_MAX_ENTRIES) CurrentLog->ulRecordCount++; @@ -968,7 +975,7 @@ void TL_Insert_Status_Rec(int iLog, BACNET_LOG_STATUS eStatus, bool bState) bool TL_Is_Enabled(int iLog) { TL_LOG_INFO *CurrentLog; - time_t Now; + time_t tNow; bool bStatus; bStatus = true; @@ -984,24 +991,24 @@ bool TL_Is_Enabled(int iLog) bStatus = false; } else if(CurrentLog->ucTimeFlags != (TL_T_START_WILD | TL_T_STOP_WILD)) { /* enabled and either 1 wild card or none */ - Now = time(NULL); + tNow = time(NULL); #if 0 printf("\nFlags - %u, Current - %u, Start - %u, Stop - %u\n", (unsigned int)CurrentLog->ucTimeFlags,(unsigned int)Now, (unsigned int)CurrentLog->tStartTime, (unsigned int)CurrentLog->tStopTime); #endif if((CurrentLog->ucTimeFlags & TL_T_START_WILD) != 0) { /* wild card start time */ - if(Now > CurrentLog->tStopTime) + if(tNow > CurrentLog->tStopTime) bStatus = false; } else if((CurrentLog->ucTimeFlags & TL_T_STOP_WILD) != 0) { /* wild card stop time */ - if(Now < CurrentLog->tStartTime) + if(tNow < CurrentLog->tStartTime) bStatus = false; } else { #if 0 printf("\nCurrent - %u, Start - %u, Stop - %u\n", (unsigned int)Now, (unsigned int)CurrentLog->tStartTime, (unsigned int)CurrentLog->tStopTime); #endif /* No wildcards so use both times */ - if((Now < CurrentLog->tStartTime) || (Now > CurrentLog->tStopTime)) + if((tNow < CurrentLog->tStartTime) || (tNow > CurrentLog->tStopTime)) bStatus = false; } } @@ -1571,10 +1578,179 @@ int TL_encode_entry(uint8_t *apdu, int iLog, int iEntry) if((pSource->ucStatus & 128) == 128) { bitstring_init(&TempBits); bitstring_set_bits_used(&TempBits, 1, 4); - /* only insert the 1st 4 bits - in BACnet order */ - bitstring_set_octet(&TempBits, 0, (pSource->Datum.ucLogStatus & 0xF0)); + /* only insert the 1st 4 bits */ + bitstring_set_octet(&TempBits, 0, (pSource->Datum.ucLogStatus & 0x0F)); iLen += encode_context_bitstring(&apdu[iLen], 2, &TempBits); } return(iLen); +} + +/**************************************************************************** + * Attempt to fetch the logged property and store it in the Trend Log * + ****************************************************************************/ + +void TL_fetch_property(int iLog) + +{ + uint8_t ValueBuf[MAX_APDU]; /* This is a big buffer in case someone selects the device object list for example */ + uint8_t StatusBuf[3]; /* Should be tag, bits unused in last octet and 1 byte of data */ + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + int iLen; + int iCount; + TL_LOG_INFO *CurrentLog; + TL_DATA_REC TempRec; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + BACNET_BIT_STRING TempBits; + + CurrentLog = &LogInfo[iLog]; + + /* Record the current time in the log entry and also in the info block + * for the log so we can figure out when the next reading is due */ + TempRec.tTimeStamp = time(NULL); + CurrentLog->tLastDataTime = TempRec.tTimeStamp; + TempRec.ucStatus = 0; + + iLen = local_read_property(ValueBuf, StatusBuf, &LogInfo[iLog].Source, &error_class, &error_code); + if(iLen < 0) { + /* Insert error code into log */ + TempRec.Datum.Error.usClass = error_class; + TempRec.Datum.Error.usCode = error_code; + TempRec.ucRecType = TL_TYPE_ERROR; + } else { + /* Decode data returned and see if we can fit it into the log */ + iLen = decode_tag_number_and_value(ValueBuf, &tag_number, &len_value_type); + switch(tag_number) { + case BACNET_APPLICATION_TAG_NULL: + TempRec.ucRecType = TL_TYPE_NULL; + break; + + case BACNET_APPLICATION_TAG_BOOLEAN: + TempRec.ucRecType = TL_TYPE_BOOL; + TempRec.Datum.ucBoolean = decode_boolean(len_value_type); + break; + + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + TempRec.ucRecType = TL_TYPE_UNSIGN; + decode_unsigned(&ValueBuf[iLen], len_value_type, &TempRec.Datum.ulUValue); + break; + + case BACNET_APPLICATION_TAG_SIGNED_INT: + TempRec.ucRecType = TL_TYPE_SIGN; + decode_signed(&ValueBuf[iLen], len_value_type, &TempRec.Datum.lSValue); + break; + + case BACNET_APPLICATION_TAG_REAL: + TempRec.ucRecType = TL_TYPE_REAL; + decode_real_safe(&ValueBuf[iLen], len_value_type, &TempRec.Datum.fReal); + break; + + case BACNET_APPLICATION_TAG_BIT_STRING: + TempRec.ucRecType = TL_TYPE_BITS; + decode_bitstring(&ValueBuf[iLen], len_value_type, &TempBits); + /* We truncate any bitstrings at 32 bits to conserve space */ + if(bitstring_bits_used(&TempBits) < 32) { + /* Store the bytes used and the bits free in the last byte */ + TempRec.Datum.Bits.ucLen = bitstring_bytes_used(&TempBits) << 4; + TempRec.Datum.Bits.ucLen |= (8 - (bitstring_bits_used(&TempBits) % 8)) & 7; + /* Fetch the octets with the bits directly */ + for(iCount = 0; iCount < bitstring_bytes_used(&TempBits); iCount++) + TempRec.Datum.Bits.ucStore[iCount] = bitstring_octet(&TempBits, iCount); + } else { + /* We will only use the first 4 octets to save space */ + TempRec.Datum.Bits.ucLen = 4 << 4; + for(iCount = 0; iCount < 4; iCount++) + TempRec.Datum.Bits.ucStore[iCount] = bitstring_octet(&TempBits, iCount); + } + break; + + case BACNET_APPLICATION_TAG_ENUMERATED: + TempRec.ucRecType = TL_TYPE_ENUM; + decode_enumerated(&ValueBuf[iLen], len_value_type, &TempRec.Datum.ulEnum); + break; + + default: + /* Fake an error response for any types we cannot handle */ + TempRec.Datum.Error.usClass = ERROR_CLASS_PROPERTY; + TempRec.Datum.Error.usCode = ERROR_CODE_DATATYPE_NOT_SUPPORTED; + TempRec.ucRecType = TL_TYPE_ERROR; + break; + } + /* Finally insert the status flags into the record */ + iLen += decode_tag_number_and_value(StatusBuf, &tag_number, &len_value_type); + decode_bitstring(&ValueBuf[iLen], len_value_type, &TempBits); + TempRec.ucStatus = 128 | bitstring_octet(&TempBits,0); + } + + Logs[iLog][CurrentLog->iIndex++] = TempRec; + if(CurrentLog->iIndex >= TL_MAX_ENTRIES) + CurrentLog->iIndex = 0; + + CurrentLog->ulTotalRecordCount++; + + if(CurrentLog->ulRecordCount < TL_MAX_ENTRIES) + CurrentLog->ulRecordCount++; +} + +/**************************************************************************** + * Check each log to see if any data needs to be recorded. * + ****************************************************************************/ + +void trend_log_timer( + uint16_t uSeconds) +{ + TL_LOG_INFO *CurrentLog; + int iCount; + time_t tNow; + + tNow = time(NULL); + for(iCount = 0; iCount < MAX_TREND_LOGS; iCount++) { + CurrentLog = &LogInfo[iCount]; + if(CurrentLog->LoggingType == LOGGING_TYPE_POLLED) { + /* For polled logs we first need to see if they are clock + * aligned or not. + */ + if(CurrentLog->bAlignIntervals == true) { + /* Aligned logging so use the combination of the interval + * and the offset to decide when to log. Also log a reading if + * more than interval time has elapsed since last reading to ensure + * we don't miss a reading if we aren't called at the precise second + * when the match occurrs. + */ +// if(((tNow % CurrentLog->ulLogInterval) >= (CurrentLog->ulIntervalOffset % CurrentLog->ulLogInterval)) && +// ((tNow - CurrentLog->tLastDataTime) >= CurrentLog->ulLogInterval)) { + if((tNow % CurrentLog->ulLogInterval) == (CurrentLog->ulIntervalOffset % CurrentLog->ulLogInterval)) { + /* Record value if time synchronised trigger condition is met + * and at least one period has elapsed. + */ + TL_fetch_property(iCount); + } else if((tNow - CurrentLog->tLastDataTime) > CurrentLog->ulLogInterval) { + /* Also record value if we have waited more than a period + * since the last reading. This ensures we take a reading as + * soon as possible after a power down if we have been off for + * more than a single period. + */ + TL_fetch_property(iCount); + } + } else if(((tNow - CurrentLog->tLastDataTime) >= CurrentLog->ulLogInterval) || + (CurrentLog->bTrigger == true)) { + /* If not aligned take a reading when we have either waited long + * enough or a trigger is set. + */ + TL_fetch_property(iCount); + } + + CurrentLog->bTrigger = false; /* Clear this every time */ + } else if(CurrentLog->LoggingType == LOGGING_TYPE_TRIGGERED) { + /* Triggered logs take a reading when the trigger is set and + * then reset the trigger to wait for the next event + */ + if(CurrentLog->bTrigger == true) { + TL_fetch_property(iCount); + CurrentLog->bTrigger = false; + } + } + } } \ No newline at end of file diff --git a/bacnet-stack/demo/readrange/main.c b/bacnet-stack/demo/readrange/main.c index efc5efe9..c17d7f85 100644 --- a/bacnet-stack/demo/readrange/main.c +++ b/bacnet-stack/demo/readrange/main.c @@ -296,7 +296,8 @@ int main( int iCount = 0; int iType = 0; int iKey; - static int iLimit[3] = { 7, 11, 7 }; + int iSecondsRun = 0; + if (((argc != 2) && (argc != 3)) || ((argc >= 2) && (strcmp(argv[1], "--help") == 0))) { @@ -352,8 +353,6 @@ int main( for (;;) { /* increment timer - exit if timed out */ current_seconds = time(NULL); - if (current_seconds != last_seconds) { - } /* returns 0 bytes on timeout */ pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); @@ -365,10 +364,16 @@ int main( /* at least one second has passed */ if (current_seconds != last_seconds) { putchar('.'); /* Just to show that time is passing... */ - last_seconds = current_seconds; - tsm_timer_milliseconds(((current_seconds - - last_seconds) * 1000)); + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); address_cache_timer(current_seconds - last_seconds); + trend_log_timer(current_seconds - last_seconds); + last_seconds = current_seconds; + /* Change the analog input PVs for testing purposes */ + for(iCount = 0; iCount < MAX_ANALOG_INPUTS; iCount++) { + Analog_Input_Present_Value_Set(iCount, iSecondsRun * (iCount + 1)); + } + + iSecondsRun++; } if (_kbhit()) { @@ -403,9 +408,10 @@ int main( } /* at least one second has passed */ if (current_seconds != last_seconds) { - tsm_timer_milliseconds(((current_seconds - - last_seconds) * 1000)); + tsm_timer_milliseconds(((current_seconds - last_seconds) * 1000)); address_cache_timer(current_seconds - last_seconds); + trend_log_timer(current_seconds - last_seconds); + last_seconds = current_seconds; } if (Error_Detected) break; diff --git a/bacnet-stack/include/ai.h b/bacnet-stack/include/ai.h index c37519b1..9b9e0c5e 100644 --- a/bacnet-stack/include/ai.h +++ b/bacnet-stack/include/ai.h @@ -32,6 +32,11 @@ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 7 +#endif + void Analog_Input_Property_Lists( const int **pRequired, const int **pOptional, diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index dc64301d..8c338bbd 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -78,6 +78,13 @@ extern "C" { uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); + + int local_read_property( + uint8_t * value, + uint8_t * status, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *Source, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); void handler_rr_object_set( BACNET_OBJECT_TYPE object_type, diff --git a/bacnet-stack/include/trendlog.h b/bacnet-stack/include/trendlog.h index 51612024..a976e524 100644 --- a/bacnet-stack/include/trendlog.h +++ b/bacnet-stack/include/trendlog.h @@ -119,6 +119,9 @@ extern "C" { BACNET_ERROR_CLASS *error_class, BACNET_ERROR_CODE *error_code); + void trend_log_timer( + uint16_t uSeconds); + #ifdef __cplusplus } #endif /* __cplusplus */