diff --git a/bacnet-stack/bacstr.h b/bacnet-stack/bacstr.h index 048d9f4a..52637002 100644 --- a/bacnet-stack/bacstr.h +++ b/bacnet-stack/bacstr.h @@ -115,8 +115,8 @@ extern "C" { char_string); size_t characterstring_capacity(BACNET_CHARACTER_STRING * char_string); -/* returns false if the string exceeds capacity - initialize by using length=0 */ + /* returns false if the string exceeds capacity + initialize by using length=0 */ bool octetstring_init(BACNET_OCTET_STRING * octet_string, uint8_t * value, size_t length); bool octetstring_copy(BACNET_OCTET_STRING * dest, diff --git a/bacnet-stack/demo/handler/h_awf.c b/bacnet-stack/demo/handler/h_awf.c new file mode 100644 index 00000000..cc27d945 --- /dev/null +++ b/bacnet-stack/demo/handler/h_awf.c @@ -0,0 +1,151 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include +#include +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "awf.h" +/* demo objects */ +#include "device.h" +#include "bacfile.h" + +/* +from BACnet SSPC-135-2004 + +14. FILE ACCESS SERVICES + +This clause defines the set of services used to access and +manipulate files contained in BACnet devices. The concept of files +is used here as a network-visible representation for a collection +of octets of arbitrary length and meaning. This is an abstract +concept only and does not imply the use of disk, tape or other +mass storage devices in the server devices. These services may +be used to access vendor-defined files as well as specific +files defined in the BACnet protocol standard. +Every file that is accessible by File Access Services shall +have a corresponding File object in the BACnet device. This File +object is used to identify the particular file by name. In addition, +the File object provides access to "header information," such +as the file's total size, creation date, and type. File Access +Services may model files in two ways: as a continuous stream of +octets or as a contiguous sequence of numbered records. +The File Access Services provide atomic read and write operations. +In this context "atomic" means that during the execution +of a read or write operation, no other AtomicReadFile or +AtomicWriteFile operations are allowed for the same file. +Synchronization of these services with internal operations +of the BACnet device is a local matter and is not defined by this +standard. +*/ + +void handler_atomic_write_file(uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + BACNET_ATOMIC_WRITE_FILE_DATA data; + int len = 0; + int pdu_len = 0; + bool error = false; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS my_address; + BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT; + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + +#if PRINT_ENABLED + fprintf(stderr, "Received AtomicWriteFile Request!\n"); +#endif + len = awf_decode_service_request(service_request, service_len, &data); + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, + &my_address, &npdu_data); + /* bad decoding - send an abort */ + if (len < 0) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_OTHER, true); +#if PRINT_ENABLED + fprintf(stderr, "Bad Encoding. Sending Abort!\n"); +#endif + } else if (service_data->segmented_message) { + len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, "Segmented Message. Sending Abort!\n"); +#endif + } else if (data.object_type == OBJECT_FILE) { + if (!bacfile_valid_instance(data.object_instance)) { + error = true; + } else if (data.access == FILE_STREAM_ACCESS) { + if (bacfile_write_stream_data(&data)) { + len = + encode_simple_ack(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_ATOMIC_WRITE_FILE); + } else { + error = true; + error_class = ERROR_CLASS_OBJECT; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD; +#if PRINT_ENABLED + fprintf(stderr, "Record Access Requested. Sending Error!\n"); +#endif + } + } else { + error = true; + error_class = ERROR_CLASS_SERVICES; + error_code = ERROR_CODE_FILE_ACCESS_DENIED; + } + if (error) { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_ATOMIC_READ_FILE, error_class, error_code); + } + pdu_len += len; + bytes_sent = datalink_send_pdu(src, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); +#if PRINT_ENABLED + if (bytes_sent <= 0) { + fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno)); + } +#endif + + return; +} diff --git a/bacnet-stack/demo/handler/handlers.h b/bacnet-stack/demo/handler/handlers.h index b11a5b20..0ad74fc6 100644 --- a/bacnet-stack/demo/handler/handlers.h +++ b/bacnet-stack/demo/handler/handlers.h @@ -77,6 +77,10 @@ extern "C" { uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_atomic_write_file(uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); void handler_reinitialize_device(uint8_t * service_request, uint16_t service_len, diff --git a/bacnet-stack/demo/object/bacfile.c b/bacnet-stack/demo/object/bacfile.c index 4e00f8cd..d2b1be4f 100644 --- a/bacnet-stack/demo/object/bacfile.c +++ b/bacnet-stack/demo/object/bacfile.c @@ -374,3 +374,30 @@ bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA * data) return found; } + +bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA * data) +{ + char *pFilename = NULL; + bool found = false; + FILE *pFile = NULL; + + pFilename = bacfile_name(data->object_instance); + if (pFilename) { + found = true; + /* open the file as a clean slate when starting at 0 */ + if (data->type.stream.fileStartPosition == 0) + pFile = fopen(pFilename, "wb"); + else + pFile = fopen(pFilename, "rb+"); + if (pFile) { + (void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET); + if (fwrite(octetstring_value(&data->fileData), + octetstring_length(&data->fileData),1,pFile) != 1) { + + } + fclose(pFile); + } + } + + return found; +} diff --git a/bacnet-stack/demo/object/bacfile.h b/bacnet-stack/demo/object/bacfile.h index a80714db..82cefe94 100644 --- a/bacnet-stack/demo/object/bacfile.h +++ b/bacnet-stack/demo/object/bacfile.h @@ -40,6 +40,7 @@ #include "bacenum.h" #include "apdu.h" #include "arf.h" +#include "awf.h" #ifdef __cplusplus extern "C" { @@ -51,16 +52,17 @@ extern "C" { uint32_t bacfile_index_to_instance(unsigned find_index); uint32_t bacfile_instance(char *filename); #if TSM_ENABLED -/* this is one way to match up the invoke ID with */ -/* the file ID from the AtomicReadFile request. */ -/* Another way would be to store the */ -/* invokeID and file instance in a list or table */ -/* when the request was sent */ + /* this is one way to match up the invoke ID with */ + /* the file ID from the AtomicReadFile request. */ + /* Another way would be to store the */ + /* invokeID and file instance in a list or table */ + /* when the request was sent */ uint32_t bacfile_instance_from_tsm(uint8_t invokeID); #endif -/* AtomicReadFile ACK helper */ + /* handler ACK helper */ bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA * data); + bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA * data); /* handling for read property service */ int bacfile_encode_property_apdu(uint8_t * apdu, diff --git a/bacnet-stack/demo/server/Makefile b/bacnet-stack/demo/server/Makefile index 0aec2d2a..a08571ce 100644 --- a/bacnet-stack/demo/server/Makefile +++ b/bacnet-stack/demo/server/Makefile @@ -31,6 +31,7 @@ SRCS = main.c \ $(BACNET_HANDLER)/h_rp.c \ $(BACNET_HANDLER)/h_wp.c \ $(BACNET_HANDLER)/h_arf.c \ + $(BACNET_HANDLER)/h_awf.c \ $(BACNET_HANDLER)/h_rd.c \ $(BACNET_HANDLER)/h_dcc.c \ $(BACNET_HANDLER)/h_ts.c \ @@ -63,6 +64,7 @@ SRCS = main.c \ $(BACNET_ROOT)/rp.c \ $(BACNET_ROOT)/wp.c \ $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/awf.c \ $(BACNET_ROOT)/rd.c \ $(BACNET_ROOT)/dcc.c \ $(BACNET_ROOT)/timesync.c \ diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c index 8c12c527..ec4fdae2 100644 --- a/bacnet-stack/demo/server/main.c +++ b/bacnet-stack/demo/server/main.c @@ -70,6 +70,8 @@ static void Init_Service_Handlers(void) handler_write_property); apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, handler_atomic_read_file); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, + handler_atomic_write_file); apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device); apdu_set_unconfirmed_handler diff --git a/bacnet-stack/demo/server/makefile.b32 b/bacnet-stack/demo/server/makefile.b32 index a57195c6..e284909c 100644 --- a/bacnet-stack/demo/server/makefile.b32 +++ b/bacnet-stack/demo/server/makefile.b32 @@ -28,6 +28,7 @@ SRCS = main.c \ ..\..\demo\handler\h_rp.c \ ..\..\demo\handler\h_wp.c \ ..\..\demo\handler\h_arf.c \ + ..\..\demo\handler\h_awf.c \ ..\..\demo\handler\h_rd.c \ ..\..\demo\handler\h_dcc.c \ ..\..\demo\handler\h_ts.c \