diff --git a/CMakeLists.txt b/CMakeLists.txt index aa8d387a..e37d57be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -736,10 +736,13 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/linux) target_link_libraries(${PROJECT_NAME} PUBLIC m) add_compile_definitions(BACNET_PORT=linux) + include_directories(ports/posix) target_sources(${PROJECT_NAME} PRIVATE ports/linux/bacport.h ports/linux/datetime-init.c + ports/posix/bacfile-posix.c + ports/posix/bacfile-posix.h $<$:ports/linux/bip-init.c> $<$:ports/linux/bip6.c> $<$:ports/linux/bzll-init.c> @@ -767,8 +770,12 @@ elseif(WIN32) $<$:ws2_32> $<$:iphlpapi>) + include_directories(ports/posix) + target_sources(${PROJECT_NAME} PRIVATE ports/win32/bacport.h + ports/posix/bacfile-posix.c + ports/posix/bacfile-posix.h $<$:ports/win32/bip6.c> $<$:ports/win32/bip-init.c> $<$:ports/win32/bzll-init.c> @@ -815,9 +822,12 @@ elseif(APPLE) message(STATUS "BACNET: building for APPLE") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) add_compile_definitions(BACNET_PORT=bsd) + include_directories(ports/posix) target_sources(${PROJECT_NAME} PRIVATE ports/bsd/bacport.h + ports/posix/bacfile-posix.c + ports/posix/bacfile-posix.h $<$:ports/bsd/bip-init.c> $<$:ports/bsd/bzll-init.c> $<$:ports/bsd/bip6.c> @@ -856,9 +866,12 @@ elseif(APPLE) elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") message(STATUS "BACNET: building for FreeBSD") set(BACNET_PORT_DIRECTORY_PATH ${CMAKE_CURRENT_LIST_DIR}/ports/bsd) + include_directories(ports/posix) target_sources(${PROJECT_NAME} PRIVATE ports/bsd/bacport.h + ports/posix/bacfile-posix.c + ports/posix/bacfile-posix.h $<$:ports/bsd/bip-init.c> $<$:ports/bsd/bzll-init.c> $<$:ports/bsd/bip6.c> diff --git a/apps/Makefile b/apps/Makefile index aaa77a56..a58d246c 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -138,6 +138,7 @@ endif # source file locations BACNET_PORT_DIR = $(realpath ../ports/$(BACNET_PORT)) +BACNET_POSIX_DIR = $(realpath ../ports/posix) BACNET_SRC_DIR = $(realpath ../src) # Compiler flag to set the C Standard level. @@ -197,7 +198,7 @@ BACNET_DEFINES += -DBACNET_PROPERTY_LISTS=1 BACNET_DEFINES += -DBACNET_PROTOCOL_REVISION=24 # put all the flags together -INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR) +INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR) -I$(BACNET_POSIX_DIR) CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES) CFLAGS += $(CSTANDARD) ifneq (${BACNET_LIB},) diff --git a/apps/lib/Makefile b/apps/lib/Makefile index 5fc61f7f..1f4a5ce8 100644 --- a/apps/lib/Makefile +++ b/apps/lib/Makefile @@ -6,6 +6,7 @@ BACNET_LIB_DIR ?= $(realpath .) BACNET_LIB_TARGET ?= $(BACNET_LIB_DIR)/lib$(BACNET_LIB_NAME).a BACNET_SRC_DIR ?= $(realpath ../../src) BACNET_PORT_DIR ?= $(realpath ../../ports/linux) +BACNET_POSIX_DIR ?= $(realpath ../../ports/posix) BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object BACNET_DEFINES ?= @@ -35,6 +36,7 @@ CFLAGS += $(WARNINGS) $(DEBUGGING) $(OPTIMIZATION) $(BACNET_DEFINES) $(INCLUDES) CFLAGS += -ffunction-sections -fdata-sections APPS_ENVIRONMENT_SRC = \ + $(BACNET_POSIX_DIR)/bacfile-posix.c \ $(BACNET_SRC_DIR)/bacnet/datalink/dlenv.c PORT_ARCNET_SRC = \ diff --git a/ports/posix/bacfile-posix.c b/ports/posix/bacfile-posix.c new file mode 100644 index 00000000..3be97a1b --- /dev/null +++ b/ports/posix/bacfile-posix.c @@ -0,0 +1,272 @@ +/** + * @file + * @brief A POSIX BACnet File Object implementation. + * @author Steve Karg + * @date 2005 + * @copyright SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" +#include "bacnet/basic/object/bacfile.h" + +#ifndef FILE_RECORD_SIZE +#define FILE_RECORD_SIZE MAX_OCTET_STRING_BYTES +#endif + +/** + * @brief Determines the file size for a given file + * @param pFile - file handle + * @return file size in bytes, or 0 if not found + */ +static long fsize(FILE *pFile) +{ + long size = 0; + long origin = 0; + + if (pFile) { + origin = ftell(pFile); + fseek(pFile, 0L, SEEK_END); + size = ftell(pFile); + fseek(pFile, origin, SEEK_SET); + } + return (size); +} + +/** + * @brief Determines the file size for a given file + * @param pathname - name of the file to get the size for + * @return file size in bytes, or 0 if not found + */ +size_t bacfile_posix_file_size(const char *pathname) +{ + FILE *pFile = NULL; + long file_position = 0; + size_t file_size = 0; + + if (pathname) { + pFile = fopen(pathname, "rb"); + if (pFile) { + file_position = fsize(pFile); + if (file_position >= 0) { + file_size = (size_t)file_position; + } + fclose(pFile); + } + } + + return file_size; +} + +/** + * @brief Sets the file size property value + * @param pathname - name of the file to set the size for + * @param file_size - value of the file size property + * @return true if file size is writable + */ +bool bacfile_posix_file_size_set(const char *pathname, size_t file_size) +{ + bool status = false; + + (void)pathname; /* unused parameter */ + (void)file_size; /* unused parameter */ + /* FIXME: add clever POSIX file stuff here */ + + return status; +} + +/** + * @brief Reads stream data from a file + * @param pathname - name of the file to read from + * @param fileStartPosition - starting position in the file + * @param fileData - data buffer to read into + * @param fileDataLen - size of the data buffer + * @return number of bytes read, or 0 if not successful + */ +size_t bacfile_posix_read_stream_data( + const char *pathname, + size_t fileStartPosition, + uint8_t *fileData, + size_t fileDataLen) +{ + FILE *pFile = NULL; + size_t len = 0; + + if (pathname) { + pFile = fopen(pathname, "rb"); + if (pFile) { + (void)fseek(pFile, fileStartPosition, SEEK_SET); + len = fread(fileData, 1, fileDataLen, pFile); + fclose(pFile); + } + } + + return len; +} + +/** + * @brief Writes stream data to a file + * @param pathname - name of the file to write to + * @param fileStartPosition - starting position in the file + * @param fileData - data buffer to write from + * @param fileDataLen - size of the data buffer + * @return number of bytes written, or 0 if not successful + */ +size_t bacfile_posix_write_stream_data( + const char *pathname, + size_t fileStartPosition, + const uint8_t *fileData, + size_t fileDataLen) +{ + size_t bytes_written = 0; + FILE *pFile = NULL; + + if (pathname) { + if (fileStartPosition == 0) { + /* open the file as a clean slate when starting at 0 */ + pFile = fopen(pathname, "wb"); + } else if (fileStartPosition == -1) { + /* If 'File Start Position' parameter has the special + value -1, then the write operation shall be treated + as an append to the current end of file. */ + pFile = fopen(pathname, "ab+"); + } else { + /* open for update */ + pFile = fopen(pathname, "rb+"); + } + if (pFile) { + if (fileStartPosition != -1) { + (void)fseek(pFile, fileStartPosition, SEEK_SET); + } + bytes_written = fwrite(fileData, fileDataLen, 1, pFile); + fclose(pFile); + } + } + + return bytes_written; +} + +/** + * @brief Writes record data to a file + * @param pathname - name of the file to write to + * @param fileStartRecord - starting record in the file + * @param fileIndexRecord - index of the record to read + * @param fileData - data buffer to read into + * @param fileDataLen - size of the data buffer + * @return true if successful, false otherwise + */ +bool bacfile_posix_write_record_data( + const char *pathname, + size_t fileStartRecord, + size_t fileIndexRecord, + const uint8_t *fileData, + size_t fileDataLen) +{ + bool status = false; + FILE *pFile = NULL; + uint32_t i = 0; + char dummy_data[FILE_RECORD_SIZE]; + const char *pData = NULL; + size_t fileSeekRecord = 0; + + if (pathname) { + if (fileStartRecord == 0) { + /* open the file as a clean slate when starting at 0 */ + pFile = fopen(pathname, "wb"); + fileSeekRecord = fileIndexRecord; + } else if (fileStartRecord == -1) { + /* If 'File Start Record' parameter has the special + value -1, then the write operation shall be treated + as an append to the current end of file. */ + pFile = fopen(pathname, "ab+"); + fileSeekRecord = fileIndexRecord; + } else { + /* open for update */ + pFile = fopen(pathname, "rb+"); + fileSeekRecord = fileStartRecord + fileIndexRecord; + } + if (pFile) { + if ((fileStartRecord != -1) && (fileSeekRecord > 0)) { + /* seek to the start record */ + for (i = 0; i < fileSeekRecord; i++) { + pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile); + if ((pData == NULL) || feof(pFile)) { + break; + } + } + } + if (fwrite(fileData, fileDataLen, 1, pFile) == 1) { + status = true; + } + fclose(pFile); + } + } + + return status; +} + +/** + * @brief Reads record data from a file + * @param pathname - name of the file to read from + * @param fileStartRecord - starting record in the file + * @param fileIndexRecord - index of the record to read + * @param fileData - data buffer to read into + * @param fileDataLen - size of the data buffer + * @return true if successful, false otherwise + */ +bool bacfile_posix_read_record_data( + const char *pathname, + size_t fileStartRecord, + size_t fileIndexRecord, + uint8_t *fileData, + size_t fileDataLen) +{ + bool status = false; + FILE *pFile = NULL; + uint32_t i = 0; + char dummy_data[FILE_RECORD_SIZE] = { 0 }; + const char *pData = NULL; + size_t fileSeekRecord = 0; + + if (pathname) { + pFile = fopen(pathname, "rb"); + if (pFile) { + fileSeekRecord = fileStartRecord + fileIndexRecord; + /* seek to the start record */ + for (i = 0; i < fileSeekRecord; i++) { + pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile); + if ((pData == NULL) || feof(pFile)) { + break; + } + } + if ((i == fileSeekRecord) && (fileDataLen <= sizeof(dummy_data))) { + /* copy the record data */ + memmove(fileData, &dummy_data[0], fileDataLen); + status = true; + } + fclose(pFile); + } + } + + return status; +} + +/** + * @brief Initializes the object data + */ +void bacfile_posix_init(void) +{ +#if defined(BACFILE) + bacfile_write_stream_data_callback_set(bacfile_posix_write_stream_data); + bacfile_read_stream_data_callback_set(bacfile_posix_read_stream_data); + bacfile_write_record_data_callback_set(bacfile_posix_write_record_data); + bacfile_read_record_data_callback_set(bacfile_posix_read_record_data); + bacfile_file_size_callback_set(bacfile_posix_file_size); + bacfile_file_size_set_callback_set(bacfile_posix_file_size_set); +#endif +} diff --git a/ports/posix/bacfile-posix.h b/ports/posix/bacfile-posix.h new file mode 100644 index 00000000..6b0f522b --- /dev/null +++ b/ports/posix/bacfile-posix.h @@ -0,0 +1,53 @@ +/** + * @file + * @brief A POSIX BACnet File Object implementation. + * @author Steve Karg + * @date July 2025 + * @copyright SPDX-License-Identifier: MIT + */ +#ifndef BACNET_FILE_POSIX_H +#define BACNET_FILE_POSIX_H +/* BACnet Stack defines - first */ +#include "bacnet/bacdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +BACNET_STACK_EXPORT +size_t bacfile_posix_file_size(const char *pathname); +BACNET_STACK_EXPORT +bool bacfile_posix_file_size_set(const char *pathname, size_t file_size); +BACNET_STACK_EXPORT +size_t bacfile_posix_read_stream_data( + const char *pathname, + size_t fileStartPosition, + uint8_t *fileData, + size_t fileDataLen); +BACNET_STACK_EXPORT +size_t bacfile_posix_write_stream_data( + const char *pathname, + size_t fileStartPosition, + const uint8_t *fileData, + size_t fileDataLen); +BACNET_STACK_EXPORT +bool bacfile_posix_write_record_data( + const char *pathname, + size_t fileStartRecord, + size_t fileIndexRecord, + const uint8_t *fileData, + size_t fileDataLen); +BACNET_STACK_EXPORT +bool bacfile_posix_read_record_data( + const char *pathname, + size_t fileStartRecord, + size_t fileIndexRecord, + uint8_t *fileData, + size_t fileDataLen); +BACNET_STACK_EXPORT +void bacfile_posix_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj b/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj index d1a121d2..11bd2dbb 100644 --- a/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj +++ b/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj @@ -205,6 +205,7 @@ + diff --git a/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters b/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters index dffb29a1..07aa8c4b 100644 --- a/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters +++ b/ports/win32/Microsoft Visual Studio 2019/BACnet_Handler_Library/BACnet_Handler_Library.vcxproj.filters @@ -165,5 +165,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj b/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj index 8e1f94cc..5e5d3f6e 100644 --- a/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj +++ b/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj @@ -219,6 +219,7 @@ + @@ -439,6 +440,7 @@ + diff --git a/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj.filters b/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj.filters index 5fa9a875..569a4563 100644 --- a/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj.filters +++ b/ports/win32/Microsoft Visual Studio/bacnet-stack/bacnet-stack.vcxproj.filters @@ -594,6 +594,9 @@ Source Files\src\bacnet\basic\sys + + Source Files\ports\posix + Source Files\ports\win32 @@ -926,6 +929,9 @@ Source Files\src\bacnet\basic\sys + + Source Files\ports\posix + Source Files\ports\win32 diff --git a/src/bacnet/basic/object/acc.c b/src/bacnet/basic/object/acc.c index 4e1fb3f9..24eddcb8 100644 --- a/src/bacnet/basic/object/acc.c +++ b/src/bacnet/basic/object/acc.c @@ -27,17 +27,20 @@ struct object_data { static struct object_data Object_List[MAX_ACCUMULATORS]; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_PRESENT_VALUE, - PROP_STATUS_FLAGS, - PROP_EVENT_STATE, - PROP_OUT_OF_SERVICE, - PROP_SCALE, - PROP_UNITS, - PROP_MAX_PRES_VALUE, - -1 }; +static const int Properties_Required[] = { + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, + PROP_SCALE, + PROP_UNITS, + PROP_MAX_PRES_VALUE, + -1 +}; static const int Properties_Optional[] = { PROP_DESCRIPTION, -1 }; diff --git a/src/bacnet/basic/object/access_credential.c b/src/bacnet/basic/object/access_credential.c index d45cfb90..c571603c 100644 --- a/src/bacnet/basic/object/access_credential.c +++ b/src/bacnet/basic/object/access_credential.c @@ -24,20 +24,23 @@ static bool Access_Credential_Initialized = false; static ACCESS_CREDENTIAL_DESCR ac_descr[MAX_ACCESS_CREDENTIALS]; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_GLOBAL_IDENTIFIER, - PROP_STATUS_FLAGS, - PROP_RELIABILITY, - PROP_CREDENTIAL_STATUS, - PROP_REASON_FOR_DISABLE, - PROP_AUTHENTICATION_FACTORS, - PROP_ACTIVATION_TIME, - PROP_EXPIRATION_TIME, - PROP_CREDENTIAL_DISABLE, - PROP_ASSIGNED_ACCESS_RIGHTS, - -1 }; +static const int Properties_Required[] = { + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_GLOBAL_IDENTIFIER, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_CREDENTIAL_STATUS, + PROP_REASON_FOR_DISABLE, + PROP_AUTHENTICATION_FACTORS, + PROP_ACTIVATION_TIME, + PROP_EXPIRATION_TIME, + PROP_CREDENTIAL_DISABLE, + PROP_ASSIGNED_ACCESS_RIGHTS, + -1 +}; static const int Properties_Optional[] = { -1 }; diff --git a/src/bacnet/basic/object/access_door.c b/src/bacnet/basic/object/access_door.c index bcda479c..386b8d42 100644 --- a/src/bacnet/basic/object/access_door.c +++ b/src/bacnet/basic/object/access_door.c @@ -22,20 +22,23 @@ static bool Access_Door_Initialized = false; static ACCESS_DOOR_DESCR ad_descr[MAX_ACCESS_DOORS]; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_PRESENT_VALUE, - PROP_STATUS_FLAGS, - PROP_EVENT_STATE, - PROP_RELIABILITY, - PROP_OUT_OF_SERVICE, - PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, - PROP_DOOR_PULSE_TIME, - PROP_DOOR_EXTENDED_PULSE_TIME, - PROP_DOOR_OPEN_TOO_LONG_TIME, - -1 }; +static const int Properties_Required[] = { + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_DOOR_PULSE_TIME, + PROP_DOOR_EXTENDED_PULSE_TIME, + PROP_DOOR_OPEN_TOO_LONG_TIME, + -1 +}; static const int Properties_Optional[] = { PROP_DOOR_STATUS, PROP_LOCK_STATUS, diff --git a/src/bacnet/basic/object/access_point.c b/src/bacnet/basic/object/access_point.c index 42de9a3a..d97d5a53 100644 --- a/src/bacnet/basic/object/access_point.c +++ b/src/bacnet/basic/object/access_point.c @@ -23,6 +23,7 @@ static ACCESS_POINT_DESCR ap_descr[MAX_ACCESS_POINTS]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, diff --git a/src/bacnet/basic/object/access_user.c b/src/bacnet/basic/object/access_user.c index 704e1f41..e9c2ef91 100644 --- a/src/bacnet/basic/object/access_user.c +++ b/src/bacnet/basic/object/access_user.c @@ -24,6 +24,7 @@ static ACCESS_USER_DESCR au_descr[MAX_ACCESS_USERS]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER, PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_USER_TYPE, PROP_CREDENTIALS, -1 diff --git a/src/bacnet/basic/object/access_zone.c b/src/bacnet/basic/object/access_zone.c index c86e8a58..603a9dc3 100644 --- a/src/bacnet/basic/object/access_zone.c +++ b/src/bacnet/basic/object/access_zone.c @@ -24,6 +24,7 @@ static ACCESS_ZONE_DESCR az_descr[MAX_ACCESS_ZONES]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER, PROP_OCCUPANCY_STATE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_RELIABILITY, PROP_OUT_OF_SERVICE, diff --git a/src/bacnet/basic/object/ai.c b/src/bacnet/basic/object/ai.c index edc2a6f6..7383e388 100644 --- a/src/bacnet/basic/object/ai.c +++ b/src/bacnet/basic/object/ai.c @@ -30,29 +30,36 @@ static OS_Keylist Object_List; /* common object type */ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_ANALOG_INPUT; -/* clang-format off */ /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { - PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, - PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, - PROP_OUT_OF_SERVICE, PROP_UNITS, -1 + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, PROP_UNITS, -1 }; static const int Properties_Optional[] = { - PROP_DESCRIPTION, PROP_RELIABILITY, PROP_COV_INCREMENT, + /* unordered list of optional properties */ + PROP_DESCRIPTION, + PROP_RELIABILITY, + PROP_COV_INCREMENT, #if defined(INTRINSIC_REPORTING) - PROP_TIME_DELAY, PROP_NOTIFICATION_CLASS, PROP_HIGH_LIMIT, - PROP_LOW_LIMIT, PROP_DEADBAND, PROP_LIMIT_ENABLE, PROP_EVENT_ENABLE, - PROP_ACKED_TRANSITIONS, PROP_NOTIFY_TYPE, PROP_EVENT_TIME_STAMPS, + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, PROP_EVENT_DETECTION_ENABLE, #endif -1 }; -static const int Properties_Proprietary[] = { - -1 -}; -/* clang-format on */ +static const int Properties_Proprietary[] = { -1 }; /** * Initialize the pointers for the required, the optional and the properitary diff --git a/src/bacnet/basic/object/bacfile.c b/src/bacnet/basic/object/bacfile.c index a9e0c46b..ca57660e 100644 --- a/src/bacnet/basic/object/bacfile.c +++ b/src/bacnet/basic/object/bacfile.c @@ -46,20 +46,26 @@ static OS_Keylist Object_List; /* common object type */ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_FILE; /* These three arrays are used by the ReadPropertyMultiple handler */ -static const int bacfile_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_FILE_TYPE, - PROP_FILE_SIZE, - PROP_MODIFICATION_DATE, - PROP_ARCHIVE, - PROP_READ_ONLY, - PROP_FILE_ACCESS_METHOD, - -1 }; +static const int Properties_Required[] = { + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_FILE_TYPE, + PROP_FILE_SIZE, + PROP_MODIFICATION_DATE, + PROP_ARCHIVE, + PROP_READ_ONLY, + PROP_FILE_ACCESS_METHOD, + -1 +}; -static const int bacfile_Properties_Optional[] = { PROP_DESCRIPTION, -1 }; +static const int Properties_Optional[] = { + /* unordered list of optional properties */ + PROP_DESCRIPTION, -1 +}; -static const int bacfile_Properties_Proprietary[] = { -1 }; +static const int Properties_Proprietary[] = { -1 }; /** * @brief Returns the list of required, optional, and proprietary properties. @@ -75,13 +81,13 @@ void BACfile_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary) { if (pRequired) { - *pRequired = bacfile_Properties_Required; + *pRequired = Properties_Required; } if (pOptional) { - *pOptional = bacfile_Properties_Optional; + *pOptional = Properties_Optional; } if (pProprietary) { - *pProprietary = bacfile_Properties_Proprietary; + *pProprietary = Properties_Proprietary; } return; @@ -280,22 +286,221 @@ uint32_t bacfile_index_to_instance(unsigned find_index) } /** - * @brief Determines the file size for a given file - * @param pFile - file handle - * @return file size in bytes, or 0 if not found + * @brief Callback function to write record data */ -static long fsize(FILE *pFile) -{ - long size = 0; - long origin = 0; +static bool (*bacfile_write_record_data_cb)( + const char *, size_t, size_t, const uint8_t *, size_t) = NULL; - if (pFile) { - origin = ftell(pFile); - fseek(pFile, 0L, SEEK_END); - size = ftell(pFile); - fseek(pFile, origin, SEEK_SET); +/** + * @brief Callback function to write record data + * + * @param pathname - name of the file to write to + * @param fileStartRecord - starting record number in the file + * @param record_index - index of the record to write + * @param buffer - data buffer to write + * @param buffer_size - size of the data buffer + * @return true if the record data was written successfully + * @return false if the record data could not be written + */ +static bool bacfile_write_record_data_callback( + const char *pathname, + size_t fileStartRecord, + size_t record_index, + const uint8_t *buffer, + size_t buffer_size) +{ + if (bacfile_write_record_data_cb) { + return bacfile_write_record_data_cb( + pathname, fileStartRecord, record_index, buffer, buffer_size); } - return (size); + + return false; +} + +/** + * @brief Sets the callback function for writing record data + * @param callback - function pointer to the callback + */ +void bacfile_write_record_data_callback_set( + bool (*callback)(const char *, size_t, size_t, const uint8_t *, size_t)) +{ + bacfile_write_record_data_cb = callback; +} + +/** + * @brief Callback function to read record data + */ +static bool (*bacfile_read_record_data_cb)( + const char *, size_t, size_t, uint8_t *, size_t) = NULL; + +/** + * @brief Callback function to read record data + * @param pathname - name of the file to read from + * @param fileStartRecord - starting record number in the file + * @param record_index - index of the record to read + * @param buffer - data buffer to read into + * @param buffer_size - size of the data buffer + * @return true if the record data was read successfully + * @return false if the record data could not be read + */ +static bool bacfile_read_record_data_callback( + const char *pathname, + size_t fileStartRecord, + size_t record_index, + uint8_t *buffer, + size_t buffer_size) +{ + if (bacfile_read_record_data_cb) { + return bacfile_read_record_data_cb( + pathname, fileStartRecord, record_index, buffer, buffer_size); + } + + return false; +} + +/** + * @brief Sets the callback function for reading record data + * @param callback - function pointer to the callback + */ +void bacfile_read_record_data_callback_set( + bool (*callback)(const char *, size_t, size_t, uint8_t *, size_t)) +{ + bacfile_read_record_data_cb = callback; +} + +/** + * @brief Callback function to write stream data + */ +static size_t (*bacfile_write_stream_data_cb)( + const char *, size_t, const uint8_t *, size_t) = NULL; + +/** + * @brief Callback function to write stream data + * @param pathname - name of the file to write to + * @param fileStartPosition - starting position in the file + * @param buffer - data buffer to write + * @param buffer_size - size of the data buffer + * @return number of bytes written, or 0 if not successful + */ +static size_t bacfile_write_stream_data_callback( + const char *pathname, + size_t fileStartPosition, + const uint8_t *buffer, + size_t buffer_size) +{ + if (bacfile_write_stream_data_cb) { + return bacfile_write_stream_data_cb( + pathname, fileStartPosition, buffer, buffer_size); + } + + return 0; +} + +/** + * @brief Sets the callback function for writing stream data + * @param callback - function pointer to the callback + */ +void bacfile_write_stream_data_callback_set( + size_t (*callback)(const char *, size_t, const uint8_t *, size_t)) +{ + bacfile_write_stream_data_cb = callback; +} + +/** + * @brief Callback function to read stream data + */ +static size_t (*bacfile_read_stream_data_cb)( + const char *, size_t, uint8_t *, size_t) = NULL; + +/** + * @brief Callback function to read stream data + * @param pathname - name of the file to read from + * @param fileStartPosition - starting position in the file + * @param buffer - data buffer to read into + * @param buffer_size - size of the data buffer and number of bytes to read + * @return number of bytes read, or 0 if not successful + */ +static size_t bacfile_read_stream_data_callback( + const char *pathname, + size_t fileStartPosition, + uint8_t *buffer, + size_t buffer_size) +{ + if (bacfile_read_stream_data_cb) { + return bacfile_read_stream_data_cb( + pathname, fileStartPosition, buffer, buffer_size); + } + + return 0; +} + +/** + * @brief Sets the callback function for reading stream data + * @param callback - function pointer to the callback + */ +void bacfile_read_stream_data_callback_set( + size_t (*callback)(const char *, size_t, uint8_t *, size_t)) +{ + bacfile_read_stream_data_cb = callback; +} + +/** + * @brief Callback function to get file size + */ +static size_t (*bacfile_file_size_cb)(const char *) = NULL; + +/** + * @brief Callback function to get file size + * @param pathname - name of the file to get the size of + * @return size of the file in bytes, or 0 if not found + */ +static size_t bacfile_file_size_callback(const char *pathname) +{ + size_t file_size = 0; + + if (bacfile_file_size_cb) { + file_size = bacfile_file_size_cb(pathname); + } + + return file_size; +} + +/** + * @brief Sets the callback function for getting file size + * @param callback - function pointer to the callback + */ +void bacfile_file_size_callback_set(size_t (*callback)(const char *)) +{ + bacfile_file_size_cb = callback; +} + +/** + * @brief Callback function to set file size + */ +static bool (*bacfile_file_size_set_cb)(const char *, size_t) = NULL; + +/** + * @brief Callback function to set file size + * @param pathname - name of the file to set the size for + * @param file_size - value of the file size property + * @return true if file size is writable, false otherwise + */ +bool bacfile_file_size_set_callback(const char *pathname, size_t file_size) +{ + if (bacfile_file_size_set_cb) { + return bacfile_file_size_set_cb(pathname, file_size); + } + + return false; +} + +/** + * @brief Sets the callback function for setting file size + * @param callback - function pointer to the callback + */ +void bacfile_file_size_set_callback_set(bool (*callback)(const char *, size_t)) +{ + bacfile_file_size_set_cb = callback; } /** @@ -308,22 +513,13 @@ static long fsize(FILE *pFile) uint32_t bacfile_read(uint32_t object_instance, uint8_t *buffer, uint32_t buffer_size) { - const char *pFilename = NULL; - FILE *pFile = NULL; + const char *pathname = NULL; long file_size = 0; - pFilename = bacfile_pathname(object_instance); - if (pFilename) { - pFile = fopen(pFilename, "rb"); - if (pFile) { - file_size = fsize(pFile); - if (buffer && (buffer_size >= file_size)) { - if (fread(buffer, file_size, 1, pFile) == 0) { - file_size = 0; - } - } - fclose(pFile); - } + pathname = bacfile_pathname(object_instance); + if (pathname) { + file_size = + bacfile_read_stream_data_callback(pathname, 0, buffer, buffer_size); } return (uint32_t)file_size; @@ -339,20 +535,13 @@ bacfile_read(uint32_t object_instance, uint8_t *buffer, uint32_t buffer_size) uint32_t bacfile_write( uint32_t object_instance, const uint8_t *buffer, uint32_t buffer_size) { - const char *pFilename = NULL; - FILE *pFile = NULL; + const char *pathname = NULL; long file_size = 0; - pFilename = bacfile_pathname(object_instance); - if (pFilename) { - /* open the file as a clean slate when starting at 0 */ - pFile = fopen(pFilename, "wb"); - if (pFile) { - if (fwrite(buffer, buffer_size, 1, pFile) == 1) { - file_size = buffer_size; - } - fclose(pFile); - } + pathname = bacfile_pathname(object_instance); + if (pathname) { + file_size = bacfile_write_stream_data_callback( + pathname, 0, buffer, buffer_size); } return (uint32_t)file_size; @@ -365,20 +554,15 @@ uint32_t bacfile_write( */ BACNET_UNSIGNED_INTEGER bacfile_file_size(uint32_t object_instance) { - const char *pFilename = NULL; - FILE *pFile = NULL; + const char *pathname = NULL; long file_position = 0; BACNET_UNSIGNED_INTEGER file_size = 0; - pFilename = bacfile_pathname(object_instance); - if (pFilename) { - pFile = fopen(pFilename, "rb"); - if (pFile) { - file_position = fsize(pFile); - if (file_position >= 0) { - file_size = (BACNET_UNSIGNED_INTEGER)file_position; - } - fclose(pFile); + pathname = bacfile_pathname(object_instance); + if (pathname) { + file_position = bacfile_file_size_callback(pathname); + if (file_position >= 0) { + file_size = (BACNET_UNSIGNED_INTEGER)file_position; } } @@ -400,8 +584,8 @@ bool bacfile_file_size_set( pObject = Keylist_Data(Object_List, object_instance); if (pObject) { if (pObject->File_Access_Stream) { - (void)file_size; - /* FIXME: add clever POSIX file stuff here */ + status = + bacfile_file_size_set_callback(pObject->Pathname, file_size); } } @@ -717,20 +901,16 @@ bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; - case PROP_OBJECT_IDENTIFIER: - case PROP_OBJECT_NAME: - case PROP_OBJECT_TYPE: - case PROP_DESCRIPTION: - case PROP_FILE_TYPE: - case PROP_MODIFICATION_DATE: - case PROP_READ_ONLY: - case PROP_FILE_ACCESS_METHOD: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; default: - wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + if (property_lists_member( + Properties_Required, Properties_Optional, + Properties_Proprietary, wp_data->object_property)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + } break; } @@ -786,31 +966,27 @@ uint32_t bacfile_instance_from_tsm(uint8_t invokeID) bool bacfile_read_stream_data(BACNET_ATOMIC_READ_FILE_DATA *data) { - const char *pFilename = NULL; + const char *pathname = NULL; bool found = false; - FILE *pFile = NULL; size_t len = 0; + size_t requestedOctetCount = 0; - pFilename = bacfile_pathname(data->object_instance); - if (pFilename) { + pathname = bacfile_pathname(data->object_instance); + if (pathname) { found = true; - pFile = fopen(pFilename, "rb"); - if (pFile) { - (void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET); - len = fread( - octetstring_value(&data->fileData[0]), 1, - data->type.stream.requestedOctetCount, pFile); - if (len < data->type.stream.requestedOctetCount) { - data->endOfFile = true; - } else { - data->endOfFile = false; - } - octetstring_truncate(&data->fileData[0], len); - fclose(pFile); - } else { - octetstring_truncate(&data->fileData[0], 0); - data->endOfFile = true; + requestedOctetCount = data->type.stream.requestedOctetCount; + if (requestedOctetCount > octetstring_capacity(&data->fileData[0])) { + requestedOctetCount = octetstring_capacity(&data->fileData[0]); } + len = bacfile_read_stream_data_callback( + pathname, data->type.stream.fileStartPosition, + octetstring_value(&data->fileData[0]), requestedOctetCount); + if (len < requestedOctetCount) { + data->endOfFile = true; + } else { + data->endOfFile = false; + } + octetstring_truncate(&data->fileData[0], len); } else { octetstring_truncate(&data->fileData[0], 0); data->endOfFile = true; @@ -819,163 +995,137 @@ bool bacfile_read_stream_data(BACNET_ATOMIC_READ_FILE_DATA *data) return found; } +bool bacfile_read_record_data(BACNET_ATOMIC_READ_FILE_DATA *data) +{ + const char *pathname = NULL; + bool found = false; + bool status = false; + uint32_t i = 0; + + pathname = bacfile_pathname(data->object_instance); + if (pathname) { + found = true; + data->endOfFile = false; + for (i = 0; i < data->type.record.RecordCount; i++) { + status = bacfile_read_record_data_callback( + pathname, data->type.record.fileStartRecord, i, + octetstring_value(&data->fileData[i]), + octetstring_capacity(&data->fileData[i])); + if (!status) { + data->endOfFile = true; + data->type.record.RecordCount = i; + break; + } + } + } + + return found; +} + bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA *data) { - const char *pFilename = NULL; - bool found = false; - FILE *pFile = NULL; + const char *pathname = NULL; + bool status = false; + size_t bytes_written = 0; - pFilename = bacfile_pathname(data->object_instance); - if (pFilename) { - found = true; - if (data->type.stream.fileStartPosition == 0) { - /* open the file as a clean slate when starting at 0 */ - pFile = fopen(pFilename, "wb"); - } else if (data->type.stream.fileStartPosition == -1) { - /* If 'File Start Position' parameter has the special - value -1, then the write operation shall be treated - as an append to the current end of file. */ - pFile = fopen(pFilename, "ab+"); - } else { - /* open for update */ - pFile = fopen(pFilename, "rb+"); - } - if (pFile) { - if (data->type.stream.fileStartPosition != -1) { - (void)fseek( - pFile, data->type.stream.fileStartPosition, SEEK_SET); - } - if (fwrite( - octetstring_value(&data->fileData[0]), - octetstring_length(&data->fileData[0]), 1, pFile) != 1) { - /* do something if it fails? */ - } - fclose(pFile); + pathname = bacfile_pathname(data->object_instance); + if (pathname) { + status = true; + /* note: If 'File Start Position' parameter has the special + value -1, then the write operation shall be treated + as an append to the current end of file. + If the 'File Start Position' parameter is 0, + open the file as a clean slate. */ + bytes_written = bacfile_write_stream_data_callback( + pathname, data->type.stream.fileStartPosition, + octetstring_value(&data->fileData[0]), + octetstring_length(&data->fileData[0])); + if (bytes_written == 0) { + status = false; /* no data written */ } } - return found; + return status; } +/** + * @brief Write the data received to the file specified + * @param data - pointer to the data to write + * @return true - if successful + * @return false - if failed + */ bool bacfile_write_record_data(const BACNET_ATOMIC_WRITE_FILE_DATA *data) { - const char *pFilename = NULL; + const char *pathname = NULL; bool found = false; - FILE *pFile = NULL; - uint32_t i = 0; - char dummy_data[FILE_RECORD_SIZE]; - const char *pData = NULL; + size_t i = 0; - pFilename = bacfile_pathname(data->object_instance); - if (pFilename) { + pathname = bacfile_pathname(data->object_instance); + if (pathname) { found = true; - if (data->type.record.fileStartRecord == 0) { - /* open the file as a clean slate when starting at 0 */ - pFile = fopen(pFilename, "wb"); - } else if (data->type.record.fileStartRecord == -1) { - /* If 'File Start Record' parameter has the special - value -1, then the write operation shall be treated - as an append to the current end of file. */ - pFile = fopen(pFilename, "ab+"); - } else { - /* open for update */ - pFile = fopen(pFilename, "rb+"); - } - if (pFile) { - if ((data->type.record.fileStartRecord != -1) && - (data->type.record.fileStartRecord > 0)) { - for (i = 0; i < (uint32_t)data->type.record.fileStartRecord; - i++) { - pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile); - if ((pData == NULL) || feof(pFile)) { - break; - } - } - } - for (i = 0; i < data->type.record.returnedRecordCount; i++) { - if (fwrite( - octetstring_value( - (BACNET_OCTET_STRING *)&data->fileData[i]), - octetstring_length(&data->fileData[i]), 1, - pFile) != 1) { - /* do something if it fails? */ - } - } - fclose(pFile); + /* If 'File Start Record' parameter has the special + value -1, then the write operation shall be treated + as an append to the current end of file. + If the 'File Start Record' parameter is 0, + open the file as a clean slate. */ + for (i = 0; i < data->type.record.returnedRecordCount; i++) { + bacfile_write_record_data_callback( + pathname, data->type.record.fileStartRecord, i, + octetstring_value((BACNET_OCTET_STRING *)&data->fileData[i]), + octetstring_length(&data->fileData[i])); } } return found; } +/** + * @brief Write the requested data received into the file specified + * @param instance - object-instance number of the object + * @param data - pointer to the data to write + * @return true - if successful + * @return false - if failed + */ bool bacfile_read_ack_stream_data( uint32_t instance, const BACNET_ATOMIC_READ_FILE_DATA *data) { bool found = false; - FILE *pFile = NULL; - const char *pFilename = NULL; + const char *pathname = NULL; - pFilename = bacfile_pathname(instance); - if (pFilename) { + pathname = bacfile_pathname(instance); + if (pathname) { found = true; - pFile = fopen(pFilename, "rb+"); - if (pFile) { - (void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET); - if (fwrite( - octetstring_value( - (BACNET_OCTET_STRING *)&data->fileData[0]), - octetstring_length(&data->fileData[0]), 1, pFile) != 1) { -#if PRINT_ENABLED - fprintf( - stderr, "Failed to write to %s (%lu)!\n", pFilename, - (unsigned long)instance); -#endif - } - fclose(pFile); - } + bacfile_write_stream_data_callback( + pathname, data->type.stream.fileStartPosition, + octetstring_value((BACNET_OCTET_STRING *)&data->fileData[0]), + octetstring_length(&data->fileData[0])); } return found; } +/** + * @brief Write the requested data received into the file specified + * @param instance - object-instance number of the object + * @param data - pointer to the data to write + * @return true - if successful + * @return false - if failed + */ bool bacfile_read_ack_record_data( uint32_t instance, const BACNET_ATOMIC_READ_FILE_DATA *data) { bool found = false; - FILE *pFile = NULL; - const char *pFilename = NULL; + const char *pathname = NULL; uint32_t i = 0; - char dummy_data[MAX_OCTET_STRING_BYTES] = { 0 }; - char *pData = NULL; - pFilename = bacfile_pathname(instance); - if (pFilename) { + pathname = bacfile_pathname(instance); + if (pathname) { found = true; - pFile = fopen(pFilename, "rb+"); - if (pFile) { - if (data->type.record.fileStartRecord > 0) { - for (i = 0; i < (uint32_t)data->type.record.fileStartRecord; - i++) { - pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile); - if ((pData == NULL) || feof(pFile)) { - break; - } - } - } - for (i = 0; i < data->type.record.RecordCount; i++) { - if (fwrite( - octetstring_value( - (BACNET_OCTET_STRING *)&data->fileData[i]), - octetstring_length(&data->fileData[i]), 1, - pFile) != 1) { -#if PRINT_ENABLED - fprintf( - stderr, "Failed to write to %s (%lu)!\n", pFilename, - (unsigned long)instance); -#endif - } - } - fclose(pFile); + for (i = 0; i < data->type.record.RecordCount; i++) { + bacfile_write_record_data_callback( + pathname, data->type.record.fileStartRecord, i, + octetstring_value((BACNET_OCTET_STRING *)&data->fileData[i]), + octetstring_length(&data->fileData[i])); } } diff --git a/src/bacnet/basic/object/bacfile.h b/src/bacnet/basic/object/bacfile.h index fbdd6e5e..483bf4a7 100644 --- a/src/bacnet/basic/object/bacfile.h +++ b/src/bacnet/basic/object/bacfile.h @@ -117,6 +117,23 @@ BACNET_STACK_EXPORT uint32_t bacfile_write( uint32_t object_instance, const uint8_t *buffer, uint32_t buffer_size); +BACNET_STACK_EXPORT +void bacfile_write_stream_data_callback_set( + size_t (*callback)(const char *, size_t, const uint8_t *, size_t)); +BACNET_STACK_EXPORT +void bacfile_read_stream_data_callback_set( + size_t (*callback)(const char *, size_t, uint8_t *, size_t)); +BACNET_STACK_EXPORT +void bacfile_write_record_data_callback_set( + bool (*callback)(const char *, size_t, size_t, const uint8_t *, size_t)); +BACNET_STACK_EXPORT +void bacfile_read_record_data_callback_set( + bool (*callback)(const char *, size_t, size_t, uint8_t *, size_t)); +BACNET_STACK_EXPORT +void bacfile_file_size_callback_set(size_t (*callback)(const char *)); +BACNET_STACK_EXPORT +void bacfile_file_size_set_callback_set(bool (*callback)(const char *, size_t)); + BACNET_STACK_EXPORT uint32_t bacfile_create(uint32_t object_instance); BACNET_STACK_EXPORT diff --git a/src/bacnet/basic/object/bi.c b/src/bacnet/basic/object/bi.c index deaeebd5..00f3ae9f 100644 --- a/src/bacnet/basic/object/bi.c +++ b/src/bacnet/basic/object/bi.c @@ -67,21 +67,16 @@ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_BINARY_INPUT; static binary_input_write_present_value_callback Binary_Input_Write_Present_Value_Callback; -/* clang-format off */ /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - PROP_OBJECT_TYPE, - PROP_PRESENT_VALUE, - PROP_STATUS_FLAGS, - PROP_EVENT_STATE, - PROP_OUT_OF_SERVICE, - PROP_POLARITY, - -1 + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, + PROP_OUT_OF_SERVICE, PROP_POLARITY, -1 }; static const int Properties_Optional[] = { + /* unordered list of optional properties */ PROP_RELIABILITY, PROP_DESCRIPTION, PROP_ACTIVE_TEXT, @@ -100,7 +95,6 @@ static const int Properties_Optional[] = { }; static const int Properties_Proprietary[] = { -1 }; -/* clang-format on */ /** * Initialize the pointers for the required, the optional and the properitary diff --git a/src/bacnet/basic/object/bitstring_value.c b/src/bacnet/basic/object/bitstring_value.c index fb9bd98e..94e13347 100644 --- a/src/bacnet/basic/object/bitstring_value.c +++ b/src/bacnet/basic/object/bitstring_value.c @@ -42,13 +42,15 @@ static bitstring_value_write_present_value_callback /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, -1 }; -static const int Properties_Optional[] = { PROP_RELIABILITY, - PROP_OUT_OF_SERVICE, - PROP_DESCRIPTION, -1 }; +static const int Properties_Optional[] = { + /* unordered list of optional properties */ + PROP_RELIABILITY, PROP_OUT_OF_SERVICE, PROP_DESCRIPTION, -1 +}; static const int Properties_Proprietary[] = { -1 }; diff --git a/src/bacnet/basic/object/credential_data_input.c b/src/bacnet/basic/object/credential_data_input.c index 8a613587..d36a01c8 100644 --- a/src/bacnet/basic/object/credential_data_input.c +++ b/src/bacnet/basic/object/credential_data_input.c @@ -25,6 +25,7 @@ static CREDENTIAL_DATA_INPUT_DESCR cdi_descr[MAX_CREDENTIAL_DATA_INPUTS]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_RELIABILITY, diff --git a/src/bacnet/basic/object/csv.c b/src/bacnet/basic/object/csv.c index 76026ad2..08ed8cd0 100644 --- a/src/bacnet/basic/object/csv.c +++ b/src/bacnet/basic/object/csv.c @@ -29,7 +29,7 @@ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_CHARACTERSTRING_VALUE; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { - /* list of the required properties */ + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, -1 }; diff --git a/src/bacnet/basic/object/ms-input.c b/src/bacnet/basic/object/ms-input.c index 372176a0..f1175a4e 100644 --- a/src/bacnet/basic/object/ms-input.c +++ b/src/bacnet/basic/object/ms-input.c @@ -48,13 +48,16 @@ static const char *Default_State_Text = "State 1\0" /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, -1 }; -static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_RELIABILITY, - PROP_STATE_TEXT, -1 }; +static const int Properties_Optional[] = { + /* unordered list of optional properties */ + PROP_DESCRIPTION, PROP_RELIABILITY, PROP_STATE_TEXT, -1 +}; static const int Properties_Proprietary[] = { -1 }; diff --git a/src/bacnet/basic/object/msv.c b/src/bacnet/basic/object/msv.c index a563b79f..3a1d2817 100644 --- a/src/bacnet/basic/object/msv.c +++ b/src/bacnet/basic/object/msv.c @@ -48,13 +48,16 @@ static const char *Default_State_Text = "State 1\0" /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, -1 }; -static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_RELIABILITY, - PROP_STATE_TEXT, -1 }; +static const int Properties_Optional[] = { + /* unordered list of required properties */ + PROP_DESCRIPTION, PROP_RELIABILITY, PROP_STATE_TEXT, -1 +}; static const int Properties_Proprietary[] = { -1 }; diff --git a/src/bacnet/basic/object/program.c b/src/bacnet/basic/object/program.c index 38981f6b..4d2477f5 100644 --- a/src/bacnet/basic/object/program.c +++ b/src/bacnet/basic/object/program.c @@ -57,7 +57,7 @@ struct object_data { /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { - /* unordered list of properties */ + /* unordered list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PROGRAM_STATE, PROP_PROGRAM_CHANGE, PROP_STATUS_FLAGS, @@ -65,7 +65,7 @@ static const int Properties_Required[] = { }; static const int Properties_Optional[] = { - /* unordered list of properties */ + /* unordered list of optional properties */ PROP_REASON_FOR_HALT, PROP_DESCRIPTION_OF_HALT, PROP_PROGRAM_LOCATION, diff --git a/src/bacnet/basic/object/structured_view.c b/src/bacnet/basic/object/structured_view.c index 01174719..b2d229e1 100644 --- a/src/bacnet/basic/object/structured_view.c +++ b/src/bacnet/basic/object/structured_view.c @@ -45,26 +45,26 @@ struct object_data { /* Key List for storing the object data sorted by instance number */ static OS_Keylist Object_List; -/* clang-format off */ /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Properties_Required[] = { - PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, - PROP_NODE_TYPE, PROP_SUBORDINATE_LIST, - -1 + /* unordered list of required properties */ + PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, + PROP_NODE_TYPE, PROP_SUBORDINATE_LIST, -1 }; static const int Properties_Optional[] = { - PROP_DESCRIPTION, PROP_NODE_SUBTYPE, PROP_SUBORDINATE_ANNOTATIONS, - PROP_SUBORDINATE_NODE_TYPES, PROP_SUBORDINATE_RELATIONSHIPS, + /* unordered list of optional properties */ + PROP_DESCRIPTION, + PROP_NODE_SUBTYPE, + PROP_SUBORDINATE_ANNOTATIONS, + PROP_SUBORDINATE_NODE_TYPES, + PROP_SUBORDINATE_RELATIONSHIPS, PROP_DEFAULT_SUBORDINATE_RELATIONSHIP, PROP_REPRESENTS, -1 }; -static const int Properties_Proprietary[] = { - -1 -}; -/* clang-format on */ +static const int Properties_Proprietary[] = { -1 }; /** * Returns the list of required, optional, and proprietary properties. diff --git a/src/bacnet/basic/service/h_arf.c b/src/bacnet/basic/service/h_arf.c index bcfdd8d7..7f47c852 100644 --- a/src/bacnet/basic/service/h_arf.c +++ b/src/bacnet/basic/service/h_arf.c @@ -157,7 +157,7 @@ void handler_atomic_read_file( error_class = ERROR_CLASS_SERVICES; error_code = ERROR_CODE_INVALID_FILE_START_POSITION; error = true; - } else if (bacfile_read_stream_data(&data)) { + } else if (bacfile_read_record_data(&data)) { debug_fprintf( stderr, "ARF: fileStartRecord %d, %u RecordCount.\n", (int)data.type.record.fileStartRecord, diff --git a/src/bacnet/datalink/bsc/bsc-util.c b/src/bacnet/datalink/bsc/bsc-util.c index 61d73e6b..c467e22d 100644 --- a/src/bacnet/datalink/bsc/bsc-util.c +++ b/src/bacnet/datalink/bsc/bsc-util.c @@ -9,7 +9,6 @@ #include "bacnet/basic/object/bacfile.h" #include "bacnet/basic/object/netport.h" #include "bacnet/basic/object/sc_netport.h" -#include "bacnet/basic/object/bacfile.h" #include "bacnet/basic/sys/debug.h" #include diff --git a/src/bacnet/datalink/dlenv.c b/src/bacnet/datalink/dlenv.c index d0c976f8..01c49a19 100644 --- a/src/bacnet/datalink/dlenv.c +++ b/src/bacnet/datalink/dlenv.c @@ -31,6 +31,7 @@ #include "bacnet/datalink/datalink.h" #include "bacnet/datalink/dlenv.h" #include "bacnet/datalink/dlmstp.h" +#include "bacfile-posix.h" /* enable debugging */ static bool Datalink_Debug; @@ -989,6 +990,9 @@ void dlenv_init(void) port_type = PORT_TYPE_NON_BACNET; #endif #endif + /* initialize the POSIX file objects */ + bacfile_posix_init(); + /* === Initialize the Network Port Object Here === */ Network_Port_Type_Set(Network_Port_Instance, port_type); switch (port_type) { case PORT_TYPE_BIP: diff --git a/test/bacnet/basic/object/netport/CMakeLists.txt b/test/bacnet/basic/object/netport/CMakeLists.txt index f4b55e49..9f818a73 100644 --- a/test/bacnet/basic/object/netport/CMakeLists.txt +++ b/test/bacnet/basic/object/netport/CMakeLists.txt @@ -17,6 +17,11 @@ string(REGEX REPLACE "/test" TST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/ports" + PORTS_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( @@ -34,6 +39,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${PORTS_DIR}/posix ${TST_DIR}/bacnet/basic/object/test ${TST_DIR}/ztest/include ) @@ -80,6 +86,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/datalink/bvlc.c ${SRC_DIR}/bacnet/datalink/bvlc6.c ${SRC_DIR}/bacnet/datalink/bsc/bsc-util.c + ${PORTS_DIR}/posix/bacfile-posix.c # Test and test library files ${TST_DIR}/bacnet/basic/object/test/device_mock.c ${TST_DIR}/bacnet/basic/object/test/datetime_local.c diff --git a/test/bacnet/basic/object/netport/src/main.c b/test/bacnet/basic/object/netport/src/main.c index 2d24784e..48ab17d1 100644 --- a/test/bacnet/basic/object/netport/src/main.c +++ b/test/bacnet/basic/object/netport/src/main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #ifndef BACDL_BSC #define BACDL_BSC @@ -765,6 +766,7 @@ static void test_network_port_sc_certificates(void) zassert_true(count > 0, NULL); bacfile_init(); + bacfile_posix_init(); // CA certificate status = bacfile_create(BSC_ISSUER_CERTIFICATE_FILE_1_INSTANCE); zassert_true(status, NULL); diff --git a/test/bacnet/datalink/bsc-datalink/CMakeLists.txt b/test/bacnet/datalink/bsc-datalink/CMakeLists.txt index 9f98ea2e..3c90014d 100644 --- a/test/bacnet/datalink/bsc-datalink/CMakeLists.txt +++ b/test/bacnet/datalink/bsc-datalink/CMakeLists.txt @@ -27,8 +27,8 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 - BACDL_BSC MAX_BACFILES=4 + BACFILE=1 BSC_CONF_WSURL_MAX_LEN=128 BSC_CONF_WEBSOCKET_ERR_DESC_STR_MAX_LEN=128 BSC_CONF_HUB_FUNCTION_CONNECTIONS_NUM=3 @@ -47,6 +47,7 @@ add_compile_definitions( include_directories( ${SRC_DIR} + ${PORTS_DIR}/posix ${TST_DIR}/ztest/include ) @@ -197,6 +198,7 @@ target_sources(${PROJECT_NAME} PRIVATE ${SRC_DIR}/bacnet/timestamp.c ${SRC_DIR}/bacnet/weeklyschedule.c ${SRC_DIR}/bacnet/wp.c + ${PORTS_DIR}/posix/bacfile-posix.c # Test and test library files ./src/main.c ${TST_DIR}/bacnet/basic/object/test/device_mock.c diff --git a/test/bacnet/datalink/bsc-datalink/src/main.c b/test/bacnet/datalink/bsc-datalink/src/main.c index 755a35a4..5023845f 100644 --- a/test/bacnet/datalink/bsc-datalink/src/main.c +++ b/test/bacnet/datalink/bsc-datalink/src/main.c @@ -23,7 +23,8 @@ #include #include #include -#include "bacnet/basic/sys/debug.h" +#include +#include unsigned char ca_key[] = { 0x2d, 0x2d, @@ -10004,6 +10005,7 @@ static void test_sc_parameters(void) // prepare bacfile_init(); + bacfile_posix_init(); netport_object_init( SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, sizeof(server_key), @@ -10017,7 +10019,9 @@ static void test_sc_parameters(void) // check zassert_equal( - bsc_conf.ca_cert_chain_size, sizeof(ca_cert) + ZERO_BYTE, NULL); + bsc_conf.ca_cert_chain_size, sizeof(ca_cert) + ZERO_BYTE, + "ca_cert=%zu actual=%zu", bsc_conf.ca_cert_chain_size, + sizeof(ca_cert) + ZERO_BYTE); zassert_mem_equal(bsc_conf.ca_cert_chain, ca_cert, sizeof(ca_cert), NULL); zassert_equal( bsc_conf.cert_chain_size, sizeof(server_cert) + ZERO_BYTE, NULL); @@ -10155,6 +10159,7 @@ static void test_sc_datalink(void) SC_NETPORT_DIRECT_SERVER_PORT); bacfile_init(); + bacfile_posix_init(); netport_object_init( SC_DATALINK_INSTANCE, ca_cert, sizeof(ca_cert), server_cert, sizeof(server_cert), server_key, sizeof(server_key),