/* packet-bacapp.h
 * Routines for BACnet (APDU) dissection
 * Copyright 2004, Herbert Lischka <lischka@kieback-peter.de>, Berlin
 *
 * $Id: packet-bacapp.h,v 1.00 2004/03/11 17:50:07 Lka Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * Copyright 1998 Gerald Combs
 *
 * Copied from README.developer,v 1.23
 *
 * 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.
 */

#ifndef __BACAPP_H__
#define __BACAPP_H__

#ifdef HAVE_CONFIG_H
# include "config.h"
#if HAVE_ICONV_H
#include <iconv.h>
#endif
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <epan/packet.h>

#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif

#ifndef max
#define max(a,b) (((a)>(b))?(a):(b))
#endif

#ifndef FAULT
#define FAULT 			proto_tree_add_text(subtree, tvb, offset, tvb_length(tvb) - offset, "something is going wrong here !!"); \
			offset = tvb_length(tvb);
#endif

#ifndef false
#define false 0
#endif
#ifndef true
#define true 1
#endif

#ifndef LABEL
#define LABEL(lbl) (lbl==NULL ? (guint8 *) "Value: " : lbl)
#endif


/* BACnet PDU Types */
#define BACAPP_TYPE_CONFIRMED_SERVICE_REQUEST 0
#define BACAPP_TYPE_UNCONFIRMED_SERVICE_REQUEST 1
#define BACAPP_TYPE_SIMPLE_ACK 2
#define BACAPP_TYPE_COMPLEX_ACK 3
#define BACAPP_TYPE_SEGMENT_ACK 4
#define BACAPP_TYPE_ERROR 5
#define BACAPP_TYPE_REJECT 6
#define BACAPP_TYPE_ABORT 7
#define MAX_BACAPP_TYPE 8

#define BACAPP_SEGMENTED_REQUEST 0x08
#define BACAPP_MORE_SEGMENTS 0x04
#define BACAPP_SEGMENTED_RESPONSE 0x02
#define BACAPP_SEGMENT_NAK 0x02
#define BACAPP_SENT_BY 0x01


/**
 * dissect_bacapp ::= CHOICE {
 *  confirmed-request-PDU       [0] BACnet-Confirmed-Request-PDU,
 *  unconfirmed-request-PDU     [1] BACnet-Unconfirmed-Request-PDU,
 *  simpleACK-PDU               [2] BACnet-SimpleACK-PDU,
 *  complexACK-PDU              [3] BACnet-ComplexACK-PDU,
 *  segmentACK-PDU              [4] BACnet-SegmentACK-PDU,
 *  error-PDU                   [5] BACnet-Error-PDU,
 *  reject-PDU                  [6] BACnet-Reject-PDU,
 *  abort-PDU                   [7] BACnet-Abort-PDU
 * }
 * @param tvb
 * @param pinfo
 * @param tree
 */
void
dissect_bacapp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);

/**
 * ConfirmedRequest-PDU ::= SEQUENCE {
 * 	pdu-type					[0] Unsigned (0..15), -- 0 for this PDU Type
 *  segmentedMessage			[1] BOOLEAN,
 *  moreFollows					[2] BOOLEAN,
 *  segmented-response-accepted	[3] BOOLEAN,
 *  reserved					[4] Unsigned (0..3), -- must be set zero
 *  max-segments-accepted		[5] Unsigned (0..7), -- as per 20.1.2.4
 *  max-APDU-length-accepted	[5] Unsigned (0..15), -- as per 20.1.2.5
 *  invokeID					[6] Unsigned (0..255),
 *  sequence-number				[7] Unsigned (0..255) OPTIONAL, -- only if segmented msg
 *  proposed-window-size		[8] Unsigned (0..127) OPTIONAL, -- only if segmented msg
 *  service-choice				[9] BACnetConfirmedServiceChoice,
 *  service-request				[10] BACnet-Confirmed-Service-Request OPTIONAL
 * }
 * @param tvb
 * @param pinfo
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fConfirmedRequestPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Unconfirmed-Request-PDU ::= SEQUENCE {
 * 	pdu-type		[0] Unsigned (0..15), -- 1 for this PDU type
 *  reserved		[1] Unsigned (0..15), -- must be set zero
 *  service-choice	[2] BACnetUnconfirmedServiceChoice,
 *  service-request	[3] BACnetUnconfirmedServiceRequest -- Context-specific tags 0..3 are NOT used in header encoding
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fUnconfirmedRequestPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * SimpleACK-PDU ::= SEQUENCE {
 * 	pdu-type		[0] Unsigned (0..15), -- 2 for this PDU type
 *  reserved		[1] Unsigned (0..15), -- must be set zero
 *  invokeID		[2] Unsigned (0..255),
 *  service-ACK-choice	[3] BACnetUnconfirmedServiceChoice -- Context-specific tags 0..3 are NOT used in header encoding
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fSimpleAckPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ComplexACK-PDU ::= SEQUENCE {
 * 	pdu-type				[0] Unsigned (0..15), -- 3 for this PDU Type
 *  segmentedMessage		[1] BOOLEAN,
 *  moreFollows				[2] BOOLEAN,
 *  reserved				[3] Unsigned (0..3), -- must be set zero
 *  invokeID				[4] Unsigned (0..255),
 *  sequence-number			[5] Unsigned (0..255) OPTIONAL, -- only if segmented msg
 *  proposed-window-size	[6] Unsigned (0..127) OPTIONAL, -- only if segmented msg
 *  service-ACK-choice 		[7] BACnetConfirmedServiceChoice,
 *  service-ACK				[8] BACnet-Confirmed-Service-Request  -- Context-specific tags 0..8 are NOT used in header encoding
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fComplexAckPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * SegmentACK-PDU ::= SEQUENCE {
 * 	pdu-type				[0] Unsigned (0..15), -- 4 for this PDU Type
 *  reserved				[1] Unsigned (0..3), -- must be set zero
 *  negative-ACK			[2] BOOLEAN,
 *  server					[3] BOOLEAN,
 *  original-invokeID		[4] Unsigned (0..255),
 *  sequence-number			[5] Unsigned (0..255),
 *  actual-window-size		[6] Unsigned (0..127)
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fSegmentAckPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Error-PDU ::= SEQUENCE {
 * 	pdu-type				[0] Unsigned (0..15), -- 5 for this PDU Type
 *  reserved				[1] Unsigned (0..3), -- must be set zero
 *  original-invokeID		[2] Unsigned (0..255),
 *  error-choice			[3] BACnetConfirmedServiceChoice,
 *  error					[4] BACnet-Error
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fErrorPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Reject-PDU ::= SEQUENCE {
 * 	pdu-type				[0] Unsigned (0..15), -- 6 for this PDU Type
 *  reserved				[1] Unsigned (0..3), -- must be set zero
 *  original-invokeID		[2] Unsigned (0..255),
 *  reject-reason			[3] BACnetRejectReason
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fRejectPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Abort-PDU ::= SEQUENCE {
 * 	pdu-type				[0] Unsigned (0..15), -- 7 for this PDU Type
 *  reserved				[1] Unsigned (0..3), -- must be set zero
 *  server					[2] BOOLEAN,
 *  original-invokeID		[3] Unsigned (0..255),
 *  abort-reason			[4] BACnetAbortReason
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fAbortPDU(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * 20.2.4, adds the label with max 64Bit unsigned Integer Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fUnsignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * 20.2.5, adds the label with max 64Bit signed Integer Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fSignedTag (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * 20.2.8, adds the label with Octet String to tree; if lvt == 0 then lvt = restOfFrame
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @param lvt length of String
 * @return modified offset
 */
