diff --git a/bacnet-stack/demo/handler/h_rr.c b/bacnet-stack/demo/handler/h_rr.c index 5c9f1f9b..95f7ad2c 100644 --- a/bacnet-stack/demo/handler/h_rr.c +++ b/bacnet-stack/demo/handler/h_rr.c @@ -69,19 +69,21 @@ int Encode_RR_payload( /* handle each object type */ if (pRequest->object_type < MAX_BACNET_OBJECT_TYPE) info_fn_ptr = get_rr_info[pRequest->object_type]; - + if ((info_fn_ptr != NULL) && (info_fn_ptr(pRequest->object_instance, pRequest->object_property, &PropInfo, error_class, error_code) != false)) { /* We try and do some of the more generic error checking here to cut down on duplication of effort */ - if(((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) && (pRequest->array_index != 0)) { + if((pRequest->RequestType == RR_BY_POSITION) && (pRequest->Range.RefIndex == 0)) {/* First index is 1 so can't accept 0 */ + *error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ + } else if(((PropInfo.RequestTypes & RR_ARRAY_OF_LISTS) == 0) && (pRequest->array_index != 0) && (pRequest->array_index != BACNET_ARRAY_ALL)) { /* Array access attempted on a non array property */ *error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; } else if((pRequest->RequestType != RR_READ_ALL) && ((PropInfo.RequestTypes & pRequest->RequestType) == 0)) { - /* By Time or By Sequence not supported */ + /* By Time or By Sequence not supported - By Position is always required */ *error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ } - else if(pRequest->Count == 0) { /* Count cannot be zero */ + else if((pRequest->Count == 0) && (pRequest->RequestType != RR_READ_ALL)) { /* Count cannot be zero */ *error_code = ERROR_CODE_OTHER; /* I couldn't see anything more appropriate so... */ } else if(PropInfo.Handler != NULL) { @@ -128,7 +130,7 @@ void handler_read_range( #endif goto RR_ABORT; } - + memset(&data, 0, sizeof(data)); /* start with blank canvas */ len = rr_decode_service_request(service_request, service_len, &data); #if PRINT_ENABLED if (len <= 0) diff --git a/bacnet-stack/demo/object/trendlog.c b/bacnet-stack/demo/object/trendlog.c index a9d690dd..5e0e6f6a 100644 --- a/bacnet-stack/demo/object/trendlog.c +++ b/bacnet-stack/demo/object/trendlog.c @@ -56,16 +56,24 @@ typedef struct tl_error { /* Bit string of up to 32 bits for Trend Log storage */ typedef struct tl_bits { - uint8_t ucLen; + uint8_t ucLen; /* bytes used in upper nibble/bits free in lower nibble */ uint8_t ucStore[4]; } TL_BITS; -/* Storage structure for Trend Log data */ +/* Storage structure for Trend Log data + * + * Note. I've tried to minimise the storage requirements here + * as the memory requirements for logging in embedded + * implementations are frequently a big issue. For PC or + * embedded Linux type setupz this may seem like overkill + * but if you have limited memory and need to squeeze as much + * logging capacity as possible every little byte counts! + */ typedef struct tl_data_record { - time_t tTimeStamp; /* When the event occurred */ + time_t tTimeStamp; /* When the event occurred */ uint8_t ucRecType; /* What type of Event */ - uint8_t ucStatus; /* Optional Status for read value */ + uint8_t ucStatus; /* Optional Status for read value in b0-b2, b7 = 1 if status is used */ union { uint8_t ucLogStatus; /* Change of log state flags */ uint8_t ucBoolean; /* Stored boolean value */ @@ -96,13 +104,13 @@ typedef struct tl_log_info { time_t tStopTime; /* Local time working copy of stop time */ uint8_t ucTimeFlags; /* Shorthand info on times */ BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Source; /* Where the data comes from */ - uint32_t ulLogInterval; /* Time between entries in 1/100s */ + uint32_t ulLogInterval; /* Time between entries in seconds */ bool bStopWhenFull; /* Log halts when full if true */ uint32_t ulRecordCount; /* Count of items currently in the buffer */ uint32_t ulTotalRecordCount; /* Count of all items that have ever been inserted into the buffer */ BACNET_LOGGING_TYPE LoggingType; /* Polled/cov/triggered */ bool bAlignIntervals; /* If true align to the clock */ - uint32_t ulIntervalOffset; /* Offset from start of period for taking reading */ + 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 */ } TL_LOG_INFO; @@ -282,7 +290,11 @@ void Trend_Log_Init( Logs[iLog][iEntry].tTimeStamp = Clock; Logs[iLog][iEntry].ucRecType = TL_TYPE_REAL; Logs[iLog][iEntry].Datum.fReal = (float)(iEntry + (iLog * TL_MAX_ENTRIES)); - Logs[iLog][iEntry].ucStatus = 0; + /* Put status flags with every second log */ + if((iLog & 1) == 0) + Logs[iLog][iEntry].ucStatus = 128; + else + Logs[iLog][iEntry].ucStatus = 0; Clock += 900; /* advance 15 minutes */ } @@ -305,9 +317,9 @@ void Trend_Log_Init( LogInfo[iLog].Source.objectIdentifier.type = OBJECT_ANALOG_INPUT; LogInfo[iLog].Source.propertyIdentifier = PROP_PRESENT_VALUE; - datetime_set_values(&LogInfo[iLog].StartTime, 109, 1, 1, 0, 0, 0, 0); + datetime_set_values(&LogInfo[iLog].StartTime, 2009, 1, 1, 0, 0, 0, 0); LogInfo[iLog].tStartTime = TL_BAC_Time_To_Local(&LogInfo[iLog].StartTime); - datetime_set_values(&LogInfo[iLog].StopTime, 109, 11, 22, 23, 59, 59, 99); + datetime_set_values(&LogInfo[iLog].StopTime, 2009, 12, 22, 23, 59, 59, 99); LogInfo[iLog].tStopTime = TL_BAC_Time_To_Local(&LogInfo[iLog].StopTime); } } @@ -390,11 +402,11 @@ int Trend_Log_Encode_Property_APDU( break; case PROP_RECORD_COUNT: - apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentLog->ulRecordCount); + apdu_len += encode_application_unsigned(&apdu[0], CurrentLog->ulRecordCount); break; case PROP_TOTAL_RECORD_COUNT: - apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentLog->ulTotalRecordCount); + apdu_len += encode_application_unsigned(&apdu[0], CurrentLog->ulTotalRecordCount); break; case PROP_EVENT_STATE: @@ -445,12 +457,12 @@ int Trend_Log_Encode_Property_APDU( * deviceIdentifier [3] BACnetObjectIdentifier OPTIONAL * } */ - apdu_len += bacapp_encode_device_obj_property_ref(&apdu[apdu_len], &CurrentLog->Source); + apdu_len += bacapp_encode_device_obj_property_ref(&apdu[0], &CurrentLog->Source); break; case PROP_LOG_INTERVAL: /* We only log to 1 sec accuracy so must multiply by 100 before passing it on */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentLog->ulLogInterval * 100); + apdu_len += encode_application_unsigned(&apdu[0], CurrentLog->ulLogInterval * 100); break; case PROP_ALIGN_INTERVALS: @@ -459,7 +471,7 @@ int Trend_Log_Encode_Property_APDU( case PROP_INTERVAL_OFFSET: /* We only log to 1 sec accuracy so must multiply by 100 before passing it on */ - apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentLog->ulIntervalOffset * 100); + apdu_len += encode_application_unsigned(&apdu[0], CurrentLog->ulIntervalOffset * 100); break; case PROP_TRIGGER: @@ -646,10 +658,12 @@ bool Trend_Log_Write_Property( wp_data->application_data_len - len, &value); if (len && value.tag == BACNET_APPLICATION_TAG_TIME) { + /* First record the current enable state of the log */ + bEffectiveEnable = TL_Is_Enabled(wp_data->object_instance); CurrentLog->StartTime.date = TempDate; /* Safe to copy the date now */ CurrentLog->StartTime.time = value.type.Time; - if (datetime_wildcard(&CurrentLog->StartTime)) { + if (datetime_wildcard_present(&CurrentLog->StartTime)) { /* Mark start time as wild carded */ CurrentLog->ucTimeFlags |= TL_T_START_WILD; CurrentLog->tStartTime = 0; @@ -658,7 +672,17 @@ bool Trend_Log_Write_Property( CurrentLog->ucTimeFlags &= ~TL_T_START_WILD; CurrentLog->tStartTime = TL_BAC_Time_To_Local(&CurrentLog->StartTime); } - + + if(bEffectiveEnable != TL_Is_Enabled(wp_data->object_instance)) { + /* Enable status has changed because of time update */ + if(bEffectiveEnable == true) { + /* Say we went from enabled to disabled */ + TL_Insert_Status_Rec(wp_data->object_instance, LOG_STATUS_LOG_DISABLED, true); + } else { + /* Say we went from disabled to enabled */ + TL_Insert_Status_Rec(wp_data->object_instance, LOG_STATUS_LOG_DISABLED, false); + } + } status = true; } else { *error_class = ERROR_CLASS_PROPERTY; @@ -680,19 +704,31 @@ bool Trend_Log_Write_Property( wp_data->application_data_len - len, &value); if (len && value.tag == BACNET_APPLICATION_TAG_TIME) { + /* First record the current enable state of the log */ + bEffectiveEnable = TL_Is_Enabled(wp_data->object_instance); CurrentLog->StopTime.date = TempDate; /* Safe to copy the date now */ CurrentLog->StopTime.time = value.type.Time; - if (datetime_wildcard(&CurrentLog->StopTime)) { + if (datetime_wildcard_present(&CurrentLog->StopTime)) { /* Mark stop time as wild carded */ CurrentLog->ucTimeFlags |= TL_T_STOP_WILD; CurrentLog->tStopTime = 0xFFFFFFFF; /* Fixme: how do we set this to max for time_t ? */ } else { /* Clear wild card flag and set time in local format */ CurrentLog->ucTimeFlags &= ~TL_T_STOP_WILD; - CurrentLog->tStartTime = TL_BAC_Time_To_Local(&CurrentLog->StartTime); + CurrentLog->tStopTime = TL_BAC_Time_To_Local(&CurrentLog->StopTime); } + if(bEffectiveEnable != TL_Is_Enabled(wp_data->object_instance)) { + /* Enable status has changed because of time update */ + if(bEffectiveEnable == true) { + /* Say we went from enabled to disabled */ + TL_Insert_Status_Rec(wp_data->object_instance, LOG_STATUS_LOG_DISABLED, true); + } else { + /* Say we went from disabled to enabled */ + TL_Insert_Status_Rec(wp_data->object_instance, LOG_STATUS_LOG_DISABLED, false); + } + } status = true; } else { *error_class = ERROR_CLASS_PROPERTY; @@ -851,22 +887,25 @@ void TrendLog_Init( { } -bool Trend_Log_GetRRInfo( - uint32_t Object, /* Which particular object - obviously not important for device object */ +bool TrendLogGetRRInfo( + uint32_t Object, /* Which particular object */ BACNET_PROPERTY_ID Property, /* Which property */ RR_PROP_INFO *pInfo, /* Where to put the information */ BACNET_ERROR_CLASS *error_class, BACNET_ERROR_CODE *error_code) { - if(Property == PROP_LOG_BUFFER) { + if(Object >= MAX_TREND_LOGS) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + } else if(Property == PROP_LOG_BUFFER) { pInfo->RequestTypes = RR_BY_POSITION | RR_BY_TIME | RR_BY_SEQUENCE; - pInfo->Handler = NULL; + pInfo->Handler = rr_trend_log_encode; return(true); } else { *error_class = ERROR_CLASS_SERVICES; *error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST; } - + return(false); } @@ -889,17 +928,19 @@ void TL_Insert_Status_Rec(int iLog, BACNET_LOG_STATUS eStatus, bool bState) TempRec.ucRecType = TL_TYPE_STATUS; TempRec.ucStatus = 0; TempRec.Datum.ucLogStatus = 0; + /* Note we set the bits in correct order so that we can place them directly + * into the bitstring structure later on when we have to encode them */ switch(eStatus) { case LOG_STATUS_LOG_DISABLED: if(bState) - TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_DISABLED; + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_DISABLED; break; case LOG_STATUS_BUFFER_PURGED: if(bState) - TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_BUFFER_PURGED; + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_BUFFER_PURGED; break; case LOG_STATUS_LOG_INTERRUPTED: - TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_INTERRUPTED; + TempRec.Datum.ucLogStatus = 1 << LOG_STATUS_LOG_INTERRUPTED; break; default: break; @@ -927,6 +968,9 @@ bool TL_Is_Enabled(int iLog) bStatus = true; CurrentLog = &LogInfo[iLog]; +#if 0 + printf("\nFlags - %u, Start - %u, Stop - %u\n", (unsigned int)CurrentLog->ucTimeFlags,(unsigned int)CurrentLog->tStartTime, (unsigned int)CurrentLog->tStopTime); +#endif if(CurrentLog->bEnable == false) { /* Not enabled so time is irrelevant */ bStatus = false; @@ -936,15 +980,22 @@ bool TL_Is_Enabled(int iLog) } else if(CurrentLog->ucTimeFlags != (TL_T_START_WILD | TL_T_STOP_WILD)) { /* enabled and either 1 wild card or none */ Now = time(NULL); - if((CurrentLog->ucTimeFlags | TL_T_START_WILD) != 0) { +#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) bStatus = false; - } else if((CurrentLog->ucTimeFlags | TL_T_STOP_WILD) != 0) { + } else if((CurrentLog->ucTimeFlags & TL_T_STOP_WILD) != 0) { /* wild card stop time */ if(Now < 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)) bStatus = false; } @@ -961,12 +1012,285 @@ time_t TL_BAC_Time_To_Local(BACNET_DATE_TIME *SourceTime) { struct tm LocalTime; - LocalTime.tm_year = SourceTime->date.year; - LocalTime.tm_mon = SourceTime->date.month + 1; + LocalTime.tm_year = SourceTime->date.year - 1900; /* We store BACnet year in full format */ + LocalTime.tm_mon = SourceTime->date.month - 1; LocalTime.tm_mday = SourceTime->date.day; LocalTime.tm_hour = SourceTime->time.hour; LocalTime.tm_min = SourceTime->time.min; LocalTime.tm_sec = SourceTime->time.sec; return(mktime(&LocalTime)); +} + +/***************************************************************************** + * Convert a local time in seconds since the local epoch into a BACnet time * + *****************************************************************************/ + +void TL_Local_Time_To_BAC(BACNET_DATE_TIME *DestTime, time_t SourceTime) +{ + struct tm *TempTime; + + TempTime = localtime(&SourceTime); + + DestTime->date.year = TempTime->tm_year + 1900; + DestTime->date.month = TempTime->tm_mon + 1; + DestTime->date.day = TempTime->tm_mday; + /* BACnet is 1 to 7 = Monday to Sunday + * Windows is days from Sunday 0 - 6 so we + * have to adjust */ + if(TempTime->tm_wday == 0) + DestTime->date.wday = 7; + else + DestTime->date.wday = TempTime->tm_wday; + DestTime->time.hour = TempTime->tm_hour; + DestTime->time.min = TempTime->tm_min; + DestTime->time.sec = TempTime->tm_sec; + DestTime->time.hundredths = 0; +} + +/**************************************************************************** + * Build a list of Trend Log entries from the Log Buffer property as * + * required for the ReadsRange functionality. * + * * + * We have to support By Position, By Sequence and By Time requests. * + * * + * We need to treat the address cache as a contiguous array but in reality * + * it could be sparsely populated. We can get the count but we can only * + * extract entries by doing a linear scan starting from the first entry in * + * the cache and picking them off one by one. * + * * + * We do assume the list cannot change whilst we are accessing it so would * + * not be multithread safe if there are other tasks that write to the log. * + * * + * We take the simple approach here to filling the buffer by taking a max * + * size for a single entry and then stopping if there is less than that * + * left in the buffer. You could build each entry in a seperate buffer and * + * determine the exact length before copying but this is time consuming, * + * requires more memory and would probably only let you sqeeeze one more * + * entry in on occasion. The value is calculated as 10 bytes for the time * + * stamp + 6 bytes for our largest data item (bit string capped at 32 bits) * + * + 3 bytes for the status flags + 4 for the context tags to give 23. * + ****************************************************************************/ + +#define TL_MAX_ENC 23 /* Maximum size of encoded log entry, see above */ + +int rr_trend_log_encode( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + /* Initialise result flags to all false */ + bitstring_init(&pRequest->ResultFlags); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, false); + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, false); + pRequest->ItemCount = 0; /* Start out with nothing */ + + if((pRequest->RequestType == RR_BY_POSITION) || (pRequest->RequestType == RR_READ_ALL)) + return(TL_encode_by_position(apdu, pRequest, error_class, error_code)); + else { + /* Reject by sequence and by time for the moment */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + } + + return(-1); +} + +int TL_encode_by_position( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + int iLen; + int32_t iTemp; + TL_LOG_INFO *CurrentLog; + + uint32_t uiIndex; /* Current entry number */ + uint32_t uiFirst; /* Entry number we started encoding from */ + uint32_t uiLast; /* Entry number we finished encoding on */ + uint32_t uiTarget; /* Last entry we are required to encode */ + uint32_t uiRemaining; /* Amount of unused space in packet */ + + uiFirst = 0; + uiLast = 0; + iLen = 0; + uiRemaining = MAX_APDU - pRequest->Overhead; /* See how much space we have */ + + CurrentLog = &LogInfo[pRequest->object_instance]; + + if(CurrentLog->ulRecordCount == 0) /* Bail out now if nowt - should never happen for a Trend Log... */ + return(0); + + if(pRequest->RequestType == RR_READ_ALL) { + /* + * Read all the list or as much as will fit in the buffer by selecting + * a range that covers the whole list and falling through to the next + * section of code + */ + pRequest->Count = CurrentLog->ulRecordCount; /* Full list */ + pRequest->Range.RefIndex = 1; /* Starting at the beginning */ + } + + if(pRequest->Count < 0) { /* negative count means work from index backwards */ + /* + * Convert from end index/negative count to + * start index/positive count and then process as + * normal. This assumes that the order to return items + * is always first to last, if this is not true we will + * have to handle this differently. + * + * Note: We need to be careful about how we convert these + * values due to the mix of signed and unsigned types - don't + * try to optimise the code unless you understand all the + * implications of the data type conversions! + */ + + iTemp = pRequest->Range.RefIndex; /* pull out and convert to signed */ + iTemp += pRequest->Count + 1; /* Adjust backwards, remember count is -ve */ + if(iTemp < 1) { /* if count is too much, return from 1 to start index */ + pRequest->Count = pRequest->Range.RefIndex; + pRequest->Range.RefIndex = 1; + } + else { /* Otherwise adjust the start index and make count +ve */ + pRequest->Range.RefIndex = iTemp; + pRequest->Count = -pRequest->Count; + } + } + + /* From here on in we only have a starting point and a positive count */ + + if(pRequest->Range.RefIndex > CurrentLog->ulRecordCount) /* Nothing to return as we are past the end of the list */ + return(0); + + uiTarget = pRequest->Range.RefIndex + pRequest->Count - 1; /* Index of last required entry */ + if(uiTarget > CurrentLog->ulRecordCount) /* Capped at end of list if necessary */ + uiTarget = CurrentLog->ulRecordCount; + + uiIndex = pRequest->Range.RefIndex; + uiFirst = uiIndex; /* Record where we started from */ + while(uiIndex <= uiTarget) { + if(uiRemaining < TL_MAX_ENC) { + /* + * Can't fit any more in! We just set the result flag to say there + * was more and drop out of the loop early + */ + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, true); + break; + } + + iTemp = TL_encode_entry(&apdu[iLen], pRequest->object_instance, uiIndex); + + uiRemaining -= iTemp; /* Reduce the remaining space */ + iLen += iTemp; /* and increase the length consumed */ + uiLast = uiIndex; /* Record the last entry encoded */ + uiIndex++; /* and get ready for next one */ + pRequest->ItemCount++; /* Chalk up another one for the response count */ + } + + /* Set remaining result flags if necessary */ + if(uiFirst == 1) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, true); + + if(uiLast == CurrentLog->ulRecordCount) + bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true); + +return(iLen); +} + + +int TL_encode_entry(uint8_t *apdu, int iLog, int iEntry) +{ + int iLen; + TL_DATA_REC *pSource; + BACNET_BIT_STRING TempBits; + int iCount; + BACNET_DATE_TIME TempTime; + + /* Convert from BACnet 1 based to 0 based array index and then + * handle wrap around of the circular buffer */ + + pSource = &Logs[iLog][(iEntry - 1) % TL_MAX_ENTRIES]; + iLen = 0; + /* First stick the time stamp in with tag [0] */ + TL_Local_Time_To_BAC(&TempTime, pSource->tTimeStamp); + iLen += bacapp_encode_context_datetime(apdu, 0, &TempTime); + + /* Next comes the actual entry with tag [1] */ + iLen += encode_opening_tag(&apdu[iLen], 1); + /* The data entry is tagged individually [0] - [10] to indicate which type */ + switch(pSource->ucRecType) { + case TL_TYPE_STATUS: + /* Build bit string directly from the stored octet */ + bitstring_init(&TempBits); + bitstring_set_bits_used(&TempBits, 1, 5); + bitstring_set_octet(&TempBits, 0, pSource->Datum.ucLogStatus); + iLen += encode_context_bitstring(&apdu[iLen], pSource->ucRecType, &TempBits); + break; + + case TL_TYPE_BOOL: + iLen += encode_context_boolean(&apdu[iLen], pSource->ucRecType, pSource->Datum.ucBoolean); + break; + + case TL_TYPE_REAL: + iLen += encode_context_real(&apdu[iLen], pSource->ucRecType, pSource->Datum.fReal); + break; + + case TL_TYPE_ENUM: + iLen += encode_context_enumerated(&apdu[iLen], pSource->ucRecType, pSource->Datum.ulEnum); + break; + + case TL_TYPE_UNSIGN: + iLen += encode_context_unsigned(&apdu[iLen], pSource->ucRecType, pSource->Datum.ulUValue); + break; + + case TL_TYPE_SIGN: + iLen += encode_context_signed(&apdu[iLen], pSource->ucRecType, pSource->Datum.lSValue); + break; + + case TL_TYPE_BITS: + /* Rebuild bitstring directly from stored octets - which we + * have limited to 32 bits maximum as allowed by the standard + */ + bitstring_init(&TempBits); + bitstring_set_bits_used(&TempBits, (pSource->Datum.Bits.ucLen >> 4) & 0x0F, pSource->Datum.Bits.ucLen & 0x0F); + for(iCount = pSource->Datum.Bits.ucLen >> 4; iCount > 0; iCount--) + bitstring_set_octet(&TempBits, iCount - 1, pSource->Datum.Bits.ucStore[iCount-1]); + + iLen += encode_context_bitstring(&apdu[iLen], pSource->ucRecType, &TempBits); + break; + + case TL_TYPE_NULL: + iLen += encode_context_null(&apdu[iLen], pSource->ucRecType); + break; + + case TL_TYPE_ERROR: + iLen += encode_opening_tag(&apdu[iLen], TL_TYPE_ERROR); + iLen += encode_application_enumerated(&apdu[iLen], pSource->Datum.Error.usClass); + iLen += encode_application_enumerated(&apdu[iLen], pSource->Datum.Error.usCode); + iLen += encode_closing_tag(&apdu[iLen], TL_TYPE_ERROR); + break; + + case TL_TYPE_DELTA: + iLen += encode_context_real(&apdu[iLen], pSource->ucRecType, pSource->Datum.fTime); + break; + + case TL_TYPE_ANY: + /* Should never happen as we don't support this at the moment */ + break; + } + + iLen += encode_closing_tag(&apdu[iLen], 1); + /* Check if status bit string is required and insert with tag [2] */ + 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)); + iLen += encode_context_bitstring(&apdu[iLen], 2, &TempBits); + } + + return(iLen); } \ No newline at end of file diff --git a/bacnet-stack/demo/readrange/main.c b/bacnet-stack/demo/readrange/main.c index 471632d4..efc5efe9 100644 --- a/bacnet-stack/demo/readrange/main.c +++ b/bacnet-stack/demo/readrange/main.c @@ -223,7 +223,7 @@ static void Init_Objects( Trend_Log_Encode_Property_APDU, Trend_Log_Valid_Instance, Trend_Log_Write_Property, Trend_Log_Count, Trend_Log_Index_To_Instance, - Trend_Log_Name, NULL); + Trend_Log_Name, TrendLogGetRRInfo); #if defined(BACFILE) bacfile_init(); @@ -253,8 +253,8 @@ static void Init_Service_Handlers( /* we must implement read property - it's required! */ apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, - handler_read_range); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, + handler_read_property_multiple); apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_RANGE, diff --git a/bacnet-stack/include/datetime.h b/bacnet-stack/include/datetime.h index 4f9d7311..9d607d9c 100644 --- a/bacnet-stack/include/datetime.h +++ b/bacnet-stack/include/datetime.h @@ -131,6 +131,8 @@ extern "C" { /* date and time wildcards */ bool datetime_wildcard( BACNET_DATE_TIME * bdatetime); + bool datetime_wildcard_present( + BACNET_DATE_TIME * bdatetime); void datetime_wildcard_set( BACNET_DATE_TIME * bdatetime); void datetime_date_wildcard_set( diff --git a/bacnet-stack/include/trendlog.h b/bacnet-stack/include/trendlog.h index aa9f2807..8721d43c 100644 --- a/bacnet-stack/include/trendlog.h +++ b/bacnet-stack/include/trendlog.h @@ -79,6 +79,34 @@ extern "C" { time_t TL_BAC_Time_To_Local( BACNET_DATE_TIME *SourceTime); + void TL_Local_Time_To_BAC( + BACNET_DATE_TIME *DestTime, + time_t SourceTime); + + int TL_encode_entry( + uint8_t *apdu, + int iLog, + int iEntry); + + int TL_encode_by_position( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + + bool TrendLogGetRRInfo( + uint32_t Object, /* Which particular object */ + BACNET_PROPERTY_ID Property, /* Which property */ + RR_PROP_INFO *pInfo, /* Where to put the information */ + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + + int rr_trend_log_encode( + uint8_t *apdu, + BACNET_READ_RANGE_DATA *pRequest, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/src/datetime.c b/bacnet-stack/src/datetime.c index a047d861..c05bb673 100644 --- a/bacnet-stack/src/datetime.c +++ b/bacnet-stack/src/datetime.c @@ -399,6 +399,26 @@ bool datetime_wildcard( return wildcard_present; } +/* Returns true if any type of wildcard is present except for day of week + * on it's own. + */ +bool datetime_wildcard_present( + BACNET_DATE_TIME * bdatetime) +{ + bool wildcard_present = false; + + if (bdatetime) { + if ((bdatetime->date.year == (1900 + 0xFF)) || + (bdatetime->date.month > 12) || (bdatetime->date.day > 31) || + (bdatetime->time.hour == 0xFF) || + (bdatetime->time.min == 0xFF) || (bdatetime->time.sec == 0xFF) || + (bdatetime->time.hundredths == 0xFF)) { + wildcard_present = true; + } + } + + return wildcard_present; +} void datetime_date_wildcard_set( BACNET_DATE * bdate) { diff --git a/bacnet-stack/src/readrange.c b/bacnet-stack/src/readrange.c index 9212fd37..f3372ddf 100644 --- a/bacnet-stack/src/readrange.c +++ b/bacnet-stack/src/readrange.c @@ -174,7 +174,7 @@ int rr_decode_service_request( rrdata->object_property = (BACNET_PROPERTY_ID) UnsignedTemp; rrdata->Overhead = RR_OVERHEAD; /* Start with the fixed overhead */ - /* Tag 2: Optional Array Index */ + /* Tag 2: Optional Array Index - set to ALL if not present */ rrdata->array_index = BACNET_ARRAY_ALL; /* Assuming this is the most common outcome... */ if (len < apdu_len) { TagLen =