diff --git a/bacnet-stack/bacfile.c b/bacnet-stack/bacfile.c new file mode 100644 index 00000000..a3b77612 --- /dev/null +++ b/bacnet-stack/bacfile.c @@ -0,0 +1,325 @@ +/************************************************************************** +* +* Copyright (C) 2005 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 "address.h" +#include "bacdef.h" +#include "datalink.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "tsm.h" +#include "device.h" +#include "arf.h" +#include "awf.h" + +typedef struct +{ + uint32_t instance; + char *filename; +} BACNET_FILE_LISTING; + +static BACNET_FILE_LISTING BACnet_File_Listing[] = +{ + {0, "temp.txt"}, + {1, "script.txt"}, + {0, NULL} // last file indication +}; + +char *bacfile_name(uint32_t instance) +{ + uint32_t index = 0; + char *filename = NULL; + + // linear search for file instance match + while (BACnet_File_Listing[index].filename) + { + if (BACnet_File_Listing[index].instance == instance) + { + filename = BACnet_File_Listing[index].filename; + break; + } + index++; + } + + return filename; +} + +bool bacfile_valid_instance(uint32_t object_instance) +{ + return bacfile_name(object_instance) ? true : false; +} + +uint32_t bacfile_count(void) +{ + uint32_t index = 0; + + // linear search for file instance match + while (BACnet_File_Listing[index].filename) + { + index++; + } + + return index; +} + +uint32_t bacfile_index_to_instance(unsigned find_index) +{ + uint32_t instance = BACNET_MAX_INSTANCE + 1; + uint32_t index = 0; + + // bounds checking... + while (BACnet_File_Listing[index].filename) + { + if (index == find_index) + { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +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); +} + +static unsigned bacfile_file_size(uint32_t object_instance) +{ + char *pFilename = NULL; + FILE *pFile = NULL; + unsigned file_size = 0; + + pFilename = bacfile_name(object_instance); + if (pFilename) + { + pFile = fopen(pFilename,"rb"); + if (pFile) + { + file_size = fsize(pFile); + fclose(pFile); + } + } + + return file_size; +} + +int bacfile_encode_property_apdu( + uint8_t *apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code) +{ + int apdu_len = 0; // return value + char text_string[32] = {""}; + + (void)array_index; + switch (property) + { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], + OBJECT_FILE, + object_instance); + break; + case PROP_OBJECT_NAME: + sprintf(text_string,"FILE %d",object_instance); + apdu_len = encode_tagged_character_string(&apdu[0], text_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_FILE); + break; + case PROP_DESCRIPTION: + apdu_len = encode_tagged_character_string(&apdu[0], + bacfile_name(object_instance)); + break; + case PROP_FILE_TYPE: + apdu_len = encode_tagged_character_string(&apdu[0],"TEXT"); + break; + case PROP_FILE_SIZE: + apdu_len = encode_tagged_unsigned(&apdu[0], + bacfile_file_size(object_instance)); + break; + case PROP_MODIFICATION_DATE: + // FIXME: get the actual value + apdu_len = encode_tagged_date(&apdu[0], + 2005, + 12, + 25, + 7 /* sunday */); + // FIXME: get the actual value + apdu_len += encode_tagged_time(&apdu[apdu_len], + 12, + 0, + 0, + 0); + break; + case PROP_ARCHIVE: + // FIXME: get the actual value: note it may be inverse... + apdu_len = encode_tagged_boolean(&apdu[0],true); + break; + case PROP_READ_ONLY: + // FIXME: get the actual value + apdu_len = encode_tagged_boolean(&apdu[0],true); + break; + case PROP_FILE_ACCESS_METHOD: + apdu_len = encode_tagged_enumerated(&apdu[0],FILE_STREAM_ACCESS); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return apdu_len; +} + +uint32_t bacfile_instance(char *filename) +{ + uint32_t index = 0; + uint32_t instance = BACNET_MAX_INSTANCE + 1; + + // linear search for filename match + while (BACnet_File_Listing[index].filename) + { + if (strcmp(BACnet_File_Listing[index].filename,filename) == 0) + { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +// 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) +{ + BACNET_NPDU_DATA npdu_data = {0}; // dummy for getting npdu length + BACNET_CONFIRMED_SERVICE_DATA service_data = {0}; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + BACNET_ADDRESS dest; // where the original packet was destined + uint8_t pdu[MAX_PDU] = {0}; // original sent packet + uint16_t pdu_len = 0; // original packet length + uint16_t len = 0; // apdu header length + BACNET_ATOMIC_READ_FILE_DATA data = {0}; + uint32_t object_instance = BACNET_MAX_INSTANCE + 1; // return value + bool found = false; + int apdu_offset = 0; + + found = tsm_get_transaction_pdu( + invokeID, + &dest, + &pdu[0], + &pdu_len); + if (found) + { + apdu_offset = npdu_decode( + &pdu[0], // data to decode + NULL, // destination address - get the DNET/DLEN/DADR if in there + NULL, // source address - get the SNET/SLEN/SADR if in there + &npdu_data); // amount of data to decode + if (!npdu_data.network_layer_message && + ((pdu[apdu_offset] & 0xF0) == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) + { + len = apdu_decode_confirmed_service_request( + &pdu[apdu_offset], // APDU data + pdu_len - apdu_offset, + &service_data, + &service_choice, + &service_request, + &service_request_len); + if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) + { + len = arf_decode_service_request( + service_request, + service_request_len, + &data); + if (len > 0) + { + if (data.object_type == OBJECT_FILE) + object_instance = data.object_instance; + } + } + } + } + + return object_instance; +} + +bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA *data) +{ + char *pFilename = NULL; + bool found = false; + FILE *pFile = NULL; + + pFilename = bacfile_name(data->object_instance); + if (pFilename) + { + pFile = fopen(pFilename,"rb"); + if (pFile) + { + (void)fseek(pFile, + data->type.stream.fileStartPosition, + SEEK_SET); + data->fileDataLength = fread(data->fileData, 1, + data->type.stream.requestedOctetCount, pFile); + fclose(pFile); + } + else + data->fileDataLength = 0; + } + else + data->fileDataLength = 0; + + return found; +} + diff --git a/bacnet-stack/bacfile.h b/bacnet-stack/bacfile.h new file mode 100644 index 00000000..a1bfe10f --- /dev/null +++ b/bacnet-stack/bacfile.h @@ -0,0 +1,69 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef BACFILE_H +#define BACFILE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "apdu.h" +#include "arf.h" + +char *bacfile_name(uint32_t instance); +bool bacfile_valid_instance(uint32_t object_instance); +uint32_t bacfile_count(void); +uint32_t bacfile_index_to_instance(unsigned find_index); +uint32_t bacfile_instance(char *filename); +// 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); + +// AtomicReadFile ACK helper +bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA *data); + +// handling for read property service +int bacfile_encode_property_apdu( + uint8_t *apdu, + uint32_t object_instance, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS *error_class, + BACNET_ERROR_CODE *error_code); + +#endif