From ece1f05de91ee9fe41858f0fc3d3842d03229f04 Mon Sep 17 00:00:00 2001 From: skarg Date: Fri, 24 Jul 2009 16:19:48 +0000 Subject: [PATCH] Corrected EOL line style and line endings on files. --- bacnet-stack/include/mstpdef.h | 240 ++-- bacnet-stack/ports/bdk-atxx4-mstp/adc.c | 188 +-- bacnet-stack/ports/bdk-atxx4-mstp/adc.h | 80 +- bacnet-stack/ports/bdk-atxx4-mstp/av.c | 914 +++++++------- bacnet-stack/ports/win32/dlmstp-mm.c | 1472 +++++++++++------------ 5 files changed, 1447 insertions(+), 1447 deletions(-) diff --git a/bacnet-stack/include/mstpdef.h b/bacnet-stack/include/mstpdef.h index cf219bff..dca2088e 100644 --- a/bacnet-stack/include/mstpdef.h +++ b/bacnet-stack/include/mstpdef.h @@ -1,120 +1,120 @@ -/*####COPYRIGHTBEGIN#### - ------------------------------------------- - Copyright (C) 2004 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 MSTPDEF_H -#define MSTPDEF_H - -#include -#include -#include -#include "bacdef.h" - -/* The value 255 is used to denote broadcast when used as a */ -/* destination address but is not allowed as a value for a station. */ -/* Station addresses for master nodes can be 0-127. */ -/* Station addresses for slave nodes can be 127-254. */ -#define MSTP_BROADCAST_ADDRESS 255 - -/* MS/TP Frame Type */ -/* Frame Types 8 through 127 are reserved by ASHRAE. */ -#define FRAME_TYPE_TOKEN 0 -#define FRAME_TYPE_POLL_FOR_MASTER 1 -#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 -#define FRAME_TYPE_TEST_REQUEST 3 -#define FRAME_TYPE_TEST_RESPONSE 4 -#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 -#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 -#define FRAME_TYPE_REPLY_POSTPONED 7 -/* Frame Types 128 through 255: Proprietary Frames */ -/* These frames are available to vendors as proprietary (non-BACnet) frames. */ -/* The first two octets of the Data field shall specify the unique vendor */ -/* identification code, most significant octet first, for the type of */ -/* vendor-proprietary frame to be conveyed. The length of the data portion */ -/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ -#define FRAME_TYPE_PROPRIETARY_MIN 128 -#define FRAME_TYPE_PROPRIETARY_MAX 255 -/* The initial CRC16 checksum value */ -#define CRC16_INITIAL_VALUE (0xFFFF) - -/* receive FSM states */ -typedef enum { - MSTP_RECEIVE_STATE_IDLE = 0, - MSTP_RECEIVE_STATE_PREAMBLE = 1, - MSTP_RECEIVE_STATE_HEADER = 2, - MSTP_RECEIVE_STATE_DATA = 3 -} MSTP_RECEIVE_STATE; - -/* master node FSM states */ -typedef enum { - MSTP_MASTER_STATE_INITIALIZE = 0, - MSTP_MASTER_STATE_IDLE = 1, - MSTP_MASTER_STATE_USE_TOKEN = 2, - MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, - MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, - MSTP_MASTER_STATE_PASS_TOKEN = 5, - MSTP_MASTER_STATE_NO_TOKEN = 6, - MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, - MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 -} MSTP_MASTER_STATE; - -/* The time without a DataAvailable or ReceiveError event before declaration */ -/* of loss of token: 500 milliseconds. */ -#define Tno_token 500 - -/* The minimum time after the end of the stop bit of the final octet of a */ -/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ -/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ -/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ -/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ -/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ -/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ -/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ -/* 40 bits is 4 octets including a start and stop bit with each octet */ -#define Tturnaround (40UL) -/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ - -#define DEFAULT_MAX_INFO_FRAMES 1 -#define DEFAULT_MAX_MASTER 127 -#define DEFAULT_MAC_ADDRESS 127 - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - - -#ifdef __cplusplus -} -#endif /* __cplusplus */ -#endif +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 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 MSTPDEF_H +#define MSTPDEF_H + +#include +#include +#include +#include "bacdef.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 +#define DEFAULT_MAC_ADDRESS 127 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/adc.c b/bacnet-stack/ports/bdk-atxx4-mstp/adc.c index adffa440..dc00d5ea 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/adc.c +++ b/bacnet-stack/ports/bdk-atxx4-mstp/adc.c @@ -1,94 +1,94 @@ -/************************************************************************** -* -* Copyright (C) 2009 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 "hardware.h" - -/* prescale select bits */ -#if (F_CPU >> 1) < 1000000 - #define ADPS_8BIT (1) - #define ADPS_10BIT (3) -#elif (F_CPU >> 2) < 1000000 - #define ADPS_8BIT (2) - #define ADPS_10BIT (4) -#elif (F_CPU >> 3) < 1000000 - #define ADPS_8BIT (3) - #define ADPS_10BIT (5) -#elif (F_CPU >> 4) < 1000000 - #define ADPS_8BIT (4) - #define ADPS_10BIT (6) -#elif (F_CPU >> 5) < 1000000 - #define ADPS_8BIT (5) - #define ADPS_10BIT (7) -#else - #error "ADC: F_CPU too large for accuracy." -#endif - -/* we could have array of ADC results */ -static volatile uint8_t Sample_Result; - -ISR(ADC_vect) -{ - /* since we configured as ADLAR=1, get value from ADCH */ - Sample_Result = ADCH; -} - -uint8_t adc_result( - uint8_t channel) /* 0..7 = ADC0..ADC7, respectively */ -{ - return Sample_Result; -} - -void adc_init(void) -{ - /* set prescaler */ - ADCSRA |= ADPS_8BIT; - /* Initial channel selection */ - /* ADLAR = Left Adjust Result - REFSx = hardware setup: cap on AREF - */ - ADMUX = 7 /* channel */ | (1 << ADLAR) | (0 << REFS1) | (1 << REFS0); - /* ADEN = Enable - ADSC = Start conversion - ADIF = Interrupt Flag - ADIE = Interrupt Enable - ADATE = Auto Trigger Enable - */ - ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADIF) | (1 << ADATE); - /* trigger selection bits - 0 0 0 Free Running mode - 0 0 1 Analog Comparator - 0 1 0 External Interrupt Request 0 - 0 1 1 Timer/Counter0 Compare Match - 1 0 0 Timer/Counter0 Overflow - 1 0 1 Timer/Counter1 Compare Match B - 1 1 0 Timer/Counter1 Overflow - 1 1 1 Timer/Counter1 Capture Event - */ - ADCSRB |= (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0); - /* start the conversions */ - ADCSRA |= (1 << ADSC); - /* Clear the Power Reduction bit */ - BIT_CLEAR(PRR, PRADC); -} +/************************************************************************** +* +* Copyright (C) 2009 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 "hardware.h" + +/* prescale select bits */ +#if (F_CPU >> 1) < 1000000 + #define ADPS_8BIT (1) + #define ADPS_10BIT (3) +#elif (F_CPU >> 2) < 1000000 + #define ADPS_8BIT (2) + #define ADPS_10BIT (4) +#elif (F_CPU >> 3) < 1000000 + #define ADPS_8BIT (3) + #define ADPS_10BIT (5) +#elif (F_CPU >> 4) < 1000000 + #define ADPS_8BIT (4) + #define ADPS_10BIT (6) +#elif (F_CPU >> 5) < 1000000 + #define ADPS_8BIT (5) + #define ADPS_10BIT (7) +#else + #error "ADC: F_CPU too large for accuracy." +#endif + +/* we could have array of ADC results */ +static volatile uint8_t Sample_Result; + +ISR(ADC_vect) +{ + /* since we configured as ADLAR=1, get value from ADCH */ + Sample_Result = ADCH; +} + +uint8_t adc_result( + uint8_t channel) /* 0..7 = ADC0..ADC7, respectively */ +{ + return Sample_Result; +} + +void adc_init(void) +{ + /* set prescaler */ + ADCSRA |= ADPS_8BIT; + /* Initial channel selection */ + /* ADLAR = Left Adjust Result + REFSx = hardware setup: cap on AREF + */ + ADMUX = 7 /* channel */ | (1 << ADLAR) | (0 << REFS1) | (1 << REFS0); + /* ADEN = Enable + ADSC = Start conversion + ADIF = Interrupt Flag + ADIE = Interrupt Enable + ADATE = Auto Trigger Enable + */ + ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1 << ADIF) | (1 << ADATE); + /* trigger selection bits + 0 0 0 Free Running mode + 0 0 1 Analog Comparator + 0 1 0 External Interrupt Request 0 + 0 1 1 Timer/Counter0 Compare Match + 1 0 0 Timer/Counter0 Overflow + 1 0 1 Timer/Counter1 Compare Match B + 1 1 0 Timer/Counter1 Overflow + 1 1 1 Timer/Counter1 Capture Event + */ + ADCSRB |= (0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0); + /* start the conversions */ + ADCSRA |= (1 << ADSC); + /* Clear the Power Reduction bit */ + BIT_CLEAR(PRR, PRADC); +} diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/adc.h b/bacnet-stack/ports/bdk-atxx4-mstp/adc.h index 75b1827f..c44d76c0 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/adc.h +++ b/bacnet-stack/ports/bdk-atxx4-mstp/adc.h @@ -1,40 +1,40 @@ -/************************************************************************** -* -* Copyright (C) 2009 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. -*********************************************************************/ -#ifndef ADC_H -#define ADC_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -uint8_t adc_result(uint8_t channel); -void adc_init(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif +/************************************************************************** +* +* Copyright (C) 2009 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. +*********************************************************************/ +#ifndef ADC_H +#define ADC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +uint8_t adc_result(uint8_t channel); +void adc_init(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif diff --git a/bacnet-stack/ports/bdk-atxx4-mstp/av.c b/bacnet-stack/ports/bdk-atxx4-mstp/av.c index 057cb5aa..2562e650 100644 --- a/bacnet-stack/ports/bdk-atxx4-mstp/av.c +++ b/bacnet-stack/ports/bdk-atxx4-mstp/av.c @@ -1,457 +1,457 @@ -/************************************************************************** -* -* Copyright (C) 2006 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. -* -*********************************************************************/ - -/* Analog Value Objects - customize for your use */ - -#include -#include -#include -#if defined(__GNUC__) && (__GNUC__ > 4) && (__GNUC_MINOR__ > 2) - #include /* for NAN */ -#else - #define NAN __builtin_nan("") -#endif - -#include "bacdef.h" -#include "bacdcode.h" -#include "bacenum.h" -#include "bacapp.h" -#include "config.h" /* the custom stuff */ -#include "wp.h" - -#ifndef MAX_ANALOG_VALUES -#define MAX_ANALOG_VALUES 4 -#endif - -static float Present_Value[MAX_ANALOG_VALUES]; - -/* These three arrays are used by the ReadPropertyMultiple handler */ -static const int Analog_Value_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 -}; - -static const int Analog_Value_Properties_Optional[] = { - PROP_DESCRIPTION, - PROP_PRIORITY_ARRAY, - PROP_RELINQUISH_DEFAULT, - -1 -}; - -static const int Analog_Value_Properties_Proprietary[] = { - -1 -}; - -void Analog_Value_Property_Lists( - const int **pRequired, - const int **pOptional, - const int **pProprietary) -{ - if (pRequired) - *pRequired = Analog_Value_Properties_Required; - if (pOptional) - *pOptional = Analog_Value_Properties_Optional; - if (pProprietary) - *pProprietary = Analog_Value_Properties_Proprietary; - - return; -} - -void Analog_Value_Init( - void) -{ - return; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need validate that the */ -/* given instance exists */ -bool Analog_Value_Valid_Instance( - uint32_t object_instance) -{ - if (object_instance < MAX_ANALOG_VALUES) - return true; - - return false; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then count how many you have */ -unsigned Analog_Value_Count( - void) -{ - return MAX_ANALOG_VALUES; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the instance */ -/* that correlates to the correct index */ -uint32_t Analog_Value_Index_To_Instance( - unsigned index) -{ - return index; -} - -/* we simply have 0-n object instances. Yours might be */ -/* more complex, and then you need to return the index */ -/* that correlates to the correct instance number */ -unsigned Analog_Value_Instance_To_Index( - uint32_t object_instance) -{ - unsigned index = MAX_ANALOG_VALUES; - - if (object_instance < MAX_ANALOG_VALUES) - index = object_instance; - - return index; -} - -float Analog_Value_Present_Value( - uint32_t object_instance) -{ - float value = NAN; - unsigned index = 0; - - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - value = Present_Value[index]; - } - - return value; -} - -bool Analog_Value_Present_Value_Set( - uint32_t object_instance, - float value, - uint8_t priority) -{ - unsigned index = 0; - bool status = false; - - priority = priority; - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - if (priority && (priority <= BACNET_MAX_PRIORITY) && - (priority != 6 /* reserved */ ) && - (value >= 0.0) && (value <= 100.0)) { - Present_Value[index] = value; - /* Note: you could set the physical output here if we - are the highest priority. - However, if Out of Service is TRUE, then don't set the - physical output. This comment may apply to the - main loop (i.e. check out of service before changing output) */ - status = true; - } - } - return status; -} - -/* note: the object name must be unique within this device */ -char *Analog_Value_Name( - uint32_t object_instance) -{ - static char text_string[32] = ""; /* okay for single thread */ - unsigned index = 0; - - index = Analog_Value_Instance_To_Index(object_instance); - if (index < MAX_ANALOG_VALUES) { - sprintf(text_string, "AV-%lu", object_instance); - return text_string; - } - - return NULL; -} - -/* return apdu len, or -1 on error */ -int Analog_Value_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 */ - BACNET_BIT_STRING bit_string; - BACNET_CHARACTER_STRING char_string; - float real_value = 1.414F; -#if 0 - unsigned object_index = 0; - unsigned i = 0; - bool state = false; -#endif - - switch (property) { - case PROP_OBJECT_IDENTIFIER: - apdu_len = - encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, - object_instance); - break; - case PROP_OBJECT_NAME: - case PROP_DESCRIPTION: - characterstring_init_ansi(&char_string, - Analog_Value_Name(object_instance)); - apdu_len = - encode_application_character_string(&apdu[0], &char_string); - break; - case PROP_OBJECT_TYPE: - apdu_len = - encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); - break; - case PROP_PRESENT_VALUE: - real_value = Analog_Value_Present_Value(object_instance); - apdu_len = encode_application_real(&apdu[0], real_value); - break; - case PROP_STATUS_FLAGS: - bitstring_init(&bit_string); - bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); - apdu_len = encode_application_bitstring(&apdu[0], &bit_string); - break; - case PROP_EVENT_STATE: - apdu_len = - encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); - break; - case PROP_OUT_OF_SERVICE: -#if 0 - object_index = Analog_Value_Instance_To_Index(object_instance); - state = Analog_Value_Out_Of_Service[object_index]; -#endif - apdu_len = encode_application_boolean(&apdu[0], false); - break; - case PROP_UNITS: - apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); - break; -#if 0 - case PROP_PRIORITY_ARRAY: - /* Array element zero is the number of elements in the array */ - if (array_index == 0) - apdu_len = - encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); - /* if no index was specified, then try to encode the entire list */ - /* into one packet. */ - else if (array_index == BACNET_ARRAY_ALL) { - object_index = Analog_Value_Instance_To_Index(object_instance); - for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - /* FIXME: check if we have room before adding it to APDU */ - if (Present_Value[object_index][i] == ANALOG_LEVEL_NULL) - len = encode_application_null(&apdu[apdu_len]); - else { - real_value = Present_Value[object_index][i]; - len = - encode_application_real(&apdu[apdu_len], - real_value); - } - /* add it if we have room */ - if ((apdu_len + len) < MAX_APDU) - apdu_len += len; - else { - *error_class = ERROR_CLASS_SERVICES; - *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; - apdu_len = -1; - break; - } - } - } else { - object_index = Analog_Value_Instance_To_Index(object_instance); - if (array_index <= BACNET_MAX_PRIORITY) { - if (Present_Value[object_index][array_index - 1] == - ANALOG_LEVEL_NULL) - apdu_len = encode_application_null(&apdu[0]); - else { - real_value = - Present_Value[object_index][array_index - 1]; - apdu_len = - encode_application_real(&apdu[0], real_value); - } - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; - apdu_len = -1; - } - } - - break; - case PROP_RELINQUISH_DEFAULT: - real_value = ANALOG_RELINQUISH_DEFAULT; - apdu_len = encode_application_real(&apdu[0], real_value); - break; -#endif - default: - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_UNKNOWN_PROPERTY; - apdu_len = -1; - break; - } - - return apdu_len; -} - -/* returns true if successful */ -bool Analog_Value_Write_Property( - BACNET_WRITE_PROPERTY_DATA * wp_data, - BACNET_ERROR_CLASS * error_class, - BACNET_ERROR_CODE * error_code) -{ - bool status = false; /* return value */ -#if 0 - unsigned int object_index = 0; - unsigned int priority = 0; -#endif - int len = 0; - BACNET_APPLICATION_DATA_VALUE value; - - if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { - *error_class = ERROR_CLASS_OBJECT; - *error_code = ERROR_CODE_UNKNOWN_OBJECT; - return false; - } - /* decode the some of the request */ - len = - bacapp_decode_application_data(wp_data->application_data, - wp_data->application_data_len, &value); - /* FIXME: len < application_data_len: more data? */ - /* FIXME: len == 0: unable to decode? */ - switch (wp_data->object_property) { - case PROP_PRESENT_VALUE: - if (value.tag == BACNET_APPLICATION_TAG_REAL) { - if (Analog_Value_Present_Value_Set( - wp_data->object_instance, - value.type.Real, - wp_data->priority)) { - status = true; - } else if (wp_data->priority == 6) { - /* Command priority 6 is reserved for use by Minimum On/Off - algorithm and may not be used for other purposes in any - object. */ - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } -#if 0 - } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { - if (Analog_Value_Present_Value_Set( - wp_data->object_instance, - NAN, - wp_data->priority)) { - status = true; - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; - } -#endif - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; -#if 0 - case PROP_OUT_OF_SERVICE: - if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { - object_index = - Analog_Value_Instance_To_Index(wp_data->object_instance); - Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; - status = true; - } else { - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_INVALID_DATA_TYPE; - } - break; -#endif - default: - *error_class = ERROR_CLASS_PROPERTY; - *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; - break; - } - - return status; -} - - -#ifdef TEST -#include -#include -#include "ctest.h" - -void testAnalog_Value( - Test * pTest) -{ - uint8_t apdu[MAX_APDU] = { 0 }; - int len = 0; - uint32_t len_value = 0; - uint8_t tag_number = 0; - BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE; - uint32_t decoded_instance = 0; - uint32_t instance = 123; - BACNET_ERROR_CLASS error_class; - BACNET_ERROR_CODE error_code; - - - len = - Analog_Value_Encode_Property_APDU(&apdu[0], instance, - PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); - ct_test(pTest, len != 0); - len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); - ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); - len = - decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); - ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE); - ct_test(pTest, decoded_instance == instance); - - return; -} - -#ifdef TEST_ANALOG_VALUE -int main( - void) -{ - Test *pTest; - bool rc; - - pTest = ct_create("BACnet Analog Value", NULL); - /* individual tests */ - rc = ct_addTestFunction(pTest, testAnalog_Value); - assert(rc); - - ct_setStream(pTest, stdout); - ct_run(pTest); - (void) ct_report(pTest); - ct_destroy(pTest); - - return 0; -} -#endif /* TEST_ANALOG_VALUE */ -#endif /* TEST */ +/************************************************************************** +* +* Copyright (C) 2006 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. +* +*********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include +#if defined(__GNUC__) && (__GNUC__ > 4) && (__GNUC_MINOR__ > 2) + #include /* for NAN */ +#else + #define NAN __builtin_nan("") +#endif + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 4 +#endif + +static float Present_Value[MAX_ANALOG_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Value_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 +}; + +static const int Analog_Value_Properties_Optional[] = { + PROP_DESCRIPTION, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + -1 +}; + +static const int Analog_Value_Properties_Proprietary[] = { + -1 +}; + +void Analog_Value_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Analog_Value_Properties_Required; + if (pOptional) + *pOptional = Analog_Value_Properties_Optional; + if (pProprietary) + *pProprietary = Analog_Value_Properties_Proprietary; + + return; +} + +void Analog_Value_Init( + void) +{ + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count( + void) +{ + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +float Analog_Value_Present_Value( + uint32_t object_instance) +{ + float value = NAN; + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = Present_Value[index]; + } + + return value; +} + +bool Analog_Value_Present_Value_Set( + uint32_t object_instance, + float value, + uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + priority = priority; + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= 0.0) && (value <= 100.0)) { + Present_Value[index] = value; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + return status; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name( + uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + sprintf(text_string, "AV-%lu", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_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 */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = 1.414F; +#if 0 + unsigned object_index = 0; + unsigned i = 0; + bool state = false; +#endif + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Value_Name(object_instance)); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ANALOG_VALUE); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(object_instance); + apdu_len = encode_application_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: +#if 0 + object_index = Analog_Value_Instance_To_Index(object_instance); + state = Analog_Value_Out_Of_Service[object_index]; +#endif + apdu_len = encode_application_boolean(&apdu[0], false); + break; + case PROP_UNITS: + apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + break; +#if 0 + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = Analog_Value_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Present_Value[object_index][i] == ANALOG_LEVEL_NULL) + len = encode_application_null(&apdu[apdu_len]); + else { + real_value = Present_Value[object_index][i]; + len = + encode_application_real(&apdu[apdu_len], + real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = Analog_Value_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Present_Value[object_index][array_index - 1] == + ANALOG_LEVEL_NULL) + apdu_len = encode_application_null(&apdu[0]); + else { + real_value = + Present_Value[object_index][array_index - 1]; + apdu_len = + encode_application_real(&apdu[0], real_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = ANALOG_RELINQUISH_DEFAULT; + apdu_len = encode_application_real(&apdu[0], real_value); + break; +#endif + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ +#if 0 + unsigned int object_index = 0; + unsigned int priority = 0; +#endif + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = + bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + if (Analog_Value_Present_Value_Set( + wp_data->object_instance, + value.type.Real, + wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#if 0 + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + if (Analog_Value_Present_Value_Set( + wp_data->object_instance, + NAN, + wp_data->priority)) { + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } +#endif + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#if 0 + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; +#endif + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalog_Value( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = + Analog_Value_Encode_Property_APDU(&apdu[0], instance, + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL, &error_class, &error_code); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = + decode_object_id(&apdu[len], (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == OBJECT_ANALOG_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/bacnet-stack/ports/win32/dlmstp-mm.c b/bacnet-stack/ports/win32/dlmstp-mm.c index a98002a3..a0b6b4eb 100644 --- a/bacnet-stack/ports/win32/dlmstp-mm.c +++ b/bacnet-stack/ports/win32/dlmstp-mm.c @@ -1,736 +1,736 @@ -/************************************************************************** -* -* Copyright (C) 2006 Steve Karg -* Multimedia Timer contribution by Cameron Crothers, 2008 -* -* 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 -#include "bacdef.h" -#include "bacaddr.h" -#include "mstp.h" -#include "dlmstp.h" -#include "rs485.h" -#include "npdu.h" -#include "bits.h" - -#define WIN32_LEAN_AND_MEAN -#define STRICT 1 -#include -#include /* for multimedia timers */ - -/* Number of MS/TP Packets Rx/Tx */ -uint16_t MSTP_Packets = 0; - -/* packet queues */ -static DLMSTP_PACKET Receive_Packet; -static HANDLE Receive_Packet_Flag; -/* mechanism to wait for a frame in state machine */ -HANDLE Received_Frame_Flag; -static DLMSTP_PACKET Transmit_Packet; -/* local MS/TP port data - shared with RS-485 */ -volatile struct mstp_port_struct_t MSTP_Port; -/* buffers needed by mstp port struct */ -static uint8_t TxBuffer[MAX_MPDU]; -static uint8_t RxBuffer[MAX_MPDU]; -/* Timer that indicates line silence - and functions */ -static uint32_t SilenceStartTime; -static uint32_t TimeBeginPeriod; -/* 1-millisecond target resolution */ -#define TARGET_RESOLUTION 1 - -static uint16_t Timer_Silence( - void) -{ - uint32_t now = timeGetTime(); - uint32_t delta_time = 0; - - if (SilenceStartTime < now) { - delta_time = now - SilenceStartTime; - } else { - delta_time = (UINT32_MAX - SilenceStartTime) + now + 1; - } - if (delta_time > 0xFFFF) { - delta_time = 0xFFFF; - } - - return (uint16_t)delta_time; -} - -static void Timer_Silence_Reset( - void) -{ - SilenceStartTime = timeGetTime(); -} - -void dlmstp_reinit( - void) -{ - /*RS485_Reinit(); */ - dlmstp_set_mac_address(DEFAULT_MAC_ADDRESS); - dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); - dlmstp_set_max_master(DEFAULT_MAX_MASTER); - /* reset timer resolution */ - timeEndPeriod(TimeBeginPeriod); -} - -void dlmstp_cleanup( - void) -{ - /* nothing to do for static buffers */ - if (Received_Frame_Flag) { - CloseHandle(Received_Frame_Flag); - } - if (Receive_Packet_Flag) { - CloseHandle(Receive_Packet_Flag); - } -} - -/* returns number of bytes sent on success, zero on failure */ -int dlmstp_send_pdu( - BACNET_ADDRESS * dest, /* destination address */ - BACNET_NPDU_DATA * npdu_data, /* network information */ - uint8_t * pdu, /* any data to be sent - may be null */ - unsigned pdu_len) -{ /* number of bytes of data */ - int bytes_sent = 0; - unsigned i = 0; - - if (!Transmit_Packet.ready) { - if (npdu_data->data_expecting_reply) { - Transmit_Packet.frame_type = - FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; - } else { - Transmit_Packet.frame_type = - FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; - } - Transmit_Packet.pdu_len = pdu_len; - for (i = 0; i < pdu_len; i++) { - Transmit_Packet.pdu[i] = pdu[i]; - } - bacnet_address_copy(&Transmit_Packet.address, dest); - bytes_sent = pdu_len + MAX_HEADER; - Transmit_Packet.ready = true; - } - - return bytes_sent; -} - -uint16_t dlmstp_receive( - BACNET_ADDRESS * src, /* source address */ - uint8_t * pdu, /* PDU data */ - uint16_t max_pdu, /* amount of space available in the PDU */ - unsigned timeout) -{ /* milliseconds to wait for a packet */ - uint16_t pdu_len = 0; - DWORD wait_status = 0; - - (void) max_pdu; - /* see if there is a packet available, and a place - to put the reply (if necessary) and process it */ - wait_status = WaitForSingleObject(Receive_Packet_Flag, timeout); - if (wait_status == WAIT_OBJECT_0) { - if (Receive_Packet.ready) { - if (Receive_Packet.pdu_len) { - MSTP_Packets++; - if (src) { - memmove(src, &Receive_Packet.address, - sizeof(Receive_Packet.address)); - } - if (pdu) { - memmove(pdu, &Receive_Packet.pdu, - sizeof(Receive_Packet.pdu)); - } - pdu_len = Receive_Packet.pdu_len; - } - Receive_Packet.ready = false; - } - } - - return pdu_len; -} - -static void dlmstp_receive_fsm_task( - void *pArg) -{ - bool received_frame; - - (void) pArg; - (void) SetThreadPriority(GetCurrentThread(), - THREAD_PRIORITY_TIME_CRITICAL); - while (TRUE) { - /* only do receive state machine while we don't have a frame */ - if ((MSTP_Port.ReceivedValidFrame == false) && - (MSTP_Port.ReceivedInvalidFrame == false)) { - do { - RS485_Check_UART_Data(&MSTP_Port); - MSTP_Receive_Frame_FSM(&MSTP_Port); - received_frame = MSTP_Port.ReceivedValidFrame || - MSTP_Port.ReceivedInvalidFrame; - if (received_frame) { - ReleaseSemaphore(Received_Frame_Flag, 1, NULL); - break; - } - } while (MSTP_Port.DataAvailable); - } - } -} - -static void dlmstp_master_fsm_task( - void *pArg) -{ - DWORD dwMilliseconds = 0; - - (void) pArg; - (void) SetThreadPriority(GetCurrentThread(), - THREAD_PRIORITY_TIME_CRITICAL); - while (TRUE) { - switch (MSTP_Port.master_state) { - case MSTP_MASTER_STATE_IDLE: - dwMilliseconds = Tno_token; - break; - case MSTP_MASTER_STATE_WAIT_FOR_REPLY: - dwMilliseconds = Treply_timeout; - break; - case MSTP_MASTER_STATE_POLL_FOR_MASTER: - dwMilliseconds = Tusage_timeout; - break; - default: - dwMilliseconds = 0; - break; - } - if (dwMilliseconds) - WaitForSingleObject(Received_Frame_Flag, dwMilliseconds); - MSTP_Master_Node_FSM(&MSTP_Port); - } -} - -void dlmstp_fill_bacnet_address( - BACNET_ADDRESS * src, - uint8_t mstp_address) -{ - int i = 0; - - if (mstp_address == MSTP_BROADCAST_ADDRESS) { - /* mac_len = 0 if broadcast address */ - src->mac_len = 0; - src->mac[0] = 0; - } else { - src->mac_len = 1; - src->mac[0] = mstp_address; - } - /* fill with 0's starting with index 1; index 0 filled above */ - for (i = 1; i < MAX_MAC_LEN; i++) { - src->mac[i] = 0; - } - src->net = 0; - src->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - src->adr[i] = 0; - } -} - -/* for the MS/TP state machine to use for putting received data */ -uint16_t MSTP_Put_Receive( - volatile struct mstp_port_struct_t *mstp_port) -{ - uint16_t pdu_len = 0; - BOOL rc; - - if (!Receive_Packet.ready) { - /* bounds check - maybe this should send an abort? */ - pdu_len = mstp_port->DataLength; - if (pdu_len > sizeof(Receive_Packet.pdu)) - pdu_len = sizeof(Receive_Packet.pdu); - memmove((void *) &Receive_Packet.pdu[0], - (void *) &mstp_port->InputBuffer[0], pdu_len); - dlmstp_fill_bacnet_address(&Receive_Packet.address, - mstp_port->SourceAddress); - Receive_Packet.pdu_len = mstp_port->DataLength; - Receive_Packet.ready = true; - rc = ReleaseSemaphore(Receive_Packet_Flag, 1, NULL); - } - - return pdu_len; -} - -/* for the MS/TP state machine to use for getting data to send */ -/* Return: amount of PDU data */ -uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t * mstp_port, - unsigned timeout) -{ /* milliseconds to wait for a packet */ - uint16_t pdu_len = 0; - uint8_t destination = 0; /* destination address */ - - (void) timeout; - if (!Transmit_Packet.ready) { - return 0; - } - /* load destination MAC address */ - if (Transmit_Packet.address.mac_len == 1) { - destination = Transmit_Packet.address.mac[0]; - } else { - return 0; - } - if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { - return 0; - } - /* convert the PDU into the MSTP Frame */ - pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ - mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, - mstp_port->This_Station, &Transmit_Packet.pdu[0], - Transmit_Packet.pdu_len); - Transmit_Packet.ready = false; - - return pdu_len; -} - -bool dlmstp_compare_data_expecting_reply( - uint8_t * request_pdu, - uint16_t request_pdu_len, - uint8_t src_address, - uint8_t * reply_pdu, - uint16_t reply_pdu_len, - BACNET_ADDRESS * dest_address) -{ - uint16_t offset; - /* One way to check the message is to compare NPDU - src, dest, along with the APDU type, invoke id. - Seems a bit overkill */ - struct DER_compare_t { - BACNET_NPDU_DATA npdu_data; - BACNET_ADDRESS address; - uint8_t pdu_type; - uint8_t invoke_id; - uint8_t service_choice; - }; - struct DER_compare_t request; - struct DER_compare_t reply; - - /* unused parameters */ - request_pdu_len = request_pdu_len; - reply_pdu_len = reply_pdu_len; - /* decode the request data */ - request.address.mac[0] = src_address; - request.address.mac_len = 1; - offset = - npdu_decode(&request_pdu[0], NULL, &request.address, - &request.npdu_data); - if (request.npdu_data.network_layer_message) { - return false; - } - request.pdu_type = request_pdu[offset] & 0xF0; - if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { - return false; - } - request.invoke_id = request_pdu[offset + 2]; - /* segmented message? */ - if (request_pdu[offset] & BIT3) - request.service_choice = request_pdu[offset + 5]; - else - request.service_choice = request_pdu[offset + 3]; - /* decode the reply data */ - bacnet_address_copy(&reply.address, dest_address); - offset = - npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); - if (reply.npdu_data.network_layer_message) { - return false; - } - /* reply could be a lot of things: - confirmed, simple ack, abort, reject, error */ - reply.pdu_type = reply_pdu[offset] & 0xF0; - switch (reply.pdu_type) { - case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: - reply.invoke_id = reply_pdu[offset + 2]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset + 5]; - else - reply.service_choice = reply_pdu[offset + 3]; - break; - case PDU_TYPE_SIMPLE_ACK: - reply.invoke_id = reply_pdu[offset + 1]; - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_COMPLEX_ACK: - reply.invoke_id = reply_pdu[offset + 1]; - /* segmented message? */ - if (reply_pdu[offset] & BIT3) - reply.service_choice = reply_pdu[offset + 4]; - else - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_ERROR: - reply.invoke_id = reply_pdu[offset + 1]; - reply.service_choice = reply_pdu[offset + 2]; - break; - case PDU_TYPE_REJECT: - case PDU_TYPE_ABORT: - reply.invoke_id = reply_pdu[offset + 1]; - break; - default: - return false; - } - /* these don't have service choice included */ - if ((reply.pdu_type == PDU_TYPE_REJECT) || - (reply.pdu_type == PDU_TYPE_ABORT)) { - if (request.invoke_id != reply.invoke_id) { - return false; - } - } else { - if (request.invoke_id != reply.invoke_id) { - return false; - } - if (request.service_choice != reply.service_choice) { - return false; - } - } - if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { - return false; - } - if (request.npdu_data.priority != reply.npdu_data.priority) { - return false; - } - if (!bacnet_address_same(&request.address, &reply.address)) { - return false; - } - - return true; -} - -/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ -uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t * mstp_port, - unsigned timeout) -{ /* milliseconds to wait for a packet */ - uint16_t pdu_len = 0; /* return value */ - uint8_t destination = 0; /* destination address */ - bool matched = false; - - (void) timeout; - if (!Transmit_Packet.ready) { - return 0; - } - /* load destination MAC address */ - if (Transmit_Packet.address.mac_len == 1) { - destination = Transmit_Packet.address.mac[0]; - } else { - return 0; - } - if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { - return 0; - } - /* is this the reply to the DER? */ - matched = - dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], - mstp_port->DataLength, mstp_port->SourceAddress, - &Transmit_Packet.pdu[0], Transmit_Packet.pdu_len, - &Transmit_Packet.address); - if (!matched) - return 0; - /* convert the PDU into the MSTP Frame */ - pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ - mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, - mstp_port->This_Station, &Transmit_Packet.pdu[0], - Transmit_Packet.pdu_len); - Transmit_Packet.ready = false; - - return pdu_len; -} - -void dlmstp_set_mac_address( - uint8_t mac_address) -{ - /* Master Nodes can only have address 0-127 */ - if (mac_address <= 127) { - MSTP_Port.This_Station = mac_address; - /* FIXME: implement your data storage */ - /* I2C_Write_Byte( - EEPROM_DEVICE_ADDRESS, - mac_address, - EEPROM_MSTP_MAC_ADDR); */ - if (mac_address > MSTP_Port.Nmax_master) - dlmstp_set_max_master(mac_address); - } - - return; -} - -uint8_t dlmstp_mac_address( - void) -{ - return MSTP_Port.This_Station; -} - -/* This parameter represents the value of the Max_Info_Frames property of */ -/* the node's Device object. The value of Max_Info_Frames specifies the */ -/* maximum number of information frames the node may send before it must */ -/* pass the token. Max_Info_Frames may have different values on different */ -/* nodes. This may be used to allocate more or less of the available link */ -/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ -/* node, its value shall be 1. */ -void dlmstp_set_max_info_frames( - uint8_t max_info_frames) -{ - if (max_info_frames >= 1) { - MSTP_Port.Nmax_info_frames = max_info_frames; - /* FIXME: implement your data storage */ - /* I2C_Write_Byte( - EEPROM_DEVICE_ADDRESS, - (uint8_t)max_info_frames, - EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ - } - - return; -} - -uint8_t dlmstp_max_info_frames( - void) -{ - return MSTP_Port.Nmax_info_frames; -} - -/* This parameter represents the value of the Max_Master property of the */ -/* node's Device object. The value of Max_Master specifies the highest */ -/* allowable address for master nodes. The value of Max_Master shall be */ -/* less than or equal to 127. If Max_Master is not writable in a node, */ -/* its value shall be 127. */ -void dlmstp_set_max_master( - uint8_t max_master) -{ - if (max_master <= 127) { - if (MSTP_Port.This_Station <= max_master) { - MSTP_Port.Nmax_master = max_master; - /* FIXME: implement your data storage */ - /* I2C_Write_Byte( - EEPROM_DEVICE_ADDRESS, - max_master, - EEPROM_MSTP_MAX_MASTER_ADDR); */ - } - } - - return; -} - -uint8_t dlmstp_max_master( - void) -{ - return MSTP_Port.Nmax_master; -} - -/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ -void dlmstp_set_baud_rate( - uint32_t baud) -{ - RS485_Set_Baud_Rate(baud); -} - -uint32_t dlmstp_baud_rate( - void) -{ - return RS485_Get_Baud_Rate(); -} - -void dlmstp_get_my_address( - BACNET_ADDRESS * my_address) -{ - int i = 0; /* counter */ - - my_address->mac_len = 1; - my_address->mac[0] = MSTP_Port.This_Station; - my_address->net = 0; /* local only, no routing */ - my_address->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - my_address->adr[i] = 0; - } - - return; -} - -void dlmstp_get_broadcast_address( - BACNET_ADDRESS * dest) -{ /* destination address */ - int i = 0; /* counter */ - - if (dest) { - dest->mac_len = 1; - dest->mac[0] = MSTP_BROADCAST_ADDRESS; - dest->net = BACNET_BROADCAST_NETWORK; - dest->len = 0; /* always zero when DNET is broadcast */ - for (i = 0; i < MAX_MAC_LEN; i++) { - dest->adr[i] = 0; - } - } - - return; -} - -bool dlmstp_init( - char *ifname) -{ - unsigned long hThread = 0; - uint32_t arg_value = 0; - TIMECAPS tc; - - /* initialize packet queue */ - Receive_Packet.ready = false; - Receive_Packet.pdu_len = 0; - Receive_Packet_Flag = CreateSemaphore(NULL, 0, 1, "dlmstpReceivePacket"); - if (Receive_Packet_Flag == NULL) - exit(1); - Received_Frame_Flag = CreateSemaphore(NULL, 0, 1, "dlsmtpReceiveFrame"); - if (Received_Frame_Flag == NULL) { - CloseHandle(Receive_Packet_Flag); - exit(1); - } - /* initialize hardware */ - /* initialize hardware */ - if (ifname) { - RS485_Set_Interface(ifname); -#if PRINT_ENABLED - fprintf(stderr, "MS/TP Interface: %s\n", ifname); -#endif - } - RS485_Initialize(); - MSTP_Port.InputBuffer = &RxBuffer[0]; - MSTP_Port.InputBufferSize = sizeof(RxBuffer); - MSTP_Port.OutputBuffer = &TxBuffer[0]; - MSTP_Port.OutputBufferSize = sizeof(TxBuffer); - MSTP_Port.SilenceTimer = Timer_Silence; - MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; - MSTP_Init(&MSTP_Port); -#if 0 - uint8_t data; - - /* FIXME: implement your data storage */ - data = 64; /* I2C_Read_Byte( - EEPROM_DEVICE_ADDRESS, - EEPROM_MSTP_MAC_ADDR); */ - if (data <= 127) - MSTP_Port.This_Station = data; - else - dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); - /* FIXME: implement your data storage */ - data = 127; /* I2C_Read_Byte( - EEPROM_DEVICE_ADDRESS, - EEPROM_MSTP_MAX_MASTER_ADDR); */ - if ((data <= 127) && (data >= MSTP_Port.This_Station)) - MSTP_Port.Nmax_master = data; - else - dlmstp_set_max_master(DEFAULT_MAX_MASTER); - /* FIXME: implement your data storage */ - data = 1; - /* I2C_Read_Byte( - EEPROM_DEVICE_ADDRESS, - EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ - if (data >= 1) - MSTP_Port.Nmax_info_frames = data; - else - dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); -#endif -#if PRINT_ENABLED - fprintf(stderr, "MS/TP MAC: %02X\n", MSTP_Port.This_Station); - fprintf(stderr, "MS/TP Max_Master: %02X\n", MSTP_Port.Nmax_master); - fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", MSTP_Port.Nmax_info_frames); -#endif - /* set timer resolution */ - if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { - fprintf(stderr, "Failed to set timer resolution\n"); - } - TimeBeginPeriod = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); - timeBeginPeriod(TimeBeginPeriod); - - /* start the threads */ - hThread = _beginthread(dlmstp_receive_fsm_task, 4096, &arg_value); - if (hThread == 0) { - fprintf(stderr, "Failed to start recive FSM task\n"); - } - hThread = _beginthread(dlmstp_master_fsm_task, 4096, &arg_value); - if (hThread == 0) { - fprintf(stderr, "Failed to start Master Node FSM task\n"); - } - - return true; -} - -#ifdef TEST_DLMSTP -#include - -void apdu_handler( - BACNET_ADDRESS * src, /* source address */ - uint8_t * apdu, /* APDU data */ - uint16_t pdu_len) -{ /* for confirmed messages */ - (void) src; - (void) apdu; - (void) pdu_len; -} - -/* returns a delta timestamp */ -uint32_t timestamp_ms( - void) -{ - DWORD ticks = 0, delta_ticks = 0; - static DWORD last_ticks = 0; - - ticks = GetTickCount(); - delta_ticks = - (ticks >= last_ticks ? ticks - last_ticks : MAXDWORD - last_ticks); - last_ticks = ticks; - - return delta_ticks; -} - -static char *Network_Interface = "COM3"; - -int main( - int argc, - char *argv[]) -{ - uint16_t pdu_len = 0; - - /* argv has the "COM4" or some other device */ - if (argc > 1) { - Network_Interface = argv[1]; - } - dlmstp_set_baud_rate(38400); - dlmstp_set_mac_address(0x05); - dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); - dlmstp_set_max_master(DEFAULT_MAX_MASTER); - dlmstp_init(Network_Interface); - /* forever task */ - for (;;) { - pdu_len = dlmstp_receive(NULL, NULL, 0, INFINITE); -#if 0 - MSTP_Create_And_Send_Frame(&MSTP_Port, FRAME_TYPE_TEST_REQUEST, - MSTP_Port.SourceAddress, MSTP_Port.This_Station, NULL, 0); -#endif - } - - return 0; -} -#endif +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* Multimedia Timer contribution by Cameron Crothers, 2008 +* +* 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 +#include "bacdef.h" +#include "bacaddr.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "bits.h" + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 +#include +#include /* for multimedia timers */ + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* packet queues */ +static DLMSTP_PACKET Receive_Packet; +static HANDLE Receive_Packet_Flag; +/* mechanism to wait for a frame in state machine */ +HANDLE Received_Frame_Flag; +static DLMSTP_PACKET Transmit_Packet; +/* local MS/TP port data - shared with RS-485 */ +volatile struct mstp_port_struct_t MSTP_Port; +/* buffers needed by mstp port struct */ +static uint8_t TxBuffer[MAX_MPDU]; +static uint8_t RxBuffer[MAX_MPDU]; +/* Timer that indicates line silence - and functions */ +static uint32_t SilenceStartTime; +static uint32_t TimeBeginPeriod; +/* 1-millisecond target resolution */ +#define TARGET_RESOLUTION 1 + +static uint16_t Timer_Silence( + void) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + if (SilenceStartTime < now) { + delta_time = now - SilenceStartTime; + } else { + delta_time = (UINT32_MAX - SilenceStartTime) + now + 1; + } + if (delta_time > 0xFFFF) { + delta_time = 0xFFFF; + } + + return (uint16_t)delta_time; +} + +static void Timer_Silence_Reset( + void) +{ + SilenceStartTime = timeGetTime(); +} + +void dlmstp_reinit( + void) +{ + /*RS485_Reinit(); */ + dlmstp_set_mac_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* reset timer resolution */ + timeEndPeriod(TimeBeginPeriod); +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ + if (Received_Frame_Flag) { + CloseHandle(Received_Frame_Flag); + } + if (Receive_Packet_Flag) { + CloseHandle(Receive_Packet_Flag); + } +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_send_pdu( + BACNET_ADDRESS * dest, /* destination address */ + BACNET_NPDU_DATA * npdu_data, /* network information */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + unsigned i = 0; + + if (!Transmit_Packet.ready) { + if (npdu_data->data_expecting_reply) { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + Transmit_Packet.pdu_len = pdu_len; + for (i = 0; i < pdu_len; i++) { + Transmit_Packet.pdu[i] = pdu[i]; + } + bacnet_address_copy(&Transmit_Packet.address, dest); + bytes_sent = pdu_len + MAX_HEADER; + Transmit_Packet.ready = true; + } + + return bytes_sent; +} + +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + DWORD wait_status = 0; + + (void) max_pdu; + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + wait_status = WaitForSingleObject(Receive_Packet_Flag, timeout); + if (wait_status == WAIT_OBJECT_0) { + if (Receive_Packet.ready) { + if (Receive_Packet.pdu_len) { + MSTP_Packets++; + if (src) { + memmove(src, &Receive_Packet.address, + sizeof(Receive_Packet.address)); + } + if (pdu) { + memmove(pdu, &Receive_Packet.pdu, + sizeof(Receive_Packet.pdu)); + } + pdu_len = Receive_Packet.pdu_len; + } + Receive_Packet.ready = false; + } + } + + return pdu_len; +} + +static void dlmstp_receive_fsm_task( + void *pArg) +{ + bool received_frame; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + while (TRUE) { + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) { + ReleaseSemaphore(Received_Frame_Flag, 1, NULL); + break; + } + } while (MSTP_Port.DataAvailable); + } + } +} + +static void dlmstp_master_fsm_task( + void *pArg) +{ + DWORD dwMilliseconds = 0; + + (void) pArg; + (void) SetThreadPriority(GetCurrentThread(), + THREAD_PRIORITY_TIME_CRITICAL); + while (TRUE) { + switch (MSTP_Port.master_state) { + case MSTP_MASTER_STATE_IDLE: + dwMilliseconds = Tno_token; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + dwMilliseconds = Treply_timeout; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + dwMilliseconds = Tusage_timeout; + break; + default: + dwMilliseconds = 0; + break; + } + if (dwMilliseconds) + WaitForSingleObject(Received_Frame_Flag, dwMilliseconds); + MSTP_Master_Node_FSM(&MSTP_Port); + } +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) +{ + uint16_t pdu_len = 0; + BOOL rc; + + if (!Receive_Packet.ready) { + /* bounds check - maybe this should send an abort? */ + pdu_len = mstp_port->DataLength; + if (pdu_len > sizeof(Receive_Packet.pdu)) + pdu_len = sizeof(Receive_Packet.pdu); + memmove((void *) &Receive_Packet.pdu[0], + (void *) &mstp_port->InputBuffer[0], pdu_len); + dlmstp_fill_bacnet_address(&Receive_Packet.address, + mstp_port->SourceAddress); + Receive_Packet.pdu_len = mstp_port->DataLength; + Receive_Packet.ready = true; + rc = ReleaseSemaphore(Receive_Packet_Flag, 1, NULL); + } + + return pdu_len; +} + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; + uint8_t destination = 0; /* destination address */ + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t * request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t * reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS * dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* unused parameters */ + request_pdu_len = request_pdu_len; + reply_pdu_len = reply_pdu_len; + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = + npdu_decode(&request_pdu[0], NULL, &request.address, + &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset + 2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset + 5]; + else + request.service_choice = request_pdu[offset + 3]; + /* decode the reply data */ + bacnet_address_copy(&reply.address, dest_address); + offset = + npdu_decode(&reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset + 2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 5]; + else + reply.service_choice = reply_pdu[offset + 3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset + 1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset + 4]; + else + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset + 1]; + reply.service_choice = reply_pdu[offset + 2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset + 1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t * mstp_port, + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + (void) timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* is this the reply to the DER? */ + matched = + dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0], + mstp_port->DataLength, mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (!matched) + return 0; + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, Transmit_Packet.frame_type, destination, + mstp_port->This_Station, &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return MSTP_Port.Nmax_master; +} + +/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */ +void dlmstp_set_baud_rate( + uint32_t baud) +{ + RS485_Set_Baud_Rate(baud); +} + +uint32_t dlmstp_baud_rate( + void) +{ + return RS485_Get_Baud_Rate(); +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +bool dlmstp_init( + char *ifname) +{ + unsigned long hThread = 0; + uint32_t arg_value = 0; + TIMECAPS tc; + + /* initialize packet queue */ + Receive_Packet.ready = false; + Receive_Packet.pdu_len = 0; + Receive_Packet_Flag = CreateSemaphore(NULL, 0, 1, "dlmstpReceivePacket"); + if (Receive_Packet_Flag == NULL) + exit(1); + Received_Frame_Flag = CreateSemaphore(NULL, 0, 1, "dlsmtpReceiveFrame"); + if (Received_Frame_Flag == NULL) { + CloseHandle(Receive_Packet_Flag); + exit(1); + } + /* initialize hardware */ + /* initialize hardware */ + if (ifname) { + RS485_Set_Interface(ifname); +#if PRINT_ENABLED + fprintf(stderr, "MS/TP Interface: %s\n", ifname); +#endif + } + RS485_Initialize(); + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + MSTP_Init(&MSTP_Port); +#if 0 + uint8_t data; + + /* FIXME: implement your data storage */ + data = 64; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAC_ADDR); */ + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + /* FIXME: implement your data storage */ + data = 127; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + if ((data <= 127) && (data >= MSTP_Port.This_Station)) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* FIXME: implement your data storage */ + data = 1; + /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + if (data >= 1) + MSTP_Port.Nmax_info_frames = data; + else + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); +#endif +#if PRINT_ENABLED + fprintf(stderr, "MS/TP MAC: %02X\n", MSTP_Port.This_Station); + fprintf(stderr, "MS/TP Max_Master: %02X\n", MSTP_Port.Nmax_master); + fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", MSTP_Port.Nmax_info_frames); +#endif + /* set timer resolution */ + if (timeGetDevCaps(&tc, sizeof(TIMECAPS)) != TIMERR_NOERROR) { + fprintf(stderr, "Failed to set timer resolution\n"); + } + TimeBeginPeriod = min(max(tc.wPeriodMin, TARGET_RESOLUTION), tc.wPeriodMax); + timeBeginPeriod(TimeBeginPeriod); + + /* start the threads */ + hThread = _beginthread(dlmstp_receive_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start recive FSM task\n"); + } + hThread = _beginthread(dlmstp_master_fsm_task, 4096, &arg_value); + if (hThread == 0) { + fprintf(stderr, "Failed to start Master Node FSM task\n"); + } + + return true; +} + +#ifdef TEST_DLMSTP +#include + +void apdu_handler( + BACNET_ADDRESS * src, /* source address */ + uint8_t * apdu, /* APDU data */ + uint16_t pdu_len) +{ /* for confirmed messages */ + (void) src; + (void) apdu; + (void) pdu_len; +} + +/* returns a delta timestamp */ +uint32_t timestamp_ms( + void) +{ + DWORD ticks = 0, delta_ticks = 0; + static DWORD last_ticks = 0; + + ticks = GetTickCount(); + delta_ticks = + (ticks >= last_ticks ? ticks - last_ticks : MAXDWORD - last_ticks); + last_ticks = ticks; + + return delta_ticks; +} + +static char *Network_Interface = "COM3"; + +int main( + int argc, + char *argv[]) +{ + uint16_t pdu_len = 0; + + /* argv has the "COM4" or some other device */ + if (argc > 1) { + Network_Interface = argv[1]; + } + dlmstp_set_baud_rate(38400); + dlmstp_set_mac_address(0x05); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + dlmstp_init(Network_Interface); + /* forever task */ + for (;;) { + pdu_len = dlmstp_receive(NULL, NULL, 0, INFINITE); +#if 0 + MSTP_Create_And_Send_Frame(&MSTP_Port, FRAME_TYPE_TEST_REQUEST, + MSTP_Port.SourceAddress, MSTP_Port.This_Station, NULL, 0); +#endif + } + + return 0; +} +#endif