Datentypen

Im Automation Interface und via ADS werden komplexe Properties als Byte-Array kodiert. In diesem Abschnitt soll erläutert werden, wie die BACnet-Datenkonzepte innerhalb von TwinCAT BACnet/IP auf C-Datenstrukturen abgebildet und bei der Konfiguration via "Automation Interface" bzw. ADS verwendet werden können. Im BACnet-Standard werden in Kapitel 21 ("FORMAL DESCRIPTION OF APPLICATION PROTOCOL DATA UNITS") die BACnet-Datenstrukturen spezifiziert.

Bis auf wenige Ausnahmen erfolgt die Abbildung von BACnet nach C der BACnet-Spezifikation. Ausnahmen bilden dynamische Daten, die entsprechend auf Listen bzw. AnyTypes abgebildet werden. Generell kann der Typ einer Property mit Hilfe des TwinCAT System-Managers ermittelt werden. In der Spalte "Type" im Reiter "Settings" und Reiter "Online" ist der Datentyp jeder Property inkl. der Struktur (durch Aufklappen der Property-Daten) zu erkennen. Datentypnamen die in der Spalte "Type" auf "[]" enden sind Arrays, Datentypnamen die auf "List" enden sind Listen. Choice-basierte Datentypen sind an der Auswahl-Combobox in der Spalte "Type" zu erkennen, Datentypen mit optionalen Felder an einer CheckBox im ID-Feld.

Primitive Datentypen

0 = Null

Wird abgebildet durch eine Datenlänge von 0.

1 = Boolean

Wird abgebildet auf ULONG (4 Byte) mit den Werten 0x00000001 für TRUE und 0x00000000 für FALSE

2 = Unsigned Integer

Wird abgebildet auf ULONG (4 Byte). Eine optimierte Variante kann verwendet werden, wobei Ganzzahlwerte bis 255 als 1 Byte und Ganzzahlwerte bis 65535 als 2 Byte (USHORT) dargestellt werden.

3 = Signed Integer

Wird abgebildet auf LONG (4 Byte)

4 = Real

Wird abgebildet auf FLOAT (4 Byte)

5 = Double

Wird momentan nicht verwendet

6 = Octet String

Wird abgebildet auf BYTE[]

7 = Character String

Wird abgebildet auf CHAR[]. Also 0 terminierte ISO 8859-1 Zeichenketten. Momentan werden in TwinCAT BACnet/IP Zeichenkette der Länge 256 unterstützt.

8 = Bit String

Wird abgebildet auf BYTE[]. Im höherwertigsten (most significant) Byte steht die Anzahl der im nachfolgenden Byte nicht verwendeten Bits ( 0..7 ). Der erste boole'sche Wert steht im höherwertigsten Bit des nachfolgenden Byte. Bis auf wenige Ausnahmen haben Bit Strings eine Länge von 2 Bytes.

9 = Enumerated

Wird abgebildet auf C-Enumerationen ( 4 Byte ) - von 0 beginnend.

10 = Date

Wird abgebildet auf:

struct BACnetDate 
{
  BYTE year; // year minus 1900 X'FF' = unspecified
  BYTE month; // (1.. 12) 1 = January X'FF' = unspecified
  BYTE day; // day of month (1..31), X'FF' = unspecified
  BYTE dayOfWeek; // day of week (1..7) 1 = Monday -- 7 = Sunday -- X'FF' = unspecified
}

11 = Time

Wird abgebildet auf:

struct BACnetTime 
{
  BYTE hour; // hour (0..23), (X'FF') = unspecified
  BYTE minute; // minute (0..59), (X'FF') = unspecified
  BYTE second; // (0..59), (X'FF') = unspecified
  BYTE hundredths; // hundredths (0..99), (X'FF') = unspecified
}

12 = BACnetObjectIdentifier

Wird abgebildet auf einen 4-Byte ObjektIdentifier mit 10Bit für den Objekttyp und einer 22 Bit Objektinstanz

struct BACnetObjectIdentifier 
{
  DWORD objInst : 22;
  DWORD objType : 10;
}