static guint
fOctetString (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label, guint32 lvt);

/**
 * 20.2.12, adds the label with Date Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fDate    (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * 20.2.13, adds the label with Time Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fTime (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * 20.2.14, adds Object Identifier to tree
 * use BIG ENDIAN: Bits 31..22 Object Type, Bits 21..0 Instance Number
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fObjectIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet-Confirmed-Service-Request ::= CHOICE {
 * }
 * @param tvb
 * @param tree
 * @param offset
 * @param service_choice
 * @return offset
 */
static guint
fConfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice);

/**
 * BACnet-Confirmed-Service-ACK ::= CHOICE {
 * }
 * @param tvb
 * @param tree
 * @param offset
 * @param service_choice
 * @return offset
 */
static guint
fConfirmedServiceAck (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice);

/**
 * AcknowledgeAlarm-Request ::= SEQUENCE {
 * 	acknowledgingProcessIdentifier	[0]	Unsigned32,
 * 	eventObjectIdentifier	[1] BACnetObjectIdentifer,
 * 	eventStateAcknowledge	[2] BACnetEventState,
 * 	timeStamp	[3] BACnetTimeStamp,
 * 	acknowledgementSource	[4] Character String,
 *  timeOfAcknowledgement	[5] BACnetTimeStamp
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fAcknowlegdeAlarmRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedCOVNotification-Request ::= SEQUENCE {
 * 	subscriberProcessIdentifier	[0]	Unsigned32,
 * 	initiatingDeviceIdentifier	[1] BACnetObjectIdentifer,
 * 	monitoredObjectIdentifier	[2] BACnetObjectIdentifer,
 * 	timeRemaining	[3] unsigned,
 * 	listOfValues	[4] SEQUENCE OF BACnetPropertyValues
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fConfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedEventNotification-Request ::= SEQUENCE {
 * 	ProcessIdentifier	[0]	Unsigned32,
 * 	initiatingDeviceIdentifier	[1] BACnetObjectIdentifer,
 * 	eventObjectIdentifier	[2] BACnetObjectIdentifer,
 * 	timeStamp	[3] BACnetTimeStamp,
 * 	notificationClass	[4] unsigned,
 * 	priority	[5] unsigned8,
 * 	eventType	[6] BACnetEventType,
 * 	messageText	[7] CharacterString OPTIONAL,
 * 	notifyType	[8] BACnetNotifyType,
 * 	ackRequired	[9] BOOLEAN OPTIONAL,
 * 	fromState	[10] BACnetEventState OPTIONAL,
 * 	toState	[11] BACnetEventState,
 * 	eventValues	[12] BACnetNotificationParameters OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fConfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * GetAlarmSummary-ACK ::= SEQUENCE OF SEQUENCE {
 * 	objectIdentifier	BACnetObjectIdentifer,
 * 	alarmState	BACnetEventState,
 * 	acknowledgedTransitions	 BACnetEventTransitionBits
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fGetAlarmSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * GetEnrollmentSummary-Request ::= SEQUENCE {
 * 	acknowledgmentFilter	[0]	ENUMERATED {
 *      all (0),
 *      acked   (1),
 *      not-acked   (2)
 *      },
 * 	enrollmentFilter	[1] BACnetRecipientProcess OPTIONAL,
 * 	eventStateFilter	[2] ENUMERATED {
 *      offnormal   (0),
 *      fault   (1),
 *      normal  (2),
 *      all (3),
 *      active  (4)
 *      },
 * 	eventTypeFilter	[3] BACnetEventType OPTIONAL,
 * 	priorityFilter	[4] SEQUENCE {
 *      minPriority [0] Unsigned8,
 *      maxPriority [1] Unsigned8
 *      } OPTIONAL,
 *  notificationClassFilter	[5] Unsigned OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fGetEnrollmentSummaryRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * GetEnrollmentSummary-ACK ::= SEQUENCE OF SEQUENCE {
 * 	objectIdentifier	BACnetObjectIdentifer,
 * 	eventType	BACnetEventType,
 * 	eventState	BACnetEventState,
 * 	priority    Unsigned8,
 *  notificationClass   Unsigned OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fGetEnrollmentSummaryAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * GetEventInformation-Request ::= SEQUENCE {
 * 	lastReceivedObjectIdentifier	[0] BACnetObjectIdentifer
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fGetEventInformationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * GetEventInformation-ACK ::= SEQUENCE {
 * 	listOfEventSummaries	[0] listOfEventSummaries,
 *  moreEvents  [1] BOOLEAN
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fGetEventInformationACK (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * LifeSafetyOperation-Request ::= SEQUENCE {
 * 	requestingProcessIdentifier	[0]	Unsigned32
 * 	requestingSource	[1] CharacterString
 * 	request	[2] BACnetLifeSafetyOperation
 * 	objectIdentifier	[3] BACnetObjectIdentifier OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fLifeSafetyOperationRequest(tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * SubscribeCOV-Request ::= SEQUENCE {
 * 	subscriberProcessIdentifier	[0]	Unsigned32
 * 	monitoredObjectIdentifier	[1] BACnetObjectIdentifier
 * 	issueConfirmedNotifications	[2] BOOLEAN OPTIONAL
 * 	lifetime	[3] Unsigned OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @param src
 * @return modified offset
 */