Strukturen

BACnet-Sequenzen ohne optionale Felder werden auf Strukturen abgebildet. Im Folgenden ist ein Beispiel dargestellt.

BACnetPrescale ::= 
SEQUENCE
{
  multiplier [0] Unsigned,
  moduloDivide [1] Unsigned
}
struct BACnetPrescale 
{
  ULONG multiplier;
  ULONG moduloDivide;
}

Optionale Felder

BACnet-Sequenzen mit optionalen Feldern werden auf Strukturen mit einem zusätzlichen Feld validFields abgebildet. Das zusätzliche Feld gibt an welche Felder in der Struktur aktiv sind. Bit 0 von validFields gibt an ob das nächstfolgende Feld (im Beispiel objectIdentifier) aktiv ist. Bei nicht optionalen Feldern ist/muss das entsprechende Bit immer gesetzt/sein.

BACnetObjectPropertyReference ::= SEQUENCE 
{
  objectIdentifier [0] BACnetObjectIdentifier,
  propertyIdentifier [1] BACnetPropertyIdentifier,
  propertyArrayIndex [2] Unsigned OPTIONAL
}
struct BACnetObjectPropertyReference 
{
  ULONG validFields;
  BACnetObjectIdentifier objectIdentifier; // [0]
  BACnetPropertyIdentifier propertyIdentifier; // [1]
  ULONG propertyArrayIndex; // [2] Unsigned OPTIONAL
}

Choice

Eine BACnet-Choice wird auf eine Struktur mit dem Feld choiceField und einer Union abgebildet. Das Feld choiceField selektiert dabei welches Feld der union aktiv ist. Die Länge der Daten wird dabei durch das größte Feld der union bestimmt. Das Feld choiceField ist eine Enumeration (4 Byte).

BACnetRecipient ::= 
CHOICE
{
  device [0] BACnetObjectIdentifier,
  address [1] BACnetAddress
}
enum BACnetRecipient_Choice
{
  device,
  address };struct _BACnetRecipient
{
  BACnetRecipient_Choice choiceField; union
  {
    BACnetObjectIdentifier device;
    BACnetAddress address;
  };
}

Arrays

BACnet-Arrays mit fixen Datengrößen werden auf C-Arrays abgebildet. Einige BACnet-Arrays können Daten variabler Länge enthalten (Listen oder AnyTypes). In diesem Fall werden BACnet-Arrays auf Listen abgebildet, die im folgenden Abschnitt beschrieben werden.

Listen

BACnet-Listen und einige BACnet-Arrays werden auf eine spezielle Listenstruktur abgebildet. Jedes Listenelement besitzt einen Header (ListEntryHdr) dem direkt die Daten des Elements folgen. In obersten Bit von nEntrySize (nEntrySize & 0x80000000) ist kodiert, ob ein weiteres Element folgt ( Bit 31 = 1 ), oder ob es sich um das letzte Listenelement handelt (Bit 31 = 0). In den unteren 31 Bit von nEntrySize ist die Größe der nachfolgenden Daten als Anzahl von Bytes kodiert. Der Header enthält ein weiteres intern verwendetes reserved Feld dem die Daten folgen. Das nachfolgende Listenelement beginnt an einer 4-Byte-alignten den Daten folgenden Grenze.

struct
_ListEntryHdr
{
    DWORD nEntrySize;
    DWORD reserved;
}

Enthält z.B. eine Liste ein erstes Element der Länge 1 mit dem Wert 1, ist die Liste kodiert: 08 00 00 01 00 00 00 00 01 dann folgen 3 Alignment-Bytes 00 00 00. Ist das nächste Listenelement das letzte Element der Liste mit 1 Byte Daten mit dem Wert 3: 00 00 00 01 00 00 00 00 03. Die Datenlänge der Beispielliste wäre 24 Byte, da dem letzten Element von 3 Alignment-Bytes folgen.

Eine leere Liste ist kodiert als (4 Byte): 00 00 00 00.

AnyTypes

BACnet Daten vom Typ ABSTRACT-SYNTAX.&Type werden durch die Struktur AnyType abgebildet. Das Feld nSize enthält die Größe der nachfolgenden Daten inkl. dem Feld dataType (4 Byte). Die eigentlichen Daten haben also die Größe nSize-4. Das Feld dataType gibt den Datentyp des Datenelements an. Eine Liste der Datentypen ist im Folgenden dargestellt.

struct
AnyType
{
    ULONG nSize;
    BACnetDataTypes dataType;    //... Data
}

Im Folgenden sind die verwendeten Datentyp-Kodierungen für AnyTypes aufgelistet:

#define ARRAYTYPE 0x100
#define LISTTYPE 0x200
enum BACnetDataTypes
{
// Primitive Data
DataTypeNull = 0,
DataTypeBool = 1,
DataTypeCharacterStringExt = 2,
DataTypeUnsignedInteger = 3,
DataTypeSignedInteger = 4,
DataTypeReal = 5,
DataTypeDouble = 6,
DataTypeOctetString = 7,
DataTypeEnumerated = 8,
DataTypeBACnetObjectIdentifier = 9,
DataTypeAddressBinding = 10,
DataTypeBitString = 11,
DataTypeDate = 12,
DataTypeTime = 13,
// Complex Data
DataTypeDateTime = 14,
DataTypeArrayIndex = 15,
DataTypeBACnetCalendarEntry = 16,
DataTypeBACnetObjectType = 17,
DataTypeBACnetEventTransitionBits = 18,
DataTypeBACnetStatusFlags = 20,
DataTypeBACnetEventState = 21,
DataTypeBACnetReliability = 22,
DataTypeBACnetPolarity = 23,
DataTypeBACnetDateTime = 24,
DataTypeBACnetEngineeringUnits = 25,
DataTypeBACnetPriorityValue = 26,
DataTypeBACnetLimitEnable = 27,
DataTypeBACnetNotifyType = 29,
DataTypeBACnetTimeStamp = 30,
DataTypeBACnetDeviceObjectPropertyReference = 31,
DataTypeBACnetDeviceStatus = 32,
DataTypeBACnetServicesSupported = 33,
DataTypeBACnetObjectTypesSupported = 34,
DataTypeBACnetSegmentation = 35,
DataTypeBACnetVTClass = 36,
DataTypeBACnetVTSession = 37,
DataTypeBACnetSessionKey = 38,
DataTypeBACnetRecipient = 39,
DataTypeBACnetAddressBinding = 40,
DataTypeBACnetCOVSubscription = 41,
DataTypeBACnetEventType = 42,
DataTypeBACnetEventParameter = 43,
DataTypeBACnetFileAccessMethod = 44,
DataTypeReadAccessSpecification = 45,
DataTypeReadAccessResult = 46,
DataTypeBACnetObjectPropertyReference = 47,
DataTypeBACnetSetpointReference = 48,
DataTypeBACnetDestination = 49,
DataTypeBACnetProgramState = 50,
DataTypeBACnetProgramRequest = 51,
DataTypeBACnetProgramError = 52,
DataTypeBACnetDateRange = 53,
DataTypeBACnetDailySchedule = 54,
DataTypeBACnetSpecialEvent = 55,
DataTypeBACnetClientCOV = 56,
DataTypeBACnetLogRecord = 57,
DataTypeBACnetLifeSafetyState = 58,
DataTypeBACnetLifeSafetyMode = 59,
DataTypeBACnetSilencedState = 60,
DataTypeBACnetLifeSafetyOperation = 61,
DataTypeBACnetBinaryPV = 62,
DataTypeBACnetDaysOfWeek = 63,
DataTypeBACnetWeekNDay = 64,
DataTypeBACnetTimeValue = 65,
DataTypeBACnetValue = 66,
DataTypeBACnetAddress = 67,
DataTypeBACnetPropertyIdentifier = 68,
DataTypeEnumerationValueType = 69,
DataTypeBitFieldBit = 70,
DataTypeBACnetLogDatum = 71,
DataTypeAnyType = 72,
DataTypePresentValue = 73,
DataTypeContextTag = 74,
DataTypeValueChoice = 75,
DataTypeBACnetLogStatus = 76,
DataTypeBACnetRecipientProcess = 77,
DataTypeSubscribeCOVPropertyRequest = 78,
DataTypeBACnetPropertyReference = 79,
DataTypeBACnetPropertyResult = 80,
DataTypeBACnetDeviceObjectPropertyValue = 81,
DataTypeCOVNotificationRequest = 82,
DataTypeEventNotificationRequest = 83,
DataTypeBACnetNotificationParameters = 84,
DataTypeChange_of_Bitstring = 85,
DataTypeChange_of_State=86,
DataTypeChange_of_Value=87,
DataTypeCommand_failure=88,
DataTypeFloating_limit=89,
DataTypeOut_of_Range =90,
DataTypeBACnetPropertyValue=91,
DataTypeComplex_Tag=92,
DataTypeChange_of_life_safety = 93,
DataTypeExtended=94,
DataTypeBuffer_ready =95,
DataTypeUnsigned_range = 96,
DataTypeBACnetPropertyResultValue = 97,
DataTypeServiceCOVPropSubscription = 98,
DataTypeServiceCOVSubscription = 99,
DataTypeCOVNotification = 100,
DataTypeAcknowledgeAlarmRequest = 101,
DataTypeBACnetVTOpenRequest = 102,
DataTypeBACnetPrescale = 103,
DataTypeBACnetScale = 104,
DataTypeBACnetAction = 105,
DataTypeBACnetError = 106,
DataTypeBacnetErrorType = 107,
DataTypeErrorClass = 108,
DataTypeErrorCode = 109,
DataTypeBACnetRejectReason = 110,
DataTypeBACnetAbortReason = 111,
DataTypeBDTEntry = 112,
DataTypeIpEntry = 113,
DataTypeMaskEntry = 114,
DataTypeBACnetEthernetAddress = 115,
DataTypeBACnetLonTalkAddress = 116,
DataTypeBACnetLonTalkNeuronId = 117,
DataTypeBACnetNodeType = 118,
DataTypeBACnetDeviceObjectReference = 119,
DataTypeBACnetActionCommand = 120,
DataTypeGetEventInformation = 121,
DataTypeGetEnrollmentSummaryCriteria = 122,
DataTypeGetEnrollmentSummaryCriteriaResponse = 123,
DataTypeDeviceCommunicationControl = 124,
DataTypeInt16 = 125,
DataTypeUInt16 = 126,
DataTypeUInt8 = 127,
DataTypeInt8 = 128,
DataTypeByte = 129,
DataTypeFDTEntry = 130,
DataTypeBACnetAddress_3 = 131,
DataTypeBACnetAddress_4 = 132,
DataTypeBACnetAddress_5 = 133,
DataTypePersistentDataState = 134,
DataTypeFallbackRealValue = 135,
DataTypeFallbackBinaryValue = 136,
DataTypeRealNull = 137, // PresentValue in AnalogObj
DataTypeEnumeratedNull = 138, // PresentValue in BinaryObj
DataTypeUnsignedIntegerNull = 139, // PresentValue in Multistate
DataTypeUnknown = 140,
DataTypeBACnetDiagnosisPerformance = 200,
DataTypeBACnetDiagnosisEthStatistics = 201,
DataTypeBACnetFrameStatistics = 202,
DataTypeBACnetInfo = 203,
DataTypeBACnetDiagnosis = 204,
DataTypeTcIoEthStatistic = 205,
DataTypeTcIoEthTxRxErrorCount = 206,
DataTypeTcIoEthPortStatistic = 207,
DataTypeBACnetServerStatistics = 208,
DataTypeBACnetFrameServicesDiag = 209,
// Arraytypen
DataTypeBACnetCalendarEntryArr = ARRAYTYPE + DataTypeBACnetCalendarEntry,
DataTypeBACnetObjectIdentifierArr = ARRAYTYPE + DataTypeBACnetObjectIdentifier,
DataTypeCharacterStringExtArr = ARRAYTYPE + DataTypeCharacterStringExt,
DataTypeBACnetPriorityValueArr = ARRAYTYPE + DataTypeBACnetPriorityValue,
DataTypeBACnetTimeStampArr = ARRAYTYPE + DataTypeBACnetTimeStamp,
DataTypeBACnetVTClassArr = ARRAYTYPE + DataTypeBACnetVTClass,
DataTypeBACnetVTSessionArr = ARRAYTYPE + DataTypeBACnetVTSession,
DataTypeBACnetSessionKeyArr = ARRAYTYPE + DataTypeBACnetSessionKey,
DataTypeBACnetRecipientArr = ARRAYTYPE + DataTypeBACnetRecipient,
DataTypeBACnetAddressBindingArr = ARRAYTYPE + DataTypeBACnetAddressBinding,
DataTypeBACnetCOVSubscriptionArr = ARRAYTYPE + DataTypeBACnetCOVSubscription,
DataTypeUnsignedIntegerArr = ARRAYTYPE + DataTypeUnsignedInteger,
DataTypeBACnetDestinationArr = ARRAYTYPE + DataTypeBACnetDestination,
DataTypeBACnetDeviceObjectPropertyReferenceArr = ARRAYTYPE + DataTypeBACnetDeviceObjectPropertyReference,
DataTypeBACnetLifeSafetyStateArr = ARRAYTYPE + DataTypeBACnetLifeSafetyState,
DataTypeEnumeratedArr = ARRAYTYPE + DataTypeEnumerated,
DataTypeReadAccessSpecificationArr = ARRAYTYPE + DataTypeReadAccessSpecification,
DataTypeReadAccessResultArr = ARRAYTYPE + DataTypeReadAccessResult,
DataTypeBACnetSpecialEventArr = ARRAYTYPE + DataTypeBACnetSpecialEvent,
DataTypeBACnetLogRecordArr = ARRAYTYPE + DataTypeBACnetLogRecord,
DataTypeBACnetTimeValueArr = ARRAYTYPE + DataTypeBACnetTimeValue,
DataTypeBACnetDeviceObjectReferenceArr = ARRAYTYPE + DataTypeBACnetDeviceObjectReference,
// Listen
DataTypeBACnetTimeValueList = LISTTYPE + DataTypeBACnetTimeValue,
DataTypeBACnetSpecialEventList = LISTTYPE + DataTypeBACnetSpecialEvent,
DataTypeBACnetDailyScheduleList = LISTTYPE + DataTypeBACnetTimeValueList,
DataTypeCharacterStringExtList = LISTTYPE + DataTypeCharacterStringExt,
DataTypeBACnetLogRecordList = LISTTYPE + DataTypeBACnetLogRecord,
DataTypeUnsignedIntegerList = LISTTYPE + DataTypeUnsignedInteger,
DataTypeReadAccessResultList = LISTTYPE + DataTypeReadAccessResult,
DataTypeReadAccessSpecificationList = LISTTYPE + DataTypeReadAccessSpecification,
DataTypeBACnetValueList = LISTTYPE + DataTypeBACnetValue,
DataTypeBACnetLogDatumList = LISTTYPE + DataTypeBACnetLogDatum,
DataTypeBACnetPropertyResultList = LISTTYPE + DataTypeBACnetPropertyResult,
DataTypeBACnetPropertyReferenceList = LISTTYPE + DataTypeBACnetPropertyReference,
DataTypeCOVNotificationRequestList = LISTTYPE + DataTypeCOVNotificationRequest,
DataTypeEventNotificationRequestList = LISTTYPE + DataTypeEventNotificationRequest,
DataTypeBACnetPropertyValueList = LISTTYPE + DataTypeBACnetPropertyValue,
DataTypeBACnetActionList = LISTTYPE + DataTypeBACnetActionCommand,
DataTypeBACnetActionListList = LISTTYPE + DataTypeBACnetActionList,
};