static guint
fSubscribeCOVRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * SubscribeCOVProperty-Request ::= SEQUENCE {
 * 	subscriberProcessIdentifier	[0]	Unsigned32
 * 	monitoredObjectIdentifier	[1] BACnetObjectIdentifier
 * 	issueConfirmedNotifications	[2] BOOLEAN OPTIONAL
 * 	lifetime	[3] Unsigned OPTIONAL
 * 	monitoredPropertyIdentifier	[4] BACnetPropertyReference OPTIONAL
 * 	covIncrement	[5] Unsigned OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fSubscribeCOVPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * AtomicReadFile-Request ::= SEQUENCE {
 * 	fileIdentifier	BACnetObjectIdentifier,
 *  accessMethod	CHOICE {
 *  	streamAccess	[0] SEQUENCE {
 *  		fileStartPosition	INTEGER,
 * 			requestedOctetCount	Unsigned
 * 			},
 * 		recordAccess	[1] SEQUENCE {
 * 			fileStartRecord	INTEGER,
 * 			requestedRecordCount	Unsigned
 * 			}
 * 		}
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAtomicReadFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * AtomicWriteFile-ACK ::= SEQUENCE {
 * 	endOfFile	BOOLEAN,
 *  accessMethod	CHOICE {
 *  	streamAccess	[0] SEQUENCE {
 *  		fileStartPosition	INTEGER,
 * 			fileData	OCTET STRING
 * 			},
 * 		recordAccess	[1] SEQUENCE {
 * 			fileStartRecord	INTEGER,
 * 			returnedRecordCount	Unsigned,
 * 			fileRecordData	SEQUENCE OF OCTET STRING
 * 			}
 * 		}
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAtomicReadFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * AtomicWriteFile-Request ::= SEQUENCE {
 * 	fileIdentifier	BACnetObjectIdentifier,
 *  accessMethod	CHOICE {
 *  	streamAccess	[0] SEQUENCE {
 *  		fileStartPosition	INTEGER,
 * 			fileData	OCTET STRING
 * 			},
 * 		recordAccess	[1] SEQUENCE {
 * 			fileStartRecord	INTEGER,
 * 			recordCount	Unsigned,
 * 			fileRecordData	SEQUENCE OF OCTET STRING
 * 			}
 * 		}
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAtomicWriteFileRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * AtomicWriteFile-ACK ::= SEQUENCE {
 * 		fileStartPosition	[0] INTEGER,
 * 	   	fileStartRecord	[1] INTEGER,
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAtomicWriteFileAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * AddListElement-Request ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 *  propertyIdentifier  [1] BACnetPropertyIdentifier,
 *  propertyArrayIndex  [2] Unsigned OPTIONAL, -- used only with array datatype
 *  listOfElements  [3] ABSTRACT-SYNTAX.&Type
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fAddListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * CreateObject-Request ::= SEQUENCE {
 * 	objectSpecifier	[0] ObjectSpecifier,
 *  listOfInitialValues	[1] SEQUENCE OF BACnetPropertyValue OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fCreateObjectRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset);

/**
 * CreateObject-Request ::= BACnetObjectIdentifier
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fCreateObjectAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * DeleteObject-Request ::= SEQUENCE {
 * 	ObjectIdentifier	BACnetObjectIdentifer
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fDeleteObjectRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadProperty-Request ::= SEQUENCE {
 * 	objectIdentifier	[0]	BACnetObjectIdentifier,
 * 	propertyIdentifier	[1] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadPropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadProperty-ACK ::= SEQUENCE {
 * 	objectIdentifier	[0]	BACnetObjectIdentifier,
 * 	propertyIdentifier	[1] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 * 	propertyValue	[3] ABSTRACT-SYNTAX.&Type
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadPropertyAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadPropertyConditional-Request ::= SEQUENCE {
 * 	objectSelectionCriteria	[0] objectSelectionCriteria,
 * 	listOfPropertyReferences	[1] SEQUENCE OF BACnetPropertyReference OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadPropertyConditionalRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset);

/**
 * ReadPropertyConditional-ACK ::= SEQUENCE {
 * 	listOfPReadAccessResults	SEQUENCE OF ReadAccessResult OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadPropertyConditionalAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadPropertyMultiple-Request ::= SEQUENCE {
 *  listOfReadAccessSpecs	SEQUENCE OF ReadAccessSpecification
 * }
 * @param tvb
 * @param tree
 * @param offset
 * @return offset modified
 */
static guint
fReadPropertyMultipleRequest(tvbuff_t *tvb, proto_tree *subtree, guint offset);

/**
 * ReadPropertyMultiple-Ack ::= SEQUENCE {
 *  listOfReadAccessResults	SEQUENCE OF ReadAccessResult
 * }
 * @param tvb
 * @param tree
 * @param offset
 * @return offset modified
 */
static guint
fReadPropertyMultipleAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadRange-Request ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 *  propertyIdentifier	[1] BACnetPropertyIdentifier,
 *  propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 *  	range	CHOICE {
 * 		byPosition	[3] SEQUENCE {
 * 			referencedIndex Unsigned,
 * 			count INTEGER
 * 			},
 * 		byTime	[4] SEQUENCE {
 * 			referenceTime BACnetDateTime,
 * 			count INTEGER
 * 			},
 * 		timeRange	[5] SEQUENCE {
 * 			beginningTime BACnetDateTime,
 * 			endingTime BACnetDateTime
 * 			},
 * 		} OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadRangeRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadRange-ACK ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 *  propertyIdentifier	[1] BACnetPropertyIdentifier,
 *  propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 *  resultFlags	[3] BACnetResultFlags,
 *  itemCount	[4] Unsigned,
 *  itemData	[5] SEQUENCE OF ABSTRACT-SYNTAX.&Type
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadRangeAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * RemoveListElement-Request ::= SEQUENCE {
 * 	objectIdentifier	[0]	BACnetObjectIdentifier,
 * 	propertyIdentifier	[1] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 * 	listOfElements	[3] ABSTRACT-SYNTAX.&Type
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fRemoveListElementRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * WriteProperty-Request ::= SEQUENCE {
 * 	objectIdentifier	[0]	BACnetObjectIdentifier,
 * 	propertyIdentifier	[1] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 * 	propertyValue	[3] ABSTRACT-SYNTAX.&Type
 *  priority	[4] Unsigned8 (1..16) OPTIONAL --used only when property is commandable
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWritePropertyRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * WritePropertyMultiple-Request ::= SEQUENCE {
 * 	listOfWriteAccessSpecifications	SEQUENCE OF WriteAccessSpecification
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWritePropertyMultipleRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * DeviceCommunicationControl-Request ::= SEQUENCE {
 * 	timeDuration	[0] Unsigned16 OPTIONAL,
 *  enable-disable	[1] ENUMERATED {
 * 		enable (0),
 * 		disable (1)
 * 		},
 *  password	[2] CharacterString (SIZE(1..20)) OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fDeviceCommunicationControlRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedPrivateTransfer-Request ::= SEQUENCE {
 * 	vendorID	[0]	Unsigned,
 * 	serviceNumber	[1] Unsigned,
 * 	serviceParameters	[2] ABSTRACT-SYNTAX.&Type OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fConfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedPrivateTransfer-ACK ::= SEQUENCE {
 * 	vendorID	[0]	Unsigned,
 * 	serviceNumber	[1] Unsigned,
 * 	resultBlock	[2] ABSTRACT-SYNTAX.&Type OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fConfirmedPrivateTransferAck(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedTextMessage-Request ::=  SEQUENCE {
 *  textMessageSourceDevice [0] BACnetObjectIdentifier,
 *  messageClass [1] CHOICE {
 *      numeric [0] Unsigned,
 *      character [1] CharacterString
 *      } OPTIONAL,
 *  messagePriority [2] ENUMERATED {
 *      normal (0),
 *      urgent (1)
 *      },
 *  message [3] CharacterString
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fConfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReinitializeDevice-Request ::= SEQUENCE {
 *  reinitializedStateOfDevice	[0] ENUMERATED {
 * 		coldstart (0),
 * 		warmstart (1),
 * 		startbackup (2),
 * 		endbackup (3),
 * 		startrestore (4),
 * 		endrestore (5),
 * 		abortrestor (6)
 * 		},
 *  password	[1] CharacterString (SIZE(1..20)) OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fReinitializeDeviceRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTOpen-Request ::= SEQUENCE {
 *  vtClass	BACnetVTClass,
 *  localVTSessionIdentifier	Unsigned8
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fVtOpenRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTOpen-ACK ::= SEQUENCE {
 *  remoteVTSessionIdentifier	Unsigned8
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fVtOpenAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTClose-Request ::= SEQUENCE {
 *  listOfRemoteVTSessionIdentifiers	SEQUENCE OF Unsigned8
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fVtCloseRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTData-Request ::= SEQUENCE {
 *  vtSessionIdentifier	Unsigned8,
 *  vtNewData	OCTET STRING,
 *  vtDataFlag	Unsigned (0..1)
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fVtDataRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTData-ACK ::= SEQUENCE {
 *  allNewDataAccepted	[0] BOOLEAN,
 *  acceptedOctetCount	[1] Unsigned OPTIONAL -- present only if allNewDataAccepted = FALSE
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fVtDataAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Authenticate-Request ::= SEQUENCE {
 *  pseudoRandomNumber	[0] Unsigned32,
 *  excpectedInvokeID	[1] Unsigned8 OPTIONAL,
 *  operatorName	[2] CharacterString OPTIONAL,
 *  operatorPassword	[3] CharacterString (SIZE(1..20)) OPTIONAL,
 *  startEncypheredSession	[4] BOOLEAN OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fAuthenticateRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Authenticate-ACK ::= SEQUENCE {
 *  modifiedRandomNumber	Unsigned32,
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fAuthenticateAck (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * RequestKey-Request ::= SEQUENCE {
 *  requestingDeviceIdentifier	BACnetObjectIdentifier,
 *  requestingDeviceAddress	BACnetAddress,
 *  remoteDeviceIdentifier	BACnetObjectIdentifier,
 *  remoteDeviceAddress	BACnetAddress
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fRequestKeyRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Unconfirmed-Service-Request ::= CHOICE {
 * }
 * @param tvb 
 * @param tree 
 * @param offset
 * @param service_choice
 * @return modified offset
 */
static guint
fUnconfirmedServiceRequest (tvbuff_t *tvb, proto_tree *tree, guint offset, gint service_choice);

/**
 * UnconfirmedCOVNotification-Request ::= SEQUENCE {
 * 	subscriberProcessIdentifier	[0]	Unsigned32,
 * 	initiatingDeviceIdentifier	[1] BACnetObjectIdentifer,
 * 	monitoredObjectIdentifier	[2] BACnetObjectIdentifer,
 * 	timeRemaining	[3] unsigned,
 * 	listOfValues	[4] SEQUENCE OF BACnetPropertyValues
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fUnconfirmedCOVNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * UnconfirmedEventNotification-Request ::= SEQUENCE {
 * 	ProcessIdentifier	[0]	Unsigned32,
 * 	initiatingDeviceIdentifier	[1] BACnetObjectIdentifer,
 * 	eventObjectIdentifier	[2] BACnetObjectIdentifer,
 * 	timeStamp	[3] BACnetTimeStamp,
 * 	notificationClass	[4] unsigned,
 * 	priority	[5] unsigned8,
 * 	eventType	[6] BACnetEventType,
 * 	messageText	[7] CharacterString OPTIONAL,
 * 	notifyType	[8] BACnetNotifyType,
 * 	ackRequired	[9] BOOLEAN OPTIONAL,
 * 	fromState	[10] BACnetEventState OPTIONAL,
 * 	toState	[11] BACnetEventState,
 * 	eventValues	[12] BACnetNotificationParameters OPTIONAL
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fUnconfirmedEventNotificationRequest (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * I-Am-Request ::= SEQUENCE {
 * 	aAmDeviceIdentifier	BACnetObjectIdentifier,
 *  maxAPDULengthAccepted	Unsigned,
 * 	segmentationSupported	BACnetSegmentation,
 * 	vendorID	Unsigned
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fIAmRequest  (tvbuff_t *tvb, proto_tree *tree, guint offset);


/**
 * I-Have-Request ::= SEQUENCE {
 * 	deviceIdentifier	BACnetObjectIdentifier,
 *  objectIdentifier	BACnetObjectIdentifier,
 * 	objectName	CharacterString
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fIHaveRequest  (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * UnconfirmedPrivateTransfer-Request ::= SEQUENCE {
 * 	vendorID	[0]	Unsigned,
 * 	serviceNumber	[1] Unsigned,
 * 	serviceParameters	[2] ABSTRACT-SYNTAX.&Type OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fUnconfirmedPrivateTransferRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * UnconfirmedTextMessage-Request ::=  SEQUENCE {
 *  textMessageSourceDevice [0] BACnetObjectIdentifier,
 *  messageClass [1] CHOICE {
 *      numeric [0] Unsigned,
 *      character [1] CharacterString
 *      } OPTIONAL,
 *  messagePriority [2] ENUMERATED {
 *      normal (0),
 *      urgent (1)
 *      },
 *  message [3] CharacterString
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fUnconfirmedTextMessageRequest(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * TimeSynchronization-Request ::=  SEQUENCE {
 *  BACnetDateTime
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fTimeSynchronizationRequest  (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * UTCTimeSynchronization-Request ::=  SEQUENCE {
 *  BACnetDateTime
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fUTCTimeSynchronizationRequest  (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Who-Has-Request ::=  SEQUENCE {
 *  limits SEQUENCE {
 *      deviceInstanceRangeLowLimit [0] Unsigned (0..4194303),
 *      deviceInstanceRangeHighLimit [1] Unsigned (0..4194303)
 *      } OPTIONAL,
 *  object CHOICE {
 *      objectIdentifier [2] BACnetObjectIdentifier,
 *      objectName [3] CharacterString
 *      }
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
fWhoHas (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * Who-Is-Request ::= SEQUENCE {
 * 	deviceInstanceRangeLowLimit	[0] Unsigned (0..4194303) OPTIONAL, -- must be used as a pair, see 16.9,
 * 	deviceInstanceRangeHighLimit	[0] Unsigned (0..4194303) OPTIONAL, -- must be used as a pair, see 16.9,
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWhoIsRequest  (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet-Error ::= CHOICE {
 *  addListElement          [8] ChangeList-Error,
 *  removeListElement       [9] ChangeList-Error,
 *  writePropertyMultiple   [16] WritePropertyMultiple-Error,
 *  confirmedPrivatTransfer [18] ConfirmedPrivateTransfer-Error,
 *  vtClose                 [22] VTClose-Error,
 *  readRange               [26] ObjectAccessService-Error
 *                      [default] Error
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @param service
 * @return modified offset
 */
static guint
fBACnetError(tvbuff_t *tvb, proto_tree *tree, guint offset, guint service);

/**
 * ChangeList-Error ::= SEQUENCE {
 *    errorType     [0] Error,
 *    firstFailedElementNumber  [1] Unsigned
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fChangeListError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * CreateObject-Error ::= SEQUENCE {
 *    errorType     [0] Error,
 *    firstFailedElementNumber  [1] Unsigned
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fCreateObjectError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ConfirmedPrivateTransfer-Error ::= SEQUENCE {
 *    errorType     [0] Error,
 *    vendorID      [1] Unsigned,
 *    serviceNumber [2] Unsigned,
 *    errorParameters   [3] ABSTRACT-SYNTAX.&Type OPTIONAL
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fConfirmedPrivateTransferError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * WritePropertyMultiple-Error ::= SEQUENCE {
 *    errorType     [0] Error,
 *    firstFailedWriteAttempt  [1] Unsigned
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fWritePropertyMultipleError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * VTClose-Error ::= SEQUENCE {
 *    errorType     [0] Error,
 *    listOfVTSessionIdentifiers  [1] SEQUENCE OF Unsigned8 OPTIONAL
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fVTCloseError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet Application Types chapter 20.2.1
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fApplicationTypes   (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * BACnetActionCommand ::= SEQUENCE {
 *  deviceIdentifier    [0] BACnetObjectIdentifier OPTIONAL,
 *  objectIdentifier    [1] BACnetObjectIdentifier,
 *  propertyIdentifier  [2] BACnetPropertyIdentifier,
 *  propertyArrayIndex  [3] Unsigned OPTIONAL, -- used only with array datatype
 *  propertyValue       [4] ABSTRACT-SYNTAX.&Type,
 *  priority            [5] Unsigned (1..16) OPTIONAL, -- used only when property is commandable
 *  postDelay           [6] Unsigned OPTIONAL,
 *  quitOnFailure       [7] BOOLEAN,
 *  writeSuccessful     [8] BOOLEAN
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fActionCommand (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetActionList ::= SEQUENCE {
 *  action  [0] SEQUENCE of BACnetActionCommand
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fActionList (tvbuff_t *tvb, proto_tree *tree, guint offset);

/** BACnetAddress ::= SEQUENCE {
 *  network-number  Unsigned16, -- A value 0 indicates the local network
 *  mac-address     OCTET STRING -- A string of length 0 indicates a broadcast
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAddress (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetAddressBinding ::= SEQUENCE {
 * 	deviceObjectID  BACnetObjectIdentifier
 * 	deviceAddress   BacnetAddress
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fAddressBinding (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetCalendaryEntry ::= CHOICE {
 * 	date        [0] Date,
 * 	dateRange   [1] BACnetDateRange,
 *  weekNDay    [2] BacnetWeekNday
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fCalendaryEntry (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetClientCOV ::= CHOICE {
 * 	real-increment  REAL,
 * 	default-increment   NULL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fClientCOV (tvbuff_t *tvb, proto_tree *tree, guint offset);

static guint
fDailySchedule (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetWeeklySchedule ::= SEQUENCE {
 *  week-schedule    SENQUENCE SIZE (7) OF BACnetDailySchedule
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWeeklySchedule (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetDateRange ::= SEQUENCE {
 *  StartDate   Date,
 *  EndDate     Date
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fDateRange (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetDateTime ::= SEQUENCE {
 *  date   Date,
 *  time   Time
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param label 
 * @return modified offset
 */
static guint
fDateTime (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * BACnetDestination ::= SEQUENCE {
 *  validDays   BACnetDaysOfWeek,
 *  fromTime    Time,
 *  toTime      Time,
 *  recipient   BACnetRecipient,
 *  processIdentifier   Unsigned32,
 *  issueConfirmedNotifications BOOLEAN,
 *  transitions BACnetEventTransitionBits
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fDestination (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetDeviceObjectPropertyReference ::= SEQUENCE {
 *  objectIdentifier    [0] BACnetObjectIdentifier,
 *  propertyIdentifier  [1] BACnetPropertyIdentifier,
 *  propertyArrayIndex  [2] Unsigend OPTIONAL,
 *  deviceIdentifier    [3] BACnetObjectIdentifier OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fDeviceObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetDeviceObjectReference ::= SEQUENCE {
 *  deviceIdentifier    [0] BACnetObjectIdentifier OPTIONAL,
 *  objectIdentifier    [1] BACnetObjectIdentifier
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fDeviceObjectReference (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetEventParameter ::= CHOICE {
 * 	change-of-bitstring [0] SEQUENCE {
 *   	time-delay [0] Unsigned,
 *  	bitmask [1] BIT STRING,
 *   	list-of-bitstring-values [2] SEQUENCE OF BIT STRING
 *   	},
 *  change-of-state [1] SEQUENCE {
 *   	time-delay [0] Unsigned,
 *   	list-of-values [1] SEQUENCE OF BACnetPropertyStates
 *   	},
 *   change-of-value [2] SEQUENCE {
 *   	time-delay [0] Unsigned,
 *   	cov-criteria [1] CHOICE {
 *   		bitmask [0] BIT STRING,
 *   		referenced-property-increment [1] REAL
 *			}
 *		},
 *	command-failure [3] SEQUENCE {
 *		time-delay [0] Unsigned,
 *		feedback-property-reference [1] BACnetDeviceObjectPropertyReference
 *		},
 *	floating-limit [4] SEQUENCE {
 *		time-delay [0] Unsigned,
 *		setpoint-reference [1] BACnetDeviceObjectPropertyReference,
 *		low-diff-limit [2] REAL,
 *		high-diff-limit [3] REAL,
 *		deadband [4] REAL
 *		},
 *	out-of-range [5] SEQUENCE {
 *		time-delay [0] Unsigned,
 *		low-limit [1] REAL,
 *		high-limit [2] REAL,
 *		deadband [3] REAL
 *		},
 *	buffer-ready [7] SEQUENCE {
 *		notification-threshold [0] Unsigned,
 *		previous-notification-count [1] Unsigned32
 *		}
 *	change-of-life-safety [8] SEQUENCE {
 *		time-delay [0] Unsigned,
 *		list-of-life-safety-alarm-values [1] SEQUENCE OF BACnetLifeSafetyState,
 *		list-of-alarm-values [2] SEQUENCE OF BACnetLifeSafetyState,
 *		mode-property-reference [3] BACnetDeviceObjectPropertyReference
 *		}
 *	}
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fEventParameter (tvbuff_t *tvb, proto_tree *tree, guint offset);


/**
 * BACnetLogRecord ::= SEQUENCE {
 *	timestamp [0] BACnetDateTime,
 *	logDatum [1] CHOICE {
 *		log-status [0] BACnetLogStatus,
 *		boolean-value [1] BOOLEAN,
 *		real-value [2] REAL,
 *		enum-value [3] ENUMERATED, -- Optionally limited to 32 bits
 *		unsigned-value [4] Unsigned, -- Optionally limited to 32 bits
 *		signed-value [5] INTEGER, -- Optionally limited to 32 bits
 *		bitstring-value [6] BIT STRING,-- Optionally limited to 32 bits
 *		null-value [7] NULL,
 *		failure [8] Error,
 *		time-change [9] REAL,
 *		any-value [10] ABSTRACT-SYNTAX.&Type -- Optional
 *		}
 *	statusFlags [2] BACnetStatusFlags OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fLogRecord (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetNotificationParameters ::= CHOICE {
 * 	change-of-bitstring	[0]	SEQUENCE {
 *      referenced-bitstring    [0] BIT STRING,
 *      status-flags    [1] BACnetStatusFlags
 *      },
 *  change-of-state [1]	SEQUENCE {
 *      new-state   [0] BACnetPropertyStatus,
 *      status-flags    [1] BACnetStatusFlags
 *      },
 *  change-of-value [2]	SEQUENCE {
 *      new-value   [0] CHOICE {
 *          changed-bits   [0] BIT STRING,
 *          changed-value    [1] REAL
 *          },
 *      status-flags    [1] BACnetStatusFlags
 *      },
 *  command-failure [3]	SEQUENCE {
 *      command-value   [0] ABSTRACT-SYNTAX.&Type, -- depends on ref property
 *      status-flags    [1] BACnetStatusFlags
 *      feedback-value    [2] ABSTRACT-SYNTAX.&Type -- depends on ref property
 *      },
 *  floating-limit [4]	SEQUENCE {
 *      reference-value   [0] REAL,
 *      status-flags    [1] BACnetStatusFlags
 *      setpoint-value   [2] REAL,
 *      error-limit   [3] REAL
 *      },
 *  out-of-range [5]	SEQUENCE {
 *      exceeding-value   [0] REAL,
 *      status-flags    [1] BACnetStatusFlags
 *      deadband   [2] REAL,
 *      exceeded-limit   [0] REAL
 *      },
 *  complex-event-type  [6] SEQUENCE OF BACnetPropertyValue,
 *  buffer-ready [7]	SEQUENCE {
 *      buffer-device   [0] BACnetObjectIdentifier,
 *      buffer-object    [1] BACnetObjectIdentifier
 *      previous-notification   [2] BACnetDateTime,
 *      current-notification   [3] BACnetDateTime
 *      },
 *  change-of-life-safety [8]	SEQUENCE {
 *      new-state   [0] BACnetLifeSafetyState,
 *      new-mode    [1] BACnetLifeSafetyState
 *      status-flags   [2] BACnetStatusFlags,
 *      operation-expected   [3] BACnetLifeSafetyOperation
 *      }
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fNotificationParameters (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetObjectPropertyReference ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 * 	propertyIdentifier	[1] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[2] Unsigned OPTIONAL, -- used only with array datatype
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fObjectPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetObjectPropertyValue ::= SEQUENCE {
 *		objectIdentifier [0] BACnetObjectIdentifier,
 *		propertyIdentifier [1] BACnetPropertyIdentifier,
 *		propertyArrayIndex [2] Unsigned OPTIONAL, -- used only with array datatype
 *				-- if omitted with an array the entire array is referenced
 *		value [3] ABSTRACT-SYNTAX.&Type, --any datatype appropriate for the specified property
 *		priority [4] Unsigned (1..16) OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fObjectPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetPriorityArray ::= SEQUENCE SIZE (16) OF BACnetPriorityValue
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fPriorityArray (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetPropertyReference ::= SEQUENCE {
 * 	propertyIdentifier	[0] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[1] Unsigned OPTIONAL, -- used only with array datatype
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fPropertyReference (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetPropertyValue ::= SEQUENCE {
 * 		PropertyIdentifier [0] BACnetPropertyIdentifier,
 * 		propertyArrayIndex [1] Unsigned OPTIONAL, -- used only with array datatypes
 * 				-- if omitted with an array the entire array is referenced
 * 		value [2] ABSTRACT-SYNTAX.&Type, -- any datatype appropriate for the specified property
 * 		priority [3] Unsigned (1..16) OPTIONAL -- used only when property is commandable
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fPropertyValue (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet Application PDUs chapter 21
 * BACnetRecipient::= CHOICE {
 * 	device	[0] BACnetObjectIdentifier
 * 	address	[1] BACnetAddress
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fRecipient (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet Application PDUs chapter 21
 * BACnetRecipientProcess::= SEQUENCE {
 * 	recipient	[0] BACnetRecipient
 * 	processID	[1] Unsigned32
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fRecipientProcess (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetSessionKey ::= SEQUENCE {
 * 	sessionKey	OCTET STRING (SIZE(8)), -- 56 bits for key, 8 bits for checksum
 * 	peerAddress	BACnetAddress
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 * @todo check if checksum is displayed correctly 
 */
static guint
fSessionKey (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetSetpointReference ::= SEQUENCE {
 * 	sessionKey	[0] BACnetObjectPropertyReference OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fSetpointReference (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetSpecialEvent ::= SEQUENCE {
 * 	period		CHOICE {
 * 		calendarEntry		[0] BACnetCalendarEntry,
 * 		calendarRefernce	[1] BACnetObjectIdentifier
 * 		},
 * 		listOfTimeValues	[2] SEQUENCE OF BACnetTimeValue,
 * 		eventPriority		[3] Unsigned (1..16)
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fSpecialEvent (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetTimeStamp ::= CHOICE {
 * 	time			[0] Time,
 * 	sequenceNumber	[1] Unsigned (0..65535),
 * 	dateTime		[2] BACnetDateTime
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fTimeStamp (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetTimeValue ::= SEQUENCE {
 * 	time	Time,
 * 	value	ABSTRACT-SYNTAX.&Type -- any primitive datatype, complex types cannot be decoded
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fTimeValue (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetVTSession ::= SEQUENCE {
 * 	local-vtSessionID	Unsigned8,
 * 	remote-vtSessionID	Unsigned8,
 * 	remote-vtAddress	BACnetAddress
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fVTSession (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnetWeekNDay ::= OCTET STRING (SIZE (3))
 * -- first octet month (1..12) January = 1, X'FF' = any month
 * -- second octet weekOfMonth where: 1 = days numbered 1-7
 * -- 2 = days numbered 8-14
 * -- 3 = days numbered 15-21
 * -- 4 = days numbered 22-28
 * -- 5 = days numbered 29-31
 * -- 6 = last 7 days of this month
 * -- X’FF’ = any week of this month
 * -- third octet dayOfWeek (1..7) where 1 = Monday
 * -- 7 = Sunday
 * -- X'FF' = any day of week
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWeekNDay (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadAccessResult ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 * 	listOfResults	[1] SEQUENCE OF SEQUENCE {
 * 		propertyIdentifier	[2] BACnetPropertyIdentifier,
 * 		propertyArrayIndex	[3] Unsigned OPTIONAL, -- used only with array datatype if omitted with an array the entire array is referenced
 * 		readResult	CHOICE {
 * 			propertyValue	[4] ABSTRACT-SYNTAX.&Type,
 * 			propertyAccessError	[5] Error
 * 		}
 *  } OPTIONAL
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadAccessResult (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * ReadAccessSpecification ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 * 	listOfPropertyReferences	[1] SEQUENCE OF BACnetPropertyReference
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fReadAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset);

/**
 * WriteAccessSpecification ::= SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 * 	listOfProperty	[1] SEQUENCE OF BACnetPropertyValue
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fWriteAccessSpecification (tvbuff_t *tvb, proto_tree *subtree, guint offset);


/********************************************************* Helper functions *******************************************/

/**
 * extracts the tag number from the tag header.
 * @param tvb "TestyVirtualBuffer"
 * @param offset in actual tvb
 * @return Tag Number corresponding to BACnet 20.2.1.2 Tag Number
 */
static guint
fTagNo (tvbuff_t *tvb, guint offset);
                                     
/**
 * splits Tag Header coresponding to 20.2.1 General Rules For BACnet Tags
 * @param tvb = "TestyVirtualBuffer"
 * @param offset = offset in actual tvb
 * @return tag_no BACnet 20.2.1.2 Tag Number
 * @return class_tag BACnet 20.2.1.1 Class
 * @return lvt BACnet 20.2.1.3 Length/Value/Type
 * @return offs = length of this header
 */

static guint
fTagHeader (tvbuff_t *tvb, guint offset, guint8 *tag_no, guint8* class_tag, guint32 *lvt);


/**
 * adds processID with max 32Bit unsigned Integer Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fProcessId (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * adds timeSpan with max 32Bit unsigned Integer Value to tree
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fTimeSpan (tvbuff_t *tvb, proto_tree *tree, guint offset, guint8 *label);

/**
 * BACnet Application PDUs chapter 21
 * BACnetPropertyIdentifier::= ENUMERATED {
 * 	 @see bacapp_property_identifier
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @param tt returnvalue of this item 
 * @return modified offset
 */
static guint
fPropertyIdentifier (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * listOfEventSummaries ::= SEQUENCE OF SEQUENCE {
 * 	objectIdentifier	[0] BACnetObjectIdentifier,
 *  eventState  [1] BACnetEventState,
 *  acknowledgedTransitions [2] BACnetEventTransitionBits,
 *  eventTimeStamps [3] SEQURNCE SIZE (3) OF BACnetTimeStamps,
 *  notifyType  [4] BACnetNotifyType,
 *  eventEnable [5] BACnetEventTransitionBits,
 *  eventPriorities [6] SEQUENCE SIZE (3) OF Unsigned
 * }
 * @param tvb 
 * @param tree
 * @param offset 
 * @return modified offset
 */
static guint
flistOfEventSummaries (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * SelectionCriteria ::= SEQUENCE {
 * 	propertyIdentifier	[0] BACnetPropertyIdentifier,
 * 	propertyArrayIndex	[1] Unsigned OPTIONAL, -- used only with array datatype
 *  relationSpecifier	[2] ENUMERATED { bacapp_relationSpecifier },
 *  comparisonValue	[3] ABSTRACT-SYNTAX.&Type
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fSelectionCriteria (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * objectSelectionCriteria ::= SEQUENCE {
 * 	selectionLogic	[0] ENUMERATED { bacapp_selectionLogic },
 * 	listOfSelectionCriteria	[1] SelectionCriteria
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fObjectSelectionCriteria (tvbuff_t *tvb, proto_tree *subtree, guint offset);

/**
 * ObjectSpecifier ::= CHOICE {
 * 	objectType	[0] BACnetObjectType,
 *  objectIdentifier	[1] BACnetObjectIdentifier
 * }
 * @param tvb 
 * @param tree 
 * @param offset 
 * @return modified offset
 */
static guint
fObjectSpecifier (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * BACnet-Error ::= SEQUENCE {
 *    error-class ENUMERATED {},
 *    error-code  ENUMERATED {}
 *    }
 * }
 * @param tvb
 * @param tree 
 * @param offset
 * @return modified offset
 */
static guint
fError(tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * realizes some ABSTRACT-SYNTAX.&Type
 * @param tvb
 * @param tree
 * @param offset
 * @return modified offset
 * @todo beautify this ugly construct
 */
static guint
fAbstractSyntaxNType (tvbuff_t *tvb, proto_tree *tree, guint offset);

/**
 * register_bacapp
 */
void
proto_register_bacapp(void);

/**
 * proto_reg_handoff_bacapp
 */
void
proto_reg_handoff_bacapp(void);

/**
 * converts XXX coded strings to UTF-8 if iconv is allowed
 * else 'in' is copied to 'out'
 * @param in  -- pointer to string
 * @param inbytesleft
 * @param out -- pointer to string
 * @param outbytesleft
 * @param fromcoding
 * @return count of modified characters of returned string, -1 for errors
 */
guint32
fConvertXXXtoUTF8(const guint8 *in, size_t *inbytesleft,guint8 *out, size_t *outbytesleft, guint8 *fromcoding);

#endif /* __BACAPP_H__ */


