From 67f2cedae82ffec689dc382c48619917eb591ffd Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 28 Aug 2023 15:42:59 +0300 Subject: [PATCH 01/25] add bacnet package Signed-off-by: SammyOina --- base.go | 35 +++++++++++++++++++++++++++++++++++ client.go | 19 +++++++++++++++++++ go.mod | 3 +++ 3 files changed, 57 insertions(+) create mode 100644 base.go create mode 100644 client.go create mode 100644 go.mod diff --git a/base.go b/base.go new file mode 100644 index 0000000..34ed666 --- /dev/null +++ b/base.go @@ -0,0 +1,35 @@ +package bacnet + +type NetworkPriority int + +const ( + LifeSafetyMessage NetworkPriority = iota + 1 + CriticalEquipmentMessage + UrgentMessage + NormalMessage +) + +type NetworkLayerMessage int + +const ( + WhoIsRouterToNetwork NetworkLayerMessage = iota + IAmRouterToNetwork + ICouldBeRouterToNetwork + RejectMessageToNetwork + RouterBusyToNetwork + RouterAvailableToNetwork + InitRtTable + InitRtTableAck + EstablishConnectionToNetwork + DisconnectConnectionToNetwork + ChallengeRequest + SecurityPayload + SecurityResponse + RequestKeyUpdate + UpdateKeySet + UpdateDistributionKey + RequestMasterKey + SetMasterKey + WhatIsNetworkNumber + NetworkNumberIs +) diff --git a/client.go b/client.go new file mode 100644 index 0000000..b639e27 --- /dev/null +++ b/client.go @@ -0,0 +1,19 @@ +package bacnet + +type client struct { + // Fields for client configuration +} + +type Client interface { + ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (Value, error) + WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value Value) error +} + +func NewClient(address string, port int) (Client, error) { +} + +func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (Value, error) { +} + +func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value Value) error { +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c8d470d --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/absmach/bacnet + +go 1.21.0 From 354108bbb4bc24350758e222c3141ba7ced4d090 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 28 Aug 2023 17:04:21 +0300 Subject: [PATCH 02/25] set base types and constants Signed-off-by: SammyOina --- base.go | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/base.go b/base.go index 34ed666..28fa40f 100644 --- a/base.go +++ b/base.go @@ -9,10 +9,10 @@ const ( NormalMessage ) -type NetworkLayerMessage int +type NetworkLayerMessageType int const ( - WhoIsRouterToNetwork NetworkLayerMessage = iota + WhoIsRouterToNetwork NetworkLayerMessageType = iota IAmRouterToNetwork ICouldBeRouterToNetwork RejectMessageToNetwork @@ -33,3 +33,119 @@ const ( WhatIsNetworkNumber NetworkNumberIs ) + +type BacnetMaxSegments int + +const ( + MaxSEG0 BacnetMaxSegments = 0 + MaxSEG2 BacnetMaxSegments = 0x10 + MaxSEG4 BacnetMaxSegments = 0x20 + MaxSEG8 BacnetMaxSegments = 0x30 + MaxSEG16 BacnetMaxSegments = 0x40 + MaxSEG32 BacnetMaxSegments = 0x50 + MaxSEG64 BacnetMaxSegments = 0x60 + MaxSEG65 BacnetMaxSegments = 0x70 +) + +type BacnetMaxAdpu int + +const ( + MaxAPDU50 BacnetMaxAdpu = iota + MaxAPDU128 + MaxAPDU206 + MaxAPDU480 + MaxAPDU1024 + MaxAPDU1476 +) + +type BacnetPduTypes int + +const ( + PDUTypeConfirmedServiceRequest BacnetPduTypes = iota + Server + NegativeAck + // TODO + SegmentResponseAccepted + MORE_FOLLOWS + SEGMENTED_MESSAGE BacnetPduTypes = 8 + PDUTypeUnconfirmedServiceRequest BacnetPduTypes = 0x10 + PDUTypeSimpleAck BacnetPduTypes = 0x20 + PDUTypeComplexAck BacnetPduTypes = 0x30 + PDUTypeSegmentAck BacnetPduTypes = 0x40 + PDUTypeError BacnetPduTypes = 0x50 + PDUTypeReject BacnetPduTypes = 0x60 + PDUTypeAbort BacnetPduTypes = 0x70 + PDUTypeMask BacnetPduTypes = 0xF0 +) + +type BacnetCharacterStringEncodings int + +const ( + CharacterANSIX34 BacnetCharacterStringEncodings = 0 + CharacterUTF8 BacnetCharacterStringEncodings = 0 + CharacterMSDBCS BacnetCharacterStringEncodings = 1 + CharacterJISC6226 BacnetCharacterStringEncodings = 2 + CharacterJISX0208 BacnetCharacterStringEncodings = 2 + CharacterUCS4 BacnetCharacterStringEncodings = 3 + CharacterUCS2 BacnetCharacterStringEncodings = 4 + CharacterISO8859 BacnetCharacterStringEncodings = 5 +) + +type BACnetApplicationTag int + +const ( + Null BACnetApplicationTag = iota + Boolean + UnsignedInt + SignedInt + Real + Double + OctetString + CharacterString + BitString + Enumerated + Date + Time + BACnetObjectIdentifier + Reserve1 + Reserve2 + Reserve3 +) + +type BacnetNpduControl int + +const ( + PriorityNormalMessage BacnetNpduControl = iota + PriorityUrgentMessage + PriorityCriticalMessage + PriorityLifeSafetyMessage + ExpectingReply + SourceSpecified BacnetNpduControl = 8 + DestinationSpecified BacnetNpduControl = 32 + NetworkLayerMessage BacnetNpduControl = 128 +) + +type BacnetNetworkMessageType int + +const ( + NetworkMessageWhoIsRouterToNetwork BacnetNetworkMessageType = iota + NetworkMessageIAmRouterToNetwork + NetworkMessageICouldBeRouterToNetwork + NetworkMessageRejectMessageToNetwork + NetworkMessageRouterBusyToNetwork + NetworkMessageRouterAvailableToNetwork + NetworkMessageInitRtTable + NetworkMessageInitRtTableAck + NetworkMessageEstablishConnectionToNetwork + NetworkMessageDisconnectConnectionToNetwork + NetworkMessageChallengeRequest + NetworkMessageSecurityPayload + NetworkMessageSecurityResponse + NetworkMessageRequestKeyUpdate + NetworkMessageUpdateKeySet + NetworkMessageUpdateDistributionKey + NetworkMessageRequestMasterKey + NetworkMessageSetMasterKey + NetworkMessageWhatIsNetworkNumber + NetworkMessageNetworkNumberIs +) From c46d913b6c95cf354687089ddac59f2474b77b31 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 28 Aug 2023 17:11:42 +0300 Subject: [PATCH 03/25] add error classes and codes Signed-off-by: SammyOina --- base.go | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/base.go b/base.go index 28fa40f..88e1ac5 100644 --- a/base.go +++ b/base.go @@ -149,3 +149,161 @@ const ( NetworkMessageWhatIsNetworkNumber NetworkMessageNetworkNumberIs ) + +// ErrorClassEnum represents the error classes as constants. +type ErrorClassEnum int + +const ( + Device ErrorClassEnum = iota + Object + Property + Resources + Security + Services + VT + Communication +) + +// Error Code Enum represents the error codes as constants. +type ErrorCodeEnum int + +const ( + Other ErrorCodeEnum = iota + AuthenticationFailed + ConfigurationInProgress + DeviceBusy + DynamicCreationNotSupported + FileAccessDenied + IncompatibleSecurityLevels + InconsistentParameters + InconsistentSelectionCriterion + InvalidDataType + InvalidFileAccessMethod + InvalidFileStartPosition + InvalidOperatorName + InvalidParameterDataType + InvalidTimestamp + KeyGenerationError + MissingRequiredParameter + NoObjectsOfSpecifiedType + NoSpaceForObject + NoSpaceToAddListElement + NoSpaceToWriteProperty + NoVTSessionsAvailable + PropertyIsNotAList + ObjectDeletionNotPermitted + ObjectIdentifierAlreadyExists + OperationalProblem + PasswordFailure + ReadAccessDenied + SecurityNotSupported + ServiceRequestDenied + Timeout + UnknownObject + UnknownProperty + UnknownVTClass + UnknownVTSession + UnsupportedObjectType + ValueOutOfRange + VTSessionAlreadyClosed + VTSessionTerminationFailure + WriteAccessDenied + CharacterSetNotSupported + InvalidArrayIndex + COVSubscriptionFailed + NotCOVProperty + OptionalFunctionalityNotSupported + InvalidConfigurationData + DataTypeNotSupported + DuplicateName + DuplicateObjectID + PropertyIsNotAnArray + AbortBufferOverflow + AbortInvalidAPDUInThisState + AbortPreemptedbyHigherPriorityTask + AbortSegmentationNotSupported + AbortProprietary + AbortOther + InvalidTag + NetworkDown + RejectBufferOverflow + RejectInconsistentParameters + RejectInvalidParameterDataType + RejectInvalidTag + RejectMissingRequiredParameter + RejectParameterOutOfRange + RejectTooManyArguments + RejectUndefinedEnumeration + RejectUnrecognizedService + RejectProprietary + RejectOther + UnknownDevice + UnknownRoute + ValueNotInitialized + InvalidEventState + NoAlarmConfigured + LogBufferFull + LoggedValuePurged + NoPropertySpecified + NotConfiguredForTriggeredLogging + UnknownSubscription + ParameterOutOfRange + ListElementNotFound + Busy + CommunicationDisabled + Success + AccessDenied + BadDestinationAddress + BadDestinationDeviceID + BadSignature + BadSourceAddress + BadTimestamp + CannotUseKey + CannotVerifyMessageID + CorrectKeyRevision + DestinationDeviceIDRequired + DuplicateMessage + EncryptionNotConfigured + EncryptionRequired + IncorrectKey + InvalidKeyData + KeyUpdateInProgress + MalformedMessage + NotKeyServer + SecurityNotConfigured + SourceSecurityRequired + TooManyKeys + UnknownAuthenticationType + UnknownKey + UnknownKeyRevision + UnknownSourceMessage + NotRouterToDNET + RouterBusy + UnknownNetworkMessage + MessageTooLong + SecurityError + AddressingError + WriteBDTFailed + ReadBDTFailed + RegisterForeignDeviceFailed + ReadFDTFailed + DeleteFDTEntryFailed + DistributeBroadcastFailed + UnknownFileSize + AbortAPDUTooLong + AbortApplicationExceededReplyTime + AbortOutOfResources + AbortTSMTimeout + AbortWindowSizeOutOfRange + FileFull + InconsistentConfiguration + InconsistentObjectType + InternalError + NotConfigured + OutOfMemory + ValueTooLong + AbortInsufficientSecurity + AbortSecurityError + DuplicateEntry + InvalidValueInThisState +) From 2e6bbc487a8d7cbc38c8b4c33edc5b05eaa6c717 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 29 Aug 2023 19:47:12 +0300 Subject: [PATCH 04/25] encode integers Signed-off-by: SammyOina --- base.go | 8 +++++++ encoding.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 encoding.go diff --git a/base.go b/base.go index 88e1ac5..4032a31 100644 --- a/base.go +++ b/base.go @@ -307,3 +307,11 @@ const ( DuplicateEntry InvalidValueInThisState ) + +type ObjectIdentifier struct { + Type ObjectType + Instance int +} + +func (oi *ObjectIdentifier) ASN1decode() { +} diff --git a/encoding.go b/encoding.go new file mode 100644 index 0000000..51ec325 --- /dev/null +++ b/encoding.go @@ -0,0 +1,63 @@ +package bacnet + +import ( + "bytes" + "encoding/binary" +) + +const ( + MaxObject = 0x3FF + InstanceBits = 22 + MaxInstance = 0x3FFFFF + MaxBitstringBytes = 15 + ArrayAll = 0xFFFFFFFF + NoPriority = 0 + MinPriority = 1 + MaxPriority = 16 +) + +func encodeUnsigned(value uint32) []byte { + switch { + case value < 0x100: + buf := make([]byte, 1) + buf[0] = uint8(value) + return buf + case value < 0x10000: + buf := make([]byte, 2) + binary.BigEndian.PutUint16(buf, uint16(value)) + return buf + case value < 0x1000000: + buf := make([]byte, 3) + buf[0] = byte((value & 0xff0000) >> 16) + buf[1] = byte((value & 0x00ff00) >> 8) + buf[2] = byte(value & 0x0000ff) + return buf + default: + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, value) + return buf + } +} + +func encodeSigned(value int32) []byte { + switch { + case value < 0x100: + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, uint8(value)) + return buf.Bytes() + case value < 0x10000: + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, uint16(value)) + return buf.Bytes() + case value < 0x1000000: + buf := make([]byte, 3) + buf[0] = byte((value & 0xff0000) >> 16) + buf[1] = byte((value & 0x00ff00) >> 8) + buf[2] = byte(value & 0x0000ff) + return buf + default: + buf := new(bytes.Buffer) + binary.Write(buf, binary.BigEndian, value) + return buf.Bytes() + } +} From 6c17e28e99e5dc847aef2217775e3fa6905c0013 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 29 Aug 2023 22:16:44 +0300 Subject: [PATCH 05/25] add objects Signed-off-by: SammyOina --- base.go | 29 ------ client.go | 8 +- encoding/decoding.go | 9 ++ encoding.go => encoding/encoding.go | 6 +- encoding/tags.go | 134 ++++++++++++++++++++++++++++ object.go | 51 +++++++++++ 6 files changed, 201 insertions(+), 36 deletions(-) create mode 100644 encoding/decoding.go rename encoding.go => encoding/encoding.go (92%) create mode 100644 encoding/tags.go create mode 100644 object.go diff --git a/base.go b/base.go index 4032a31..2184703 100644 --- a/base.go +++ b/base.go @@ -91,27 +91,6 @@ const ( CharacterISO8859 BacnetCharacterStringEncodings = 5 ) -type BACnetApplicationTag int - -const ( - Null BACnetApplicationTag = iota - Boolean - UnsignedInt - SignedInt - Real - Double - OctetString - CharacterString - BitString - Enumerated - Date - Time - BACnetObjectIdentifier - Reserve1 - Reserve2 - Reserve3 -) - type BacnetNpduControl int const ( @@ -307,11 +286,3 @@ const ( DuplicateEntry InvalidValueInThisState ) - -type ObjectIdentifier struct { - Type ObjectType - Instance int -} - -func (oi *ObjectIdentifier) ASN1decode() { -} diff --git a/client.go b/client.go index b639e27..0b78fc5 100644 --- a/client.go +++ b/client.go @@ -5,15 +5,15 @@ type client struct { } type Client interface { - ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (Value, error) - WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value Value) error + ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) + WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error } func NewClient(address string, port int) (Client, error) { } -func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (Value, error) { +func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) { } -func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value Value) error { +func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error { } diff --git a/encoding/decoding.go b/encoding/decoding.go new file mode 100644 index 0000000..a3c36c5 --- /dev/null +++ b/encoding/decoding.go @@ -0,0 +1,9 @@ +package encoding + +func DecodeUnsigned(buffer []byte, offset, len int) (int, uint32) { + value := uint32(0) + for i := 0; i < len; i++ { + value += uint32(buffer[offset+i]) << uint(8*(len-i-1)) + } + return len, value +} diff --git a/encoding.go b/encoding/encoding.go similarity index 92% rename from encoding.go rename to encoding/encoding.go index 51ec325..2188e7a 100644 --- a/encoding.go +++ b/encoding/encoding.go @@ -1,4 +1,4 @@ -package bacnet +package encoding import ( "bytes" @@ -16,7 +16,7 @@ const ( MaxPriority = 16 ) -func encodeUnsigned(value uint32) []byte { +func EncodeUnsigned(value uint32) []byte { switch { case value < 0x100: buf := make([]byte, 1) @@ -39,7 +39,7 @@ func encodeUnsigned(value uint32) []byte { } } -func encodeSigned(value int32) []byte { +func EncodeSigned(value int32) []byte { switch { case value < 0x100: buf := new(bytes.Buffer) diff --git a/encoding/tags.go b/encoding/tags.go new file mode 100644 index 0000000..b5888de --- /dev/null +++ b/encoding/tags.go @@ -0,0 +1,134 @@ +package encoding + +type BACnetApplicationTag int + +const ( + Null BACnetApplicationTag = iota + Boolean + UnsignedInt + SignedInt + Real + Double + OctetString + CharacterString + BitString + Enumerated + Date + Time + BACnetObjectIdentifier + Reserve1 + Reserve2 + Reserve3 +) + +func isExtendedTagNumber(b byte) bool { + return (b & 0xF0) == 0xF0 +} + +func isExtendedValue(b byte) bool { + return (b & 0x07) == 5 +} + +func isOpeningTag(b byte) bool { + return (b & 0x07) == 6 +} + +func isClosingTag(b byte) bool { + return (b & 0x07) == 7 +} + +func isContextSpecific(b byte) bool { + return (b & 0x8) == 0x8 +} + +func decodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { + len = 1 + + if isExtendedTagNumber(buf[offset]) { + return len + 1, buf[offset+len] + } + return len, buf[offset] >> 4 +} + +func DecodeIsCOntextTag(buf []byte, offset int, tagNum byte) bool { + _, myTagNum := decodeTagNumber(buf, offset) + return isContextSpecific(buf[offset]) && myTagNum == tagNum + +} + +func DecodeIsCOntextTagWithLength(buf []byte, offset int, tagNum byte) (int, bool) { + tagLen, myTagNum := decodeTagNumber(buf, offset) + return tagLen, isContextSpecific(buf[offset]) && myTagNum == tagNum +} + +func DecodeTagNumberAndValue(buf []byte, offset int) (len int, tagNum byte, val uint32) { + len, tagNum = decodeTagNumber(buf, offset) + + switch { + case isExtendedValue(buf[offset]): + switch buf[offset+len] { + case 255: + len += 1 + len1, val1 := DecodeUnsigned(buf, offset+len, 4) + len += len1 + val = val1 + case 254: + len += 1 + len1, val1 := DecodeUnsigned(buf, offset+len, 2) + len += len1 + val = val1 + default: + val = uint32(buf[offset+len]) + len += 1 + } + case isOpeningTag(buf[offset]), isClosingTag(buf[offset]): + val = 0 + default: + val = uint32(buf[offset] & 0x07) + } + return len, tagNum, val + +} + +func DecodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { + len = 1 + if isExtendedTagNumber(buf[offset]) { + return len + 1, buf[offset+len] + } + return len, buf[offset] >> 4 +} + +func EncodeTag(tagNum BACnetApplicationTag, ctxSpecific bool, lenVal int) []byte { + tag := []byte{} + value := byte(0) + + if ctxSpecific { + value = 0x8 + } + + if tagNum <= 14 { + value += byte(tagNum) << 4 + tag = append(tag, value) + } else { + value += 0xF0 + tag = append(tag, value) + tag = append(tag, byte(tagNum)) + } + + if lenVal <= 4 { + tag[0] += byte(lenVal) + return tag + } + tag[0] += 5 + switch { + case lenVal <= 253: + tag = append(tag, byte(lenVal)) + return tag + case lenVal <= 65535: + tag = append(tag, 254) + return append(tag, EncodeUnsigned(uint32(lenVal))...) + default: + tag = append(tag, 255) + return append(tag, EncodeUnsigned(uint32(lenVal))...) + } +} diff --git a/object.go b/object.go new file mode 100644 index 0000000..9c2beba --- /dev/null +++ b/object.go @@ -0,0 +1,51 @@ +package bacnet + +import ( + "encoding/binary" + + "github.com/absmach/bacnet/encoding" +) + +type ObjectInstance uint32 + +type ObjectType uint16 + +type ObjectIdentifier struct { + Type ObjectType + Instance ObjectInstance +} + +func (oi *ObjectIdentifier) Decode(buf []byte, offset, apdu_len int) int { + len, val := encoding.DecodeUnsigned(buf, offset, 4) + oi.Instance = ObjectInstance(val) & ObjectInstance(encoding.MaxInstance) + oi.Type = ObjectType(val >> encoding.InstanceBits & encoding.MaxObject) + return len +} + +func (oi *ObjectIdentifier) DecodeContext(buf []byte, offset, apdu_len int, tagNumber byte) int { + len := 0 + if encoding.DecodeIsCOntextTag(buf, offset+len, tagNumber) { + len1, _, lenVal := encoding.DecodeTagNumberAndValue(buf, offset+len) + len += len1 + len += oi.Decode(buf, offset+len1, int(lenVal)) + return len + } + return -1 +} + +func (oi *ObjectIdentifier) Encode() []byte { + value := uint32(oi.Type)&encoding.MaxObject< Date: Wed, 30 Aug 2023 01:13:24 +0300 Subject: [PATCH 06/25] encoding date time and init property value Signed-off-by: SammyOina --- base.go | 477 ++++++++++++++++++++++++++++++++++++++++++ client.go | 3 + encoding/date_time.go | 100 +++++++++ property.go | 8 + 4 files changed, 588 insertions(+) create mode 100644 encoding/date_time.go create mode 100644 property.go diff --git a/base.go b/base.go index 2184703..f44a793 100644 --- a/base.go +++ b/base.go @@ -286,3 +286,480 @@ const ( DuplicateEntry InvalidValueInThisState ) + +// BACnetPropertyIdentifier represents BACnet property identifiers. +type PropertyIdentifier int + +const ( + AckedTransitions PropertyIdentifier = 0 + AckRequired PropertyIdentifier = 1 + Action PropertyIdentifier = 2 + ActionText PropertyIdentifier = 3 + ActiveText PropertyIdentifier = 4 + ActiveVtSessions PropertyIdentifier = 5 + AlarmValue PropertyIdentifier = 6 + AlarmValues PropertyIdentifier = 7 + All PropertyIdentifier = 8 + AllWritesSuccessful PropertyIdentifier = 9 + ApduSegmentTimeout PropertyIdentifier = 10 + ApduTimeout PropertyIdentifier = 11 + ApplicationSoftwareVersion PropertyIdentifier = 12 + Archive PropertyIdentifier = 13 + Bias PropertyIdentifier = 14 + ChangeOfStateCount PropertyIdentifier = 15 + ChangeOfStateTime PropertyIdentifier = 16 + NotificationClass PropertyIdentifier = 17 + Blank1 PropertyIdentifier = 18 + ControlledVariableReference PropertyIdentifier = 19 + ControlledVariableUnits PropertyIdentifier = 20 + ControlledVariableValue PropertyIdentifier = 21 + CovIncrement PropertyIdentifier = 22 + DateList PropertyIdentifier = 23 + DaylightSavingsStatus PropertyIdentifier = 24 + Deadband PropertyIdentifier = 25 + DerivativeConstant PropertyIdentifier = 26 + DerivativeConstantUnits PropertyIdentifier = 27 + Description PropertyIdentifier = 28 + DescriptionOfHalt PropertyIdentifier = 29 + DeviceAddressBinding PropertyIdentifier = 30 + DeviceType PropertyIdentifier = 31 + EffectivePeriod PropertyIdentifier = 32 + ElapsedActiveTime PropertyIdentifier = 33 + ErrorLimit PropertyIdentifier = 34 + EventEnable PropertyIdentifier = 35 + EventState PropertyIdentifier = 36 + EventType PropertyIdentifier = 37 + ExceptionSchedule PropertyIdentifier = 38 + FaultValues PropertyIdentifier = 39 + FeedbackValue PropertyIdentifier = 40 + FileAccessMethod PropertyIdentifier = 41 + FileSize PropertyIdentifier = 42 + FileType PropertyIdentifier = 43 + FirmwareRevision PropertyIdentifier = 44 + HighLimit PropertyIdentifier = 45 + InactiveText PropertyIdentifier = 46 + InProcess PropertyIdentifier = 47 + InstanceOf PropertyIdentifier = 48 + IntegralConstant PropertyIdentifier = 49 + IntegralConstantUnits PropertyIdentifier = 50 + IssueConfirmedNotifications PropertyIdentifier = 51 + LimitEnable PropertyIdentifier = 52 + ListOfGroupMembers PropertyIdentifier = 53 + ListOfObjectPropertyReferences PropertyIdentifier = 54 + ListOfSessionKeys PropertyIdentifier = 55 + LocalDate PropertyIdentifier = 56 + LocalTime PropertyIdentifier = 57 + Location PropertyIdentifier = 58 + LowLimit PropertyIdentifier = 59 + ManipulatedVariableReference PropertyIdentifier = 60 + MaximumOutput PropertyIdentifier = 61 + MaxApduLengthAccepted PropertyIdentifier = 62 + MaxInfoFrames PropertyIdentifier = 63 + MaxMaster PropertyIdentifier = 64 + MaxPresValue PropertyIdentifier = 65 + MinimumOffTime PropertyIdentifier = 66 + MinimumOnTime PropertyIdentifier = 67 + MinimumOutput PropertyIdentifier = 68 + MinPresValue PropertyIdentifier = 69 + ModelName PropertyIdentifier = 70 + ModificationDate PropertyIdentifier = 71 + NotifyType PropertyIdentifier = 72 + NumberOfApduRetries PropertyIdentifier = 73 + NumberOfStates PropertyIdentifier = 74 + ObjectIdentifierPI PropertyIdentifier = 75 + ObjectList PropertyIdentifier = 76 + ObjectName PropertyIdentifier = 77 + ObjectPropertyReference PropertyIdentifier = 78 + ObjectTypePI PropertyIdentifier = 79 + Optional PropertyIdentifier = 80 + OutOfService PropertyIdentifier = 81 + OutputUnits PropertyIdentifier = 82 + EventParameters PropertyIdentifier = 83 + Polarity PropertyIdentifier = 84 + PresentValue PropertyIdentifier = 85 + Priority PropertyIdentifier = 86 + PriorityArray PropertyIdentifier = 87 + PriorityForWriting PropertyIdentifier = 88 + ProcessIdentifier PropertyIdentifier = 89 + ProgramChange PropertyIdentifier = 90 + ProgramLocation PropertyIdentifier = 91 + ProgramState PropertyIdentifier = 92 + ProportionalConstant PropertyIdentifier = 93 + ProportionalConstantUnits PropertyIdentifier = 94 + ProtocolConformanceClass PropertyIdentifier = 95 + ProtocolObjectTypesSupported PropertyIdentifier = 96 + ProtocolServicesSupported PropertyIdentifier = 97 + ProtocolVersion PropertyIdentifier = 98 + ReadOnly PropertyIdentifier = 99 + ReasonForHalt PropertyIdentifier = 100 + Recipient PropertyIdentifier = 101 + RecipientList PropertyIdentifier = 102 + Reliability PropertyIdentifier = 103 + RelinquishDefault PropertyIdentifier = 104 + Required PropertyIdentifier = 105 + Resolution PropertyIdentifier = 106 + SegmentationSupported PropertyIdentifier = 107 + Setpoint PropertyIdentifier = 108 + SetpointReference PropertyIdentifier = 109 + StateText PropertyIdentifier = 110 + StatusFlags PropertyIdentifier = 111 + SystemStatus PropertyIdentifier = 112 + TimeDelay PropertyIdentifier = 113 + TimeOfActiveTimeReset PropertyIdentifier = 114 + TimeOfStateCountReset PropertyIdentifier = 115 + TimeSynchronizationRecipients PropertyIdentifier = 116 + Units PropertyIdentifier = 117 + UpdateInterval PropertyIdentifier = 118 + UtcOffset PropertyIdentifier = 119 + VendorIdentifier PropertyIdentifier = 120 + VendorName PropertyIdentifier = 121 + VtClassesSupported PropertyIdentifier = 122 + WeeklySchedule PropertyIdentifier = 123 + AttemptedSamples PropertyIdentifier = 124 + AverageValue PropertyIdentifier = 125 + BufferSize PropertyIdentifier = 126 + ClientCovIncrement PropertyIdentifier = 127 + CovResubscriptionInterval PropertyIdentifier = 128 + CurrentNotifyTime PropertyIdentifier = 129 + EventTimeStamps PropertyIdentifier = 130 + LogBuffer PropertyIdentifier = 131 + LogDeviceObjectProperty PropertyIdentifier = 132 + Enable PropertyIdentifier = 133 + LogInterval PropertyIdentifier = 134 + MaximumValue PropertyIdentifier = 135 + MinimumValue PropertyIdentifier = 136 + NotificationThreshold PropertyIdentifier = 137 + PreviousNotifyTime PropertyIdentifier = 138 + ProtocolRevision PropertyIdentifier = 139 + RecordsSinceNotification PropertyIdentifier = 140 + RecordCount PropertyIdentifier = 141 + StartTime PropertyIdentifier = 142 + StopTime PropertyIdentifier = 143 + StopWhenFull PropertyIdentifier = 144 + TotalRecordCount PropertyIdentifier = 145 + ValidSamples PropertyIdentifier = 146 + WindowInterval PropertyIdentifier = 147 + WindowSamples PropertyIdentifier = 148 + MaximumValueTimestamp PropertyIdentifier = 149 + MinimumValueTimestamp PropertyIdentifier = 150 + VarianceValue PropertyIdentifier = 151 + ActiveCovSubscriptions PropertyIdentifier = 152 + BackupFailureTimeout PropertyIdentifier = 153 + ConfigurationFiles PropertyIdentifier = 154 + DatabaseRevision PropertyIdentifier = 155 + DirectReading PropertyIdentifier = 156 + LastRestoreTime PropertyIdentifier = 157 + MaintenanceRequired PropertyIdentifier = 158 + MemberOf PropertyIdentifier = 159 + Mode PropertyIdentifier = 160 + OperationExpected PropertyIdentifier = 161 + Setting PropertyIdentifier = 162 + Silenced PropertyIdentifier = 163 + TrackingValue PropertyIdentifier = 164 + ZoneMembers PropertyIdentifier = 165 + LifeSafetyAlarmValues PropertyIdentifier = 166 + MaxSegmentsAccepted PropertyIdentifier = 167 + ProfileName PropertyIdentifier = 168 + AutoSlaveDiscovery PropertyIdentifier = 169 + ManualSlaveAddressBinding PropertyIdentifier = 170 + SlaveAddressBinding PropertyIdentifier = 171 + SlaveProxyEnable PropertyIdentifier = 172 + LastNotifyRecord PropertyIdentifier = 173 + ScheduleDefault PropertyIdentifier = 174 + AcceptedModes PropertyIdentifier = 175 + AdjustValue PropertyIdentifier = 176 + Count PropertyIdentifier = 177 + CountBeforeChange PropertyIdentifier = 178 + CountChangeTime PropertyIdentifier = 179 + CovPeriod PropertyIdentifier = 180 + InputReference PropertyIdentifier = 181 + LimitMonitoringInterval PropertyIdentifier = 182 + LoggingObject PropertyIdentifier = 183 + LoggingRecord PropertyIdentifier = 184 + Prescale PropertyIdentifier = 185 + PulseRate PropertyIdentifier = 186 + Scale PropertyIdentifier = 187 + ScaleFactor PropertyIdentifier = 188 + UpdateTime PropertyIdentifier = 189 + ValueBeforeChange PropertyIdentifier = 190 + ValueSet PropertyIdentifier = 191 + ValueChangeTime PropertyIdentifier = 192 + AlignIntervals PropertyIdentifier = 193 + IntervalOffset PropertyIdentifier = 195 + LastRestartReason PropertyIdentifier = 196 + LoggingType PropertyIdentifier = 197 + RestartNotificationRecipients PropertyIdentifier = 202 + TimeOfDeviceRestart PropertyIdentifier = 203 + TimeSynchronizationInterval PropertyIdentifier = 204 + Trigger PropertyIdentifier = 205 + UtcTimeSynchronizationRecipients PropertyIdentifier = 206 + NodeSubtype PropertyIdentifier = 207 + NodeType PropertyIdentifier = 208 + StructuredObjectList PropertyIdentifier = 209 + SubordinateAnnotations PropertyIdentifier = 210 + SubordinateList PropertyIdentifier = 211 + ActualShedLevel PropertyIdentifier = 212 + DutyWindow PropertyIdentifier = 213 + ExpectedShedLevel PropertyIdentifier = 214 + FullDutyBaseline PropertyIdentifier = 215 + RequestedShedLevel PropertyIdentifier = 218 + ShedDuration PropertyIdentifier = 219 + ShedLevelDescriptions PropertyIdentifier = 220 + ShedLevels PropertyIdentifier = 221 + StateDescription PropertyIdentifier = 222 + DoorAlarmState PropertyIdentifier = 226 + DoorExtendedPulseTime PropertyIdentifier = 227 + DoorMembers PropertyIdentifier = 228 + DoorOpenTooLongTime PropertyIdentifier = 229 + DoorPulseTime PropertyIdentifier = 230 + DoorStatus PropertyIdentifier = 231 + DoorUnlockDelayTime PropertyIdentifier = 232 + LockStatus PropertyIdentifier = 233 + MaskedAlarmValues PropertyIdentifier = 234 + SecuredStatus PropertyIdentifier = 235 + AbsenteeLimit PropertyIdentifier = 244 + AccessAlarmEvents PropertyIdentifier = 245 + AccessDoors PropertyIdentifier = 246 + AccessEvent PropertyIdentifier = 247 + AccessEventAuthenticationFactor PropertyIdentifier = 248 + AccessEventCredential PropertyIdentifier = 249 + AccessEventTime PropertyIdentifier = 250 + AccessTransactionEvents PropertyIdentifier = 251 + Accompaniment PropertyIdentifier = 252 + AccompanimentTime PropertyIdentifier = 253 + ActivationTime PropertyIdentifier = 254 + ActiveAuthenticationPolicy PropertyIdentifier = 255 + AssignedAccessRights PropertyIdentifier = 256 + AuthenticationFactors PropertyIdentifier = 257 + AuthenticationPolicyList PropertyIdentifier = 258 + AuthenticationPolicyNames PropertyIdentifier = 259 + AuthenticationStatus PropertyIdentifier = 260 + AuthorizationMode PropertyIdentifier = 261 + BelongsTo PropertyIdentifier = 262 + CredentialDisable PropertyIdentifier = 263 + CredentialStatus PropertyIdentifier = 264 + Credentials PropertyIdentifier = 265 + CredentialsInZone PropertyIdentifier = 266 + DaysRemaining PropertyIdentifier = 267 + EntryPoints PropertyIdentifier = 268 + ExitPoints PropertyIdentifier = 269 + ExpiryTime PropertyIdentifier = 270 + ExtendedTimeEnable PropertyIdentifier = 271 + FailedAttemptEvents PropertyIdentifier = 272 + FailedAttempts PropertyIdentifier = 273 + FailedAttemptsTime PropertyIdentifier = 274 + LastAccessEvent PropertyIdentifier = 275 + LastAccessPoint PropertyIdentifier = 276 + LastCredentialAdded PropertyIdentifier = 277 + LastCredentialAddedTime PropertyIdentifier = 278 + LastCredentialRemoved PropertyIdentifier = 279 + LastCredentialRemovedTime PropertyIdentifier = 280 + LastUseTime PropertyIdentifier = 281 + Lockout PropertyIdentifier = 282 + LockoutRelinquishTime PropertyIdentifier = 283 + MasterExemption PropertyIdentifier = 284 + MaxFailedAttempts PropertyIdentifier = 285 + Members PropertyIdentifier = 286 + MusterPoint PropertyIdentifier = 287 + NegativeAccessRules PropertyIdentifier = 288 + NumberOfAuthenticationPolicies PropertyIdentifier = 289 + OccupancyCount PropertyIdentifier = 290 + OccupancyCountAdjust PropertyIdentifier = 291 + OccupancyCountEnable PropertyIdentifier = 292 + OccupancyExemption PropertyIdentifier = 293 + OccupancyLowerLimit PropertyIdentifier = 294 + OccupancyLowerLimitEnforced PropertyIdentifier = 295 + OccupancyState PropertyIdentifier = 296 + OccupancyUpperLimit PropertyIdentifier = 297 + OccupancyUpperLimitEnforced PropertyIdentifier = 298 + PassbackExemption PropertyIdentifier = 299 + PassbackMode PropertyIdentifier = 300 + PassbackTimeout PropertyIdentifier = 301 + PositiveAccessRules PropertyIdentifier = 302 + ReasonForDisable PropertyIdentifier = 303 + SupportedFormats PropertyIdentifier = 304 + SupportedFormatClasses PropertyIdentifier = 305 + ThreatAuthority PropertyIdentifier = 306 + ThreatLevel PropertyIdentifier = 307 + TraceFlag PropertyIdentifier = 308 + TransactionNotificationClass PropertyIdentifier = 309 + UserExternalIdentifier PropertyIdentifier = 310 + UserInformationReference PropertyIdentifier = 311 + UserName PropertyIdentifier = 317 + UserType PropertyIdentifier = 318 + UsesRemaining PropertyIdentifier = 319 + ZoneFrom PropertyIdentifier = 320 + ZoneTo PropertyIdentifier = 321 + AccessEventTag PropertyIdentifier = 322 + GlobalIdentifier PropertyIdentifier = 323 + VerificationTime PropertyIdentifier = 326 + BaseDeviceSecurityPolicy PropertyIdentifier = 327 + DistributionKeyRevision PropertyIdentifier = 328 + DoNotHide PropertyIdentifier = 329 + KeySets PropertyIdentifier = 330 + LastKeyServer PropertyIdentifier = 331 + NetworkAccessSecurityPolicies PropertyIdentifier = 332 + PacketReorderTime PropertyIdentifier = 333 + SecurityPduTimeout PropertyIdentifier = 334 + SecurityTimeWindow PropertyIdentifier = 335 + SupportedSecurityAlgorithm PropertyIdentifier = 336 + UpdateKeySetTimeout PropertyIdentifier = 337 + BackupAndRestoreState PropertyIdentifier = 338 + BackupPreparationTime PropertyIdentifier = 339 + RestoreCompletionTime PropertyIdentifier = 340 + RestorePreparationTime PropertyIdentifier = 341 + BitMask PropertyIdentifier = 342 + BitText PropertyIdentifier = 343 + IsUtc PropertyIdentifier = 344 + GroupMembers PropertyIdentifier = 345 + GroupMemberNames PropertyIdentifier = 346 + MemberStatusFlags PropertyIdentifier = 347 + RequestedUpdateInterval PropertyIdentifier = 348 + CovuPeriod PropertyIdentifier = 349 + CovuRecipients PropertyIdentifier = 350 + EventMessageTexts PropertyIdentifier = 351 + EventMessageTextsConfig PropertyIdentifier = 352 + EventDetectionEnable PropertyIdentifier = 353 + EventAlgorithmInhibit PropertyIdentifier = 354 + EventAlgorithmInhibitRef PropertyIdentifier = 355 + TimeDelayNormal PropertyIdentifier = 356 + ReliabilityEvaluationInhibit PropertyIdentifier = 357 + FaultParameters PropertyIdentifier = 358 + FaultType PropertyIdentifier = 359 + LocalForwardingOnly PropertyIdentifier = 360 + ProcessIdentifierFilter PropertyIdentifier = 361 + SubscribedRecipients PropertyIdentifier = 362 + PortFilter PropertyIdentifier = 363 + AuthorizationExemptions PropertyIdentifier = 364 + AllowGroupDelayInhibit PropertyIdentifier = 365 + ChannelNumber PropertyIdentifier = 366 + ControlGroups PropertyIdentifier = 367 + ExecutionDelay PropertyIdentifier = 368 + LastPriority PropertyIdentifier = 369 + WriteStatus PropertyIdentifier = 370 + PropertyList PropertyIdentifier = 371 + SerialNumber PropertyIdentifier = 372 + BlinkWarnEnable PropertyIdentifier = 373 + DefaultFadeTime PropertyIdentifier = 374 + DefaultRampRate PropertyIdentifier = 375 + DefaultStepIncrement PropertyIdentifier = 376 + EgressTime PropertyIdentifier = 377 + InProgress PropertyIdentifier = 378 + InstantaneousPower PropertyIdentifier = 379 + LightingCommand PropertyIdentifier = 380 + LightingCommandDefaultPriority PropertyIdentifier = 381 + MaxActualValue PropertyIdentifier = 382 + MinActualValue PropertyIdentifier = 383 + Power PropertyIdentifier = 384 + Transition PropertyIdentifier = 385 + EgressActive PropertyIdentifier = 386 + InterfaceValue PropertyIdentifier = 387 + FaultHighLimit PropertyIdentifier = 388 + FaultLowLimit PropertyIdentifier = 389 + LowDiffLimit PropertyIdentifier = 390 + StrikeCount PropertyIdentifier = 391 + TimeOfStrikeCountReset PropertyIdentifier = 392 + DefaultTimeout PropertyIdentifier = 393 + InitialTimeout PropertyIdentifier = 394 + LastStateChange PropertyIdentifier = 395 + StateChangeValues PropertyIdentifier = 396 + TimerRunning PropertyIdentifier = 397 + TimerState PropertyIdentifier = 398 + ApduLength PropertyIdentifier = 399 + IpAddress PropertyIdentifier = 400 + IpDefaultGateway PropertyIdentifier = 401 + IpDhcpEnable PropertyIdentifier = 402 + IpDhcpLeaseTime PropertyIdentifier = 403 + IpDhcpLeaseTimeRemaining PropertyIdentifier = 404 + IpDhcpServer PropertyIdentifier = 405 + IpDnsServer PropertyIdentifier = 406 + BacnetIpGlobalAddress PropertyIdentifier = 407 + BacnetIpMode PropertyIdentifier = 408 + BacnetIpMulticastAddress PropertyIdentifier = 409 + BacnetIpNatTraversal PropertyIdentifier = 410 + IpSubnetMask PropertyIdentifier = 411 + BacnetIpUdpPort PropertyIdentifier = 412 + BbmdAcceptFdRegistrations PropertyIdentifier = 413 + BbmdBroadcastDistributionTable PropertyIdentifier = 414 + BbmdForeignDeviceTable PropertyIdentifier = 415 + ChangesPending PropertyIdentifier = 416 + Command PropertyIdentifier = 417 + FdBbmdAddress PropertyIdentifier = 418 + FdSubscriptionLifetime PropertyIdentifier = 419 + LinkSpeed PropertyIdentifier = 420 + LinkSpeeds PropertyIdentifier = 421 + LinkSpeedAutonegotiate PropertyIdentifier = 422 + MacAddress PropertyIdentifier = 423 + NetworkInterfaceName PropertyIdentifier = 424 + NetworkNumber PropertyIdentifier = 425 + NetworkNumberQuality PropertyIdentifier = 426 + NetworkType PropertyIdentifier = 427 + RoutingTable PropertyIdentifier = 428 + VirtualMacAddressTable PropertyIdentifier = 429 + CommandTimeArray PropertyIdentifier = 430 + CurrentCommandPriority PropertyIdentifier = 431 + LastCommandTime PropertyIdentifier = 432 + ValueSource PropertyIdentifier = 433 + ValueSourceArray PropertyIdentifier = 434 + BacnetIpv6Mode PropertyIdentifier = 435 + Ipv6Address PropertyIdentifier = 436 + Ipv6PrefixLength PropertyIdentifier = 437 + BacnetIpv6UdpPort PropertyIdentifier = 438 + Ipv6DefaultGateway PropertyIdentifier = 439 + BacnetIpv6MulticastAddress PropertyIdentifier = 440 + Ipv6DnsServer PropertyIdentifier = 441 + Ipv6AutoAddressingEnable PropertyIdentifier = 442 + Ipv6DhcpLeaseTime PropertyIdentifier = 443 + Ipv6DhcpLeaseTimeRemaining PropertyIdentifier = 444 + Ipv6DhcpServer PropertyIdentifier = 445 + Ipv6ZoneIndex PropertyIdentifier = 446 + AssignedLandingCalls PropertyIdentifier = 447 + CarAssignedDirection PropertyIdentifier = 448 + CarDoorCommand PropertyIdentifier = 449 + CarDoorStatus PropertyIdentifier = 450 + CarDoorText PropertyIdentifier = 451 + CarDoorZone PropertyIdentifier = 452 + CarDriveStatus PropertyIdentifier = 453 + CarLoad PropertyIdentifier = 454 + CarLoadUnits PropertyIdentifier = 455 + CarMode PropertyIdentifier = 456 + CarMovingDirection PropertyIdentifier = 457 + CarPosition PropertyIdentifier = 458 + ElevatorGroup PropertyIdentifier = 459 + EnergyMeter PropertyIdentifier = 460 + EnergyMeterRef PropertyIdentifier = 461 + EscalatorMode PropertyIdentifier = 462 + FloorText PropertyIdentifier = 464 + GroupId PropertyIdentifier = 465 + GroupMode PropertyIdentifier = 467 + HigherDeck PropertyIdentifier = 468 + InstallationId PropertyIdentifier = 469 + LandingCalls PropertyIdentifier = 470 + LandingCallControl PropertyIdentifier = 471 + LandingDoorStatus PropertyIdentifier = 472 + LowerDeck PropertyIdentifier = 473 + MachineRoomId PropertyIdentifier = 474 + MakingCarCall PropertyIdentifier = 475 + NextStoppingFloor PropertyIdentifier = 476 + OperationDirection PropertyIdentifier = 477 + PassengerAlarm PropertyIdentifier = 478 + PowerMode PropertyIdentifier = 479 + RegisteredCarCall PropertyIdentifier = 480 + ActiveCovMultipleSubscriptions PropertyIdentifier = 481 + ProtocolLevel PropertyIdentifier = 482 + ReferencePort PropertyIdentifier = 483 + DeployedProfileLocation PropertyIdentifier = 484 + ProfileLocation PropertyIdentifier = 485 + Tags PropertyIdentifier = 486 + SubordinateNodeTypes PropertyIdentifier = 487 + SubordinateRelationships PropertyIdentifier = 489 + SubordinateTags PropertyIdentifier = 488 + DefaultSubordinateRelationship PropertyIdentifier = 490 + Represents PropertyIdentifier = 491 + DefaultPresentValue PropertyIdentifier = 492 + PresentStage PropertyIdentifier = 493 + Stages PropertyIdentifier = 494 + StageNames PropertyIdentifier = 495 + TargetReferences PropertyIdentifier = 496 + FaultSignals PropertyIdentifier = 463 +) diff --git a/client.go b/client.go index 0b78fc5..98f6c67 100644 --- a/client.go +++ b/client.go @@ -10,10 +10,13 @@ type Client interface { } func NewClient(address string, port int) (Client, error) { + return &client{}, nil } func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) { + return nil, nil } func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error { + return nil } diff --git a/encoding/date_time.go b/encoding/date_time.go new file mode 100644 index 0000000..4e561cb --- /dev/null +++ b/encoding/date_time.go @@ -0,0 +1,100 @@ +package encoding + +import "time" + +type DateTime struct { + Date time.Time +} + +func DecodeApplicationDate(buf []byte, offset int) (int, time.Time) { + len, tagNum := decodeTagNumber(buf, offset) + if tagNum == byte(Date) { + len1, date := decodeDate(buf, offset+len) + return len + len1, date + } + return -1, time.Time{} +} + +func DecodeApplicationTime(buf []byte, offset int) (int, time.Time) { + len, tagNum := decodeTagNumber(buf, offset) + if tagNum == byte(Time) { + len1, btime := decodeBACnetTime(buf, offset+len) + return len + len1, btime + } + return -1, time.Time{} +} + +func decodeDate(buf []byte, offset int) (int, time.Time) { + year := buf[offset] + month := buf[offset+1] + day := buf[offset+2] + wday := buf[offset+3] + if month == 0xFF && day == 0xFF && wday == 0xFF && year == 0xFF { + return 4, time.Time{} + } + return 4, time.Date(int(year)+1900, time.Month(month), int(day), 0, 0, 0, 0, nil) +} + +func decodeDateSafe(buf []byte, offset, lenVal int) (int, time.Time) { + if lenVal != 4 { + return lenVal, time.Time{} + } + return decodeDate(buf, offset) +} + +func decodeBACnetTime(buf []byte, offset int) (int, time.Time) { + hour := buf[offset] + min := buf[offset+1] + sec := buf[offset+2] + hundredths := buf[offset+3] + if hour == 0xFF && min == 0xFF && sec == 0xFF && hundredths == 0xFF { + return 4, time.Time{} + } + if hundredths > 100 { + hundredths = 0 + } + return 4, time.Date(0, 0, 0, int(hour), int(min), int(sec), int(hundredths*10), nil) +} + +func decodeBACnetTimeSafe(buf []byte, offset int, lenVal int) (int, time.Time) { + if lenVal != 4 { + return lenVal, time.Time{} + } + return decodeBACnetTime(buf, offset) +} + +func (dt *DateTime) Decode(buf []byte, offset int) int { + len, date := DecodeApplicationDate(buf, offset) + len1, ttime := DecodeApplicationTime(buf, offset+len) + dt.Date = ttime.AddDate(date.Year(), int(date.Month()), date.Day()) + return len + len1 +} + +func (dt *DateTime) Encode() []byte { + return append(encodeApplicationDate(dt.Date), encodeApplicationTime(dt.Date)...) +} + +func encodeApplicationDate(date time.Time) []byte { + return append(EncodeTag(Date, false, 4), encodeBacnetDate(date)...) +} +func encodeApplicationTime(date time.Time) []byte { + return append(EncodeTag(Time, false, 4), encodeBacnetTime(date)...) +} + +func encodeBacnetDate(date time.Time) []byte { + data := make([]byte, 4) + data[0] = byte(date.Year() - 1900) + data[1] = byte(date.Month()) + data[2] = byte(date.Day()) + data[3] = byte(date.Weekday()) + return data +} + +func encodeBacnetTime(date time.Time) []byte { + data := make([]byte, 4) + data[0] = byte(date.Hour()) + data[1] = byte(date.Minute()) + data[2] = byte(date.Second()) + data[3] = byte(date.Nanosecond() / 10) + return data +} diff --git a/property.go b/property.go new file mode 100644 index 0000000..08d5908 --- /dev/null +++ b/property.go @@ -0,0 +1,8 @@ +package bacnet + +type PropertyValue struct { + Identifier PropertyIdentifier + Arrayindex *uint32 + Value uint32 + Priority uint32 +} From fc6b4756d54baaee27db362c8db0b817c15702cb Mon Sep 17 00:00:00 2001 From: SammyOina Date: Wed, 30 Aug 2023 14:37:44 +0300 Subject: [PATCH 07/25] add who is Signed-off-by: SammyOina --- encoding/date_time.go | 19 ++------------- encoding/encoding.go | 15 ++++++++++++ general.go | 22 +++++++++++++++++ object.go | 6 ++--- whois.go | 55 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 general.go create mode 100644 whois.go diff --git a/encoding/date_time.go b/encoding/date_time.go index 4e561cb..a1a7250 100644 --- a/encoding/date_time.go +++ b/encoding/date_time.go @@ -2,10 +2,6 @@ package encoding import "time" -type DateTime struct { - Date time.Time -} - func DecodeApplicationDate(buf []byte, offset int) (int, time.Time) { len, tagNum := decodeTagNumber(buf, offset) if tagNum == byte(Date) { @@ -63,21 +59,10 @@ func decodeBACnetTimeSafe(buf []byte, offset int, lenVal int) (int, time.Time) { return decodeBACnetTime(buf, offset) } -func (dt *DateTime) Decode(buf []byte, offset int) int { - len, date := DecodeApplicationDate(buf, offset) - len1, ttime := DecodeApplicationTime(buf, offset+len) - dt.Date = ttime.AddDate(date.Year(), int(date.Month()), date.Day()) - return len + len1 -} - -func (dt *DateTime) Encode() []byte { - return append(encodeApplicationDate(dt.Date), encodeApplicationTime(dt.Date)...) -} - -func encodeApplicationDate(date time.Time) []byte { +func EncodeApplicationDate(date time.Time) []byte { return append(EncodeTag(Date, false, 4), encodeBacnetDate(date)...) } -func encodeApplicationTime(date time.Time) []byte { +func EncodeApplicationTime(date time.Time) []byte { return append(EncodeTag(Time, false, 4), encodeBacnetTime(date)...) } diff --git a/encoding/encoding.go b/encoding/encoding.go index 2188e7a..e7a64b2 100644 --- a/encoding/encoding.go +++ b/encoding/encoding.go @@ -61,3 +61,18 @@ func EncodeSigned(value int32) []byte { return buf.Bytes() } } + +func EncodeContextUnsigned(tagNum BACnetApplicationTag, val uint32) []byte { + len := 0 + switch { + case val < 0x100: + len = 1 + case val < 0x10000: + len = 2 + case val < 0x1000000: + len = 3 + default: + len = 4 + } + return append(EncodeTag(tagNum, true, len), EncodeUnsigned(val)...) +} diff --git a/general.go b/general.go new file mode 100644 index 0000000..3a5d08d --- /dev/null +++ b/general.go @@ -0,0 +1,22 @@ +package bacnet + +import ( + "time" + + "github.com/absmach/bacnet/encoding" +) + +type DateTime struct { + Date time.Time +} + +func (dt *DateTime) Decode(buf []byte, offset int) int { + len, date := encoding.DecodeApplicationDate(buf, offset) + len1, ttime := encoding.DecodeApplicationTime(buf, offset+len) + dt.Date = ttime.AddDate(date.Year(), int(date.Month()), date.Day()) + return len + len1 +} + +func (dt DateTime) Encode() []byte { + return append(encoding.EncodeApplicationDate(dt.Date), encoding.EncodeApplicationTime(dt.Date)...) +} diff --git a/object.go b/object.go index 9c2beba..f585e8e 100644 --- a/object.go +++ b/object.go @@ -33,19 +33,19 @@ func (oi *ObjectIdentifier) DecodeContext(buf []byte, offset, apdu_len int, tagN return -1 } -func (oi *ObjectIdentifier) Encode() []byte { +func (oi ObjectIdentifier) Encode() []byte { value := uint32(oi.Type)&encoding.MaxObject< length { + len1, decVal := encoding.DecodeUnsigned(buf, offset+length, int(lenVal)) + length += len1 + + if decVal <= encoding.MaxInstance { + w.LowLimit = &decVal + } + + if apduLen > length { + len1, tagNum, lenVal := encoding.DecodeTagNumberAndValue(buf, offset+length) + length += len1 + if tagNum != 1 { + return -1 + } + if apduLen > length { + len1, _ := encoding.DecodeUnsigned(buf, offset+length, int(lenVal)) + length += len1 + if decVal <= encoding.MaxInstance { + w.HighLimit = &decVal + } + } else { + return -1 + } + } else { + return -1 + } + } else { + return -1 + } + return length +} + +func (w WhoIs) Encode() []byte { + if w.LowLimit != nil && *w.LowLimit <= encoding.MaxInstance && + w.HighLimit != nil && *w.HighLimit <= encoding.MaxInstance { + return append(encoding.EncodeContextUnsigned(0, *w.LowLimit), encoding.EncodeContextUnsigned(1, *w.HighLimit)...) + } + return []byte{} +} From 11555687a1e98b454ce1c700af9d86f189a859c7 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Fri, 1 Sep 2023 01:47:01 +0300 Subject: [PATCH 08/25] add npdu Signed-off-by: SammyOina --- encoding/decoding.go | 6 ++ internal/bitarray.go | 52 ++++++++++ network.go | 124 +++++++++++++++++++++++ npdu.go | 231 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 413 insertions(+) create mode 100644 internal/bitarray.go create mode 100644 network.go create mode 100644 npdu.go diff --git a/encoding/decoding.go b/encoding/decoding.go index a3c36c5..bcdeb14 100644 --- a/encoding/decoding.go +++ b/encoding/decoding.go @@ -7,3 +7,9 @@ func DecodeUnsigned(buffer []byte, offset, len int) (int, uint32) { } return len, value } + +func DecodeOctetString(buf []byte, offset, lenVal int) (int, []byte) { + tmp := make([]byte, lenVal) + copy(tmp, buf[offset:offset+lenVal]) + return len(tmp), tmp +} diff --git a/internal/bitarray.go b/internal/bitarray.go new file mode 100644 index 0000000..ff03c23 --- /dev/null +++ b/internal/bitarray.go @@ -0,0 +1,52 @@ +package internal + +import "errors" + +var errBitArrayLen = errors.New("bit array length must be 8 to convert to byte") + +type BitArray struct { + bits []bool +} + +func NewBitArray(length int) *BitArray { + return &BitArray{ + bits: make([]bool, length), + } +} + +func (ba *BitArray) Set(index int, value bool) { + if index >= 0 && index < len(ba.bits) { + ba.bits[index] = value + } +} + +func (ba *BitArray) Get(index int) bool { + if index >= 0 && index < len(ba.bits) { + return ba.bits[index] + } + return false +} + +func (ba *BitArray) ToByte() (byte, error) { + // Ensure the length of the bit array is 8 to convert to a byte. + if len(ba.bits) != 8 { + return 0, errBitArrayLen + } + + var byteValue byte + for j := 0; j < 8; j++ { + if ba.bits[j] { + byteValue |= 1 << uint(7-j) + } + } + + return byteValue, nil +} + +func NewBitArrayFromByte(byteValue byte) *BitArray { + bitArray := NewBitArray(8) + for j := 0; j < 8; j++ { + bitArray.Set(j, byteValue&(1<>8), byte(port)) + case Ethernet: + parts := strings.Split(addr1, "-") + for _, part := range parts { + val := byte(0) + fmt.Sscanf(part, "%d", &val) + addr.MacAddress = append(addr.MacAddress, val) + } + } + } + case ObjectIdentifier: + if *netType == IPV4 { + addr.MacAddress = make([]byte, 6) + binary.LittleEndian.PutUint64(addr.MacAddress, uint64(addr1.Instance)) + } + } + + return addr +} + +func (ba *BACnetAddress) IPAndPort() (string, int) { + ip := fmt.Sprintf("%d.%d.%d.%d", ba.MacAddress[0], ba.MacAddress[1], ba.MacAddress[2], ba.MacAddress[3]) + port := int(ba.MacAddress[4])<<8 + int(ba.MacAddress[5]) + return ip, port +} + +func (ba *BACnetAddress) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(UnsignedInt) { + leng += leng1 + leng1, ba.NetworkNumber = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(OctetString) { + leng += leng1 + leng1, ba.MacAddress = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + return leng +} diff --git a/npdu.go b/npdu.go new file mode 100644 index 0000000..65b7bea --- /dev/null +++ b/npdu.go @@ -0,0 +1,231 @@ +package bacnet + +import ( + "encoding/binary" + "errors" + "log" + + "github.com/absmach/bacnet/internal" +) + +type NPDU struct { + Version uint8 // Always one. + Control NPDUControlInformation + DNET uint16 + DLEN uint8 + DADR []byte + Destination *BACnetAddress + SNET uint16 + SLEN uint8 + SADR []byte + Source *BACnetAddress + MessageType byte + HopCount byte + VendorID uint16 +} + +type NPDUControlInformation struct { + control internal.BitArray +} + +func NewNPDUControlInformation() *NPDUControlInformation { + return &NPDUControlInformation{ + control: *internal.NewBitArray(8), + } +} + +func (nci *NPDUControlInformation) IsNetworkLayerMessage() bool { + return nci.control.Get(0) +} + +func (nci *NPDUControlInformation) SetNetworkLayerMessage(a bool) { + nci.control.Set(0, a) +} + +func (nci *NPDUControlInformation) IsDestinationSpecifier() bool { + return nci.control.Get(2) +} + +func (nci *NPDUControlInformation) SetDestinationSpecifier(a bool) { + nci.control.Set(2, a) +} + +func (nci *NPDUControlInformation) IsSourceSpecifier() bool { + return nci.control.Get(4) +} + +func (nci *NPDUControlInformation) SetSourceSpecifier(a bool) { + nci.control.Set(4, a) +} + +func (nci *NPDUControlInformation) IsDataExpectingReply() bool { + return nci.control.Get(5) +} + +func (nci *NPDUControlInformation) SetDataExpectingReply(a bool) { + nci.control.Set(5, a) +} + +func (nci *NPDUControlInformation) NetworkPriority() (NetworkPriority, error) { + if !nci.control.Get(6) && !nci.control.Get(7) { + return NormalMessage, nil + } else if !nci.control.Get(6) && nci.control.Get(7) { + return UrgentMessage, nil + } else if nci.control.Get(6) && !nci.control.Get(7) { + return CriticalEquipmentMessage, nil + } else if nci.control.Get(6) && nci.control.Get(7) { + return LifeSafetyMessage, nil + } + return 0, errors.New("invalid network priority") +} + +func (nci *NPDUControlInformation) SetNetworkPriority(a NetworkPriority) error { + switch a { + case NormalMessage: + nci.control.Set(6, false) + nci.control.Set(7, false) + case UrgentMessage: + nci.control.Set(6, false) + nci.control.Set(7, true) + case CriticalEquipmentMessage: + nci.control.Set(6, true) + nci.control.Set(7, false) + case LifeSafetyMessage: + nci.control.Set(6, true) + nci.control.Set(7, true) + default: + return errors.New("invalid network priority") + } + return nil +} + +func (nci *NPDUControlInformation) Encode() ([]byte, error) { + b, err := nci.control.ToByte() + if err != nil { + return []byte{}, err + } + return []byte{b}, nil +} + +func (nci *NPDUControlInformation) Decode(buffer []byte, offset int) int { + if offset < len(buffer) { + nci.control = *internal.NewBitArrayFromByte(buffer[offset]) + return 1 + } + return 0 +} + +func NewNPDU(destination *BACnetAddress, source *BACnetAddress, hopCount int, vendorID int) *NPDU { + npdu := &NPDU{ + Version: 1, + Control: *NewNPDUControlInformation(), + Destination: destination, + Source: source, + HopCount: uint8(hopCount), + VendorID: uint16(vendorID), + } + + if destination != nil && destination.NetworkNumber > 0 { + npdu.Control.SetDestinationSpecifier(true) + npdu.DNET = uint16(destination.NetworkNumber) + npdu.DLEN = uint8(len(destination.MacAddress)) + npdu.DADR = destination.MacAddress + } + + if source != nil && source.NetworkNumber > 0 && source.NetworkNumber < 0xFFFF { + npdu.Control.SetSourceSpecifier(true) + npdu.SNET = uint16(source.NetworkNumber) + npdu.SLEN = uint8(len(source.MacAddress)) + npdu.SADR = source.MacAddress + } + + return npdu +} + +func (npdu *NPDU) Encode() ([]byte, error) { + buffer := make([]byte, 0) + buffer = append(buffer, npdu.Version) + ctrlBuf, err := npdu.Control.Encode() + if err != nil { + return buffer, err + } + buffer = append(buffer, ctrlBuf...) + + if npdu.Control.IsDestinationSpecifier() { + buffer = append(buffer, uint8(npdu.DNET>>8), uint8(npdu.DNET&0xFF)) + if npdu.DNET == 0xFFFF { + buffer = append(buffer, 0x00) + } else { + buffer = append(buffer, npdu.DLEN) + buffer = append(buffer, npdu.DADR...) + } + } + + if npdu.Control.IsSourceSpecifier() { + buffer = append(buffer, uint8(npdu.SNET>>8), uint8(npdu.SNET&0xFF)) + buffer = append(buffer, npdu.SLEN) + buffer = append(buffer, npdu.SADR...) + } + + if npdu.Control.IsDestinationSpecifier() { + buffer = append(buffer, npdu.HopCount) + } + + if npdu.Control.IsNetworkLayerMessage() { + buffer = append(buffer, npdu.MessageType) + if npdu.MessageType >= 0x80 && npdu.MessageType <= 0xFF { + buffer = append(buffer, uint8(npdu.VendorID>>8), uint8(npdu.VendorID&0xFF)) + } + } + + return buffer, nil +} + +func (npdu *NPDU) Decode(buffer []byte, offset int) int { + length := 0 + version := buffer[offset] // always 1!!!! + length++ + if version != npdu.Version { + log.Println("Received something else!") + return -1 + } + + npdu.Control = *NewNPDUControlInformation() + length += npdu.Control.Decode(buffer, offset+length) + + if npdu.Control.IsDestinationSpecifier() { + npdu.DNET = binary.BigEndian.Uint16(buffer[offset+length : offset+length+2]) + length += 2 + npdu.DLEN = buffer[offset+length] + length++ + npdu.DADR = buffer[offset+length : offset+length+int(npdu.DLEN)] + length += int(npdu.DLEN) + npdu.Destination = NewBACnetAddress(uint32(npdu.DNET), npdu.DADR, "", nil) + } + + if npdu.Control.IsSourceSpecifier() { + npdu.SNET = binary.BigEndian.Uint16(buffer[offset+length : offset+length+2]) + length += 2 + npdu.SLEN = buffer[offset+length] + length++ + npdu.SADR = buffer[offset+length : offset+length+int(npdu.SLEN)] + length += int(npdu.SLEN) + npdu.Source = NewBACnetAddress(uint32(npdu.SNET), npdu.SADR, "", nil) + } + + if npdu.Control.IsDestinationSpecifier() { + npdu.HopCount = buffer[offset+length] + length++ + } + + if npdu.Control.IsNetworkLayerMessage() { + npdu.MessageType = buffer[offset+length] + length++ + if npdu.MessageType >= 0x80 { + npdu.VendorID = binary.BigEndian.Uint16(buffer[offset+length : offset+length+2]) + length += 2 + } + } + + return length +} From b6107649c17cb31702187e11ec01e2774abfdea0 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 4 Sep 2023 22:53:58 +0300 Subject: [PATCH 09/25] testing whoIs Signed-off-by: SammyOina --- example/whois.go | 58 ++++++++++++++++ iam.go | 170 +++++++++++++++++++++++++++++++++++++++++++++++ object.go | 68 ++++++++++++++++++- 3 files changed, 294 insertions(+), 2 deletions(-) create mode 100644 example/whois.go create mode 100644 iam.go diff --git a/example/whois.go b/example/whois.go new file mode 100644 index 0000000..2b18b6e --- /dev/null +++ b/example/whois.go @@ -0,0 +1,58 @@ +package main + +import ( + "fmt" + "net" + "time" + + "github.com/absmach/bacnet" +) + +func main() { + var highlim, lowLim uint32 = 4194303, 0 + whi := bacnet.WhoIs{HighLimit: &highlim, LowLimit: &lowLim} + whoIsRequest := whi.Encode() + + // Define the BACnet broadcast address (255.255.255.255:47808) + remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.255:47809") + if err != nil { + fmt.Println("Error resolving remote address:", err) + return + } + + // Create a UDP connection + conn, err := net.DialUDP("udp", nil, remoteAddr) + if err != nil { + fmt.Println("Error creating UDP connection:", err) + return + } + defer conn.Close() + + // Send the WhoIsRequest packet + _, err = conn.Write(whoIsRequest) + if err != nil { + fmt.Println("Error sending WhoIsRequest:", err) + return + } + + // Wait for responses + buffer := make([]byte, 1500) + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Set a timeout for responses + + for { + n, _, err := conn.ReadFromUDP(buffer) + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + // Timeout reached, no more responses + fmt.Println("No more responses received.") + break + } + fmt.Println("Error reading response:", err) + break + } + + // Process the response (you'll need to parse BACnet responses here) + response := buffer[:n] + fmt.Printf("Received response: %X\n", response) + } +} diff --git a/iam.go b/iam.go new file mode 100644 index 0000000..bfad5be --- /dev/null +++ b/iam.go @@ -0,0 +1,170 @@ +package bacnet + +/* +import ( + "errors" + + "github.com/absmach/bacnet/encoding" +) + +type IAmRequest struct { + IamDeviceIdentifier ObjectIdentifier + MaxAPDULengthAccepted uint32 + SegmentationSupported int + VendorID uint32 +} + +func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, error) { + leng := 0 + iam.IamDeviceIdentifier = ObjectIdentifier{} + // OBJECT ID - object id + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + + if tagNumber != byte(BACnetObjectIdentifier) { + return -1, errors.New("Invalid tag number") + } + + leng = iam.IamDeviceIdentifier.Decode(buffer, offset+leng, int(lenValue)) + + if iam.IamDeviceIdentifier.Type != ObjectTypeDevice { + // Handle error or log message + return -1, errors.New("Got Iam from no device") + } + + // MAX APDU - unsigned + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber != byte(UnsignedInt) { + return -1, errors.New("Invalid tag number") + } + + leng1, decodedValue := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + iam.MaxAPDULengthAccepted = decodedValue + + // Segmentation - enumerated + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber != byte(Enumerated) { + return -1, errors.New("Invalid tag number") + } + segmentationSupported, err := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, SegmentationSupported) + if err != nil { + return -1, err + } + leng += leng1 + iam.SegmentationSupported = segmentationSupported + + // Vendor ID - unsigned16 + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + + if tagNumber != byte(UnsignedInt) { + return -1, errors.New("Invalid tag number") + } + + leng1, decodedValue = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + if err != nil { + return -1, err + } + + leng += leng1 + if decodedValue > 0xFFFF { + return -1, errors.New("Value exceeds 0xFFFF") + } + iam.VendorID = decodedValue + + return leng, nil +} + +func (iam *IAmRequest) ASN1Encode() []byte { + tmp := iam.IamDeviceIdentifier.Encode() + return append(append(append(append([]byte{}, encoding.EncodeTag(encoding.BACnetApplicationTag(BACnetObjectIdentifier), false, len(tmp))...), tmp...), encoding.EncodeApplicationUnsigned(iam.MaxAPDULengthAccepted)...), encoding.EncodeApplicationEnumerated(iam.SegmentationSupported, SegmentationSupported), encoding.EncodeApplicationUnsigned(iam.VendorID)...) +} + +type YouAreRequest struct { + VendorID uint32 + ModelName string + SerialNumber string + DeviceIdentifier ObjectIdentifier + DeviceMACAddress []byte +} + +func (youAre *YouAreRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, error) { + leng := 0 + + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(UnsignedInt) { + leng1, decodedValue := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + youAre.VendorID = decodedValue + } else { + return -1, errors.New("Invalid tag number") + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(CharacterString) { + decodedValue, err := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, lenValue) + if err != nil { + return -1, err + } + leng += decodedValue + youAre.ModelName = decodedValue + } else { + return -1, errors.New("Invalid tag number") + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(CharacterString) { + decodedValue, err := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, lenValue) + if err != nil { + return -1, err + } + leng += decodedValue + youAre.SerialNumber = decodedValue + } else { + return -1, errors.New("Invalid tag number") + } + + if leng < apduLen { + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(BACnetObjectIdentifier) { + leng += leng1 + youAre.DeviceIdentifier = ObjectIdentifier{} + leng = youAre.DeviceIdentifier.Decode(buffer, offset+leng, int(lenValue)) + } + } + + if leng < apduLen { + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(OctetString) { + leng += leng1 + leng1, decodedValue := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + leng += leng1 + youAre.DeviceMACAddress = decodedValue + } + } + + return leng, nil +} + +func (youAre *YouAreRequest) ASN1Encode() []byte { + buffer := append(append(append([]byte{}, encoding.EncodeApplicationUnsigned(youAre.VendorID)...), + encoding.EncodeApplicationCharacterString(youAre.ModelName)...), + encoding.EncodeApplicationCharacterString(youAre.SerialNumber)...) + + if youAre.DeviceIdentifier != (ObjectIdentifier{}) { + buffer = append(buffer, youAre.DeviceIdentifier.EncodeApp()...) + } + + if len(youAre.DeviceMACAddress) > 0 { + buffer = append(buffer, encoding.EncodeApplicationOctetString(youAre.DeviceMACAddress, 0, len(youAre.DeviceMACAddress))...) + } + + return buffer +} +*/ diff --git a/object.go b/object.go index f585e8e..ffc4b59 100644 --- a/object.go +++ b/object.go @@ -10,19 +10,83 @@ type ObjectInstance uint32 type ObjectType uint16 +const ( + AnalogInput ObjectType = iota + AnalogOutput + AnalogValue + BinaryInput + BinaryOutput + BinaryValue + Calendar + ObjectTypeCommand + ObjectTypeDevice + EventEnrollment + File + Group + Loop + MultiStateInput + MultiStateOutput + ObjectTypeNotificationClass + Program + Schedule + Averaging + MultiStateValue + TrendLog + LifeSafetyPoint + LifeSafetyZone + Accumulator + PulseConverter + EventLog + GlobalGroup + TrendLogMultiple + LoadControl + StructuredView + AccessDoor + Timer + AccessCredential + AccessPoint + AccessRights + AccessUser + AccessZone + CredentialDataInput + NetworkSecurity + BitStringValue + CharacterStringValue + DatePatternValue + DateValue + DateTimePatternValue + DateTimeValue + IntegerValue + LargeAnalogValue + OctetStringValue + PositiveIntegerValue + TimePatternValue + TimeValue + NotificationForwarder + AlertEnrollment + Channel + LightingOutput + BinaryLightingOutput + NetworkPort + ObjectTypeElevatorGroup + Escalator + Lift + Staging +) + type ObjectIdentifier struct { Type ObjectType Instance ObjectInstance } -func (oi *ObjectIdentifier) Decode(buf []byte, offset, apdu_len int) int { +func (oi *ObjectIdentifier) Decode(buf []byte, offset, apdulen int) int { len, val := encoding.DecodeUnsigned(buf, offset, 4) oi.Instance = ObjectInstance(val) & ObjectInstance(encoding.MaxInstance) oi.Type = ObjectType(val >> encoding.InstanceBits & encoding.MaxObject) return len } -func (oi *ObjectIdentifier) DecodeContext(buf []byte, offset, apdu_len int, tagNumber byte) int { +func (oi *ObjectIdentifier) DecodeContext(buf []byte, offset, apdulen int, tagNumber byte) int { len := 0 if encoding.DecodeIsCOntextTag(buf, offset+len, tagNumber) { len1, _, lenVal := encoding.DecodeTagNumberAndValue(buf, offset+len) From 12b76cc786d81fdff5442a72be4b18800bad5b38 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 5 Sep 2023 16:40:40 +0300 Subject: [PATCH 10/25] add apdu add bvlc update npdu add transport Signed-off-by: SammyOina --- apdu.go | 165 +++++++++++++++++++++++++++++++++++++++++ base.go | 11 --- bvlc.go | 110 +++++++++++++++++++++++++++ example/whois.go | 46 ++++++++++-- npdu.go | 13 +++- transport/transport.go | 7 ++ 6 files changed, 332 insertions(+), 20 deletions(-) create mode 100644 apdu.go create mode 100644 bvlc.go create mode 100644 transport/transport.go diff --git a/apdu.go b/apdu.go new file mode 100644 index 0000000..83b1628 --- /dev/null +++ b/apdu.go @@ -0,0 +1,165 @@ +package bacnet + +type BACnetConfirmedServiceChoice int + +type BACnetUnconfirmedServiceChoice int + +const ( + // Alarm and Event Services + AcknowledgeAlarm BACnetConfirmedServiceChoice = 0 + ConfirmedCovNotification BACnetConfirmedServiceChoice = 1 + ConfirmedCovNotificationMultiple BACnetConfirmedServiceChoice = 31 + ConfirmedEventNotification BACnetConfirmedServiceChoice = 2 + GetAlarmSummary BACnetConfirmedServiceChoice = 3 + GetEnrollmentSummary BACnetConfirmedServiceChoice = 4 + GetEventInformation BACnetConfirmedServiceChoice = 29 + LifeSafetyOperation BACnetConfirmedServiceChoice = 27 + SubscribeCov BACnetConfirmedServiceChoice = 5 + SubscribeCovProperty BACnetConfirmedServiceChoice = 28 + SubscribeCovPropertyMultiple BACnetConfirmedServiceChoice = 30 + // File Access Services + AtomicReadFile BACnetConfirmedServiceChoice = 6 + AtomicWriteFile BACnetConfirmedServiceChoice = 7 + // Object Access Services + AddListElement BACnetConfirmedServiceChoice = 8 + RemoveListElement BACnetConfirmedServiceChoice = 9 + CreateObject BACnetConfirmedServiceChoice = 10 + DeleteObject BACnetConfirmedServiceChoice = 11 + ReadProperty BACnetConfirmedServiceChoice = 12 + // SERVICE_CONFIRMED_READ_CONDITIONAL = 13 removed + ReadPropertyMultiple BACnetConfirmedServiceChoice = 14 + ReadRange BACnetConfirmedServiceChoice = 26 + WriteProperty BACnetConfirmedServiceChoice = 15 + WritePropertyMultiple BACnetConfirmedServiceChoice = 16 + // Remote Device Management Services + DeviceCommunicationControl BACnetConfirmedServiceChoice = 17 + ConfirmedPrivateTransfer BACnetConfirmedServiceChoice = 18 + ConfirmedTextMessage BACnetConfirmedServiceChoice = 19 + ReinitializeDevice BACnetConfirmedServiceChoice = 20 + // Virtual Terminal Services + VTOpen BACnetConfirmedServiceChoice = 21 + VTClose BACnetConfirmedServiceChoice = 22 + VTData BACnetConfirmedServiceChoice = 23 + // Security Services + // SERVICE_CONFIRMED_AUTHENTICATE = 24 removed + // SERVICE_CONFIRMED_REQUEST_KEY = 25 removed +) + +const ( + ServiceChoiceIAm BACnetUnconfirmedServiceChoice = iota + ServiceChoiceIHave + UnconfirmedCovNotification + UnconfirmedEVENTNotification + UnconfirmedPrivateTransfer + UnconfirmedTextMessage + TimeSynchronization + ServiceChoiceWhoHas + ServiceChoiceWhoIs + UTCTimeSynchronization + WriteGroup + UnconfirmedCovNotificationMultiple + ServiceChoiceWhoAmI + ServiceChoiceYouAre +) + +type APDU struct { + PduType BacnetPduTypes + SegmentedMessage bool + MoreFollows bool + SegmentedResponseAccepted bool + MaxSegmentsAccepted BacnetMaxSegments + MaxApduLengthAccepted MaxAPDU + SequenceNumber byte + InvokeID byte + RequenceNumber byte + ProposedWindowSize byte + ServiceChoice byte +} + +func (a APDU) Encode() []byte { + buffer := make([]byte, 0) + + tmp := byte(a.PduType) + if a.SegmentedMessage { + tmp |= 0x10 + } + if a.MoreFollows { + tmp |= 0x20 + } + if a.SegmentedResponseAccepted { + tmp |= 0x40 + } + buffer = append(buffer, tmp) + + if a.PduType == PDUTypeConfirmedServiceRequest { + buffer = append(buffer, byte(a.MaxSegmentsAccepted)|byte(a.MaxApduLengthAccepted)) + buffer = append(buffer, a.InvokeID) + if a.SegmentedMessage { + buffer = append(buffer, a.SequenceNumber, a.ProposedWindowSize) + } + } else if a.PduType == PDUTypeUnconfirmedServiceRequest { + // No additional fields for unconfirmed service request + } else if a.PduType == PDUTypeSimpleAck { + buffer = append(buffer, a.InvokeID) + } else if a.PduType == PDUTypeComplexAck { + buffer = append(buffer, a.InvokeID) + if a.SegmentedMessage { + buffer = append(buffer, a.SequenceNumber, a.ProposedWindowSize) + } + } else { + // Handle other PDU types + } + + buffer = append(buffer, a.ServiceChoice) + return buffer +} + +func (a *APDU) Decode(buffer []byte, offset int) int { + length := 0 + a.PduType = BacnetPduTypes(buffer[offset]) + tmp := byte(buffer[offset]) + length++ + + if a.PduType == PDUTypeConfirmedServiceRequest { + a.SegmentedMessage = tmp&0x10 != 0 + a.MoreFollows = tmp&0x20 != 0 + a.SegmentedResponseAccepted = tmp&0x40 != 0 + a.MaxSegmentsAccepted = BacnetMaxSegments(buffer[offset+length] & 0xF0) + a.MaxApduLengthAccepted = MaxAPDU(buffer[offset+length] & 0x0F) + length++ + a.InvokeID = buffer[offset+length] + length++ + if a.SegmentedMessage { + a.SequenceNumber = buffer[offset+length] + length++ + a.ProposedWindowSize = buffer[offset+length] + length++ + } + a.ServiceChoice = buffer[offset+length] + length++ + } else if a.PduType == PDUTypeUnconfirmedServiceRequest { + a.ServiceChoice = buffer[offset+length] + length++ + } else if a.PduType == PDUTypeSimpleAck { + a.InvokeID = buffer[offset+length] + length++ + a.ServiceChoice = buffer[offset+length] + length++ + } else if a.PduType == PDUTypeComplexAck { + a.SegmentedMessage = tmp&0x10 != 0 + a.InvokeID = buffer[offset+length] + length++ + a.ServiceChoice = buffer[offset+length] + length++ + if a.SegmentedMessage { + a.SequenceNumber = buffer[offset+length] + length++ + a.ProposedWindowSize = buffer[offset+length] + length++ + } + } else { + return -1 + } + + return length +} diff --git a/base.go b/base.go index f44a793..697354f 100644 --- a/base.go +++ b/base.go @@ -47,17 +47,6 @@ const ( MaxSEG65 BacnetMaxSegments = 0x70 ) -type BacnetMaxAdpu int - -const ( - MaxAPDU50 BacnetMaxAdpu = iota - MaxAPDU128 - MaxAPDU206 - MaxAPDU480 - MaxAPDU1024 - MaxAPDU1476 -) - type BacnetPduTypes int const ( diff --git a/bvlc.go b/bvlc.go new file mode 100644 index 0000000..332243b --- /dev/null +++ b/bvlc.go @@ -0,0 +1,110 @@ +package bacnet + +import ( + "encoding/binary" + "errors" + + "github.com/absmach/bacnet/transport" +) + +var ( + errUnsupportedTransport = errors.New("unsupported transport") + errInvalidMessageLength = errors.New("invalid message length") + errUnsupportedFunction = errors.New("unsupported BVLC function") +) + +type BVLCFunctions int + +const ( + BVLCResult BVLCFunctions = iota + BVLCWriteBroadcastDistributionTable + BVLCReadBroadcastDistTable + BVLCReadBroadcastDistTableAck + BVLCForwardedNPDU + BVLCRegisterForeignDevice + BVLCReadForeignDeviceTable + BVLCReadForeignDeviceTableAck + BVLCDeleteForeignDeviceTableENTRY + BVLCDistributeBroadcastToNetwok + BVLCOriginalUnicastNPDU + BVLCOriginalBroadcastNPDU +) + +type MaxAPDU int + +const ( + MaxAPDU50 MaxAPDU = iota + MaxAPDU128 + MaxAPDU206 + MaxAPDU480 + MaxAPDU1024 + MaxAPDU1476 +) + +type BVLC struct { + BVLLTypeBACnetIP byte + BVLCHeaderLength byte + BVLCMaxAPDU MaxAPDU +} + +func NewBVLC(transprt transport.Transport) *BVLC { + bvlc := &BVLC{ + BVLLTypeBACnetIP: 0x81, + BVLCHeaderLength: 4, + } + + if transprt == transport.IP { + bvlc.BVLCMaxAPDU = MaxAPDU1476 + } + + return bvlc +} + +func (bvlc *BVLC) Decode(buffer []byte, offset int) (int, byte, uint16, error) { + msgType := buffer[0] + function := BVLCFunctions(buffer[1]) + msgLength := binary.BigEndian.Uint16(buffer[2:4]) + + if msgType != bvlc.BVLLTypeBACnetIP || msgLength != uint16(len(buffer)) { + return 0, 0, 0, errUnsupportedTransport + } + + switch function { + case BVLCResult: + return 4, byte(function), msgLength, nil + case BVLCOriginalUnicastNPDU: + return 4, byte(function), msgLength, nil + case BVLCOriginalBroadcastNPDU: + return 4, byte(function), msgLength, nil + case BVLCForwardedNPDU: + // Handle this case + case BVLCDistributeBroadcastToNetwok: + // Handle this case + case BVLCRegisterForeignDevice: + // Handle this case + case BVLCReadForeignDeviceTable: + // Handle this case + case BVLCDeleteForeignDeviceTableENTRY: + // Handle this case + case BVLCReadBroadcastDistTable: + // Handle this case + case BVLCWriteBroadcastDistributionTable: + // Handle this case + default: + return -1, 0, 0, errUnsupportedFunction + } + + return 0, 0, 0, errors.New("todo") +} + +func (bvlc BVLC) First4BytesHeaderEncode(function BVLCFunctions, msgLength uint16) []byte { + b := make([]byte, 4) + b[0] = bvlc.BVLLTypeBACnetIP + b[1] = byte(function) + binary.BigEndian.PutUint16(b[2:4], msgLength) + return b +} + +func (bvlc BVLC) Encode(function BVLCFunctions, msgLength uint16) []byte { + return bvlc.First4BytesHeaderEncode(function, msgLength) +} diff --git a/example/whois.go b/example/whois.go index 2b18b6e..f386a03 100644 --- a/example/whois.go +++ b/example/whois.go @@ -2,25 +2,59 @@ package main import ( "fmt" + "log" "net" "time" "github.com/absmach/bacnet" + "github.com/absmach/bacnet/transport" ) func main() { - var highlim, lowLim uint32 = 4194303, 0 - whi := bacnet.WhoIs{HighLimit: &highlim, LowLimit: &lowLim} - whoIsRequest := whi.Encode() + + var highLimit, lowLimit uint32 = 4000000, 0 + req := bacnet.WhoIs{ + HighLimit: &highLimit, + LowLimit: &lowLimit, + } + whoisBytes := req.Encode() + + netType := bacnet.IPV4 + source := bacnet.NewBACnetAddress(20, []byte{}, "127.0.0.255:47809", &netType) + + npdu := bacnet.NewNPDU(source, nil, nil, nil) + npdu.Control.SetNetworkPriority(bacnet.NormalMessage) + npduBytes, err := npdu.Encode() + if err != nil { + log.Fatalf("failed to encode npdu with error %v", err) + } + + apdu := bacnet.APDU{ + PduType: bacnet.PDUTypeUnconfirmedServiceRequest, + ServiceChoice: byte(bacnet.ServiceChoiceWhoIs), + } + + apduBytes := apdu.Encode() + + mes := append(npduBytes, apduBytes...) + mes = append(mes, whoisBytes...) + + fmt.Println(mes) + + blvc := bacnet.NewBVLC(transport.IP) + blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes))) + message := append(blvcBytes, mes...) + + fmt.Println(message) // Define the BACnet broadcast address (255.255.255.255:47808) - remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.255:47809") + remoteAddr, err := net.ResolveUDPAddr("udp", "255.255.255.255:47809") if err != nil { fmt.Println("Error resolving remote address:", err) return } - // Create a UDP connection + // Create a UDP connectionBACnetAddress conn, err := net.DialUDP("udp", nil, remoteAddr) if err != nil { fmt.Println("Error creating UDP connection:", err) @@ -29,7 +63,7 @@ func main() { defer conn.Close() // Send the WhoIsRequest packet - _, err = conn.Write(whoIsRequest) + _, err = conn.Write(message) if err != nil { fmt.Println("Error sending WhoIsRequest:", err) return diff --git a/npdu.go b/npdu.go index 65b7bea..7dc9f92 100644 --- a/npdu.go +++ b/npdu.go @@ -115,14 +115,21 @@ func (nci *NPDUControlInformation) Decode(buffer []byte, offset int) int { return 0 } -func NewNPDU(destination *BACnetAddress, source *BACnetAddress, hopCount int, vendorID int) *NPDU { +func NewNPDU(destination *BACnetAddress, source *BACnetAddress, hopCount *uint8, vendorID *uint16) *NPDU { npdu := &NPDU{ Version: 1, Control: *NewNPDUControlInformation(), Destination: destination, Source: source, - HopCount: uint8(hopCount), - VendorID: uint16(vendorID), + } + switch hopCount { + case nil: + npdu.HopCount = 255 + default: + npdu.HopCount = *hopCount + } + if vendorID != nil { + npdu.VendorID = *vendorID } if destination != nil && destination.NetworkNumber > 0 { diff --git a/transport/transport.go b/transport/transport.go new file mode 100644 index 0000000..57ba5ac --- /dev/null +++ b/transport/transport.go @@ -0,0 +1,7 @@ +package transport + +type Transport int + +const ( + IP = iota +) From 732dc10d1723476494411d1dbceb651c0accd9d7 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 5 Sep 2023 17:39:41 +0300 Subject: [PATCH 11/25] reorganize module Signed-off-by: SammyOina --- example/whois.go | 30 +++++++++--------- apdu.go => pkg/bacnet/apdu.go | 0 base.go => pkg/bacnet/base.go | 0 bvlc.go => pkg/bacnet/bvlc.go | 2 +- client.go => pkg/bacnet/client.go | 0 general.go => pkg/bacnet/general.go | 2 +- iam.go => pkg/bacnet/iam.go | 0 network.go => pkg/bacnet/network.go | 2 +- npdu.go => pkg/bacnet/npdu.go | 0 object.go => pkg/bacnet/object.go | 2 +- property.go => pkg/bacnet/property.go | 0 whois.go => pkg/bacnet/whois.go | 2 +- {encoding => pkg/encoding}/date_time.go | 0 {encoding => pkg/encoding}/decoding.go | 0 {encoding => pkg/encoding}/encoding.go | 0 {encoding => pkg/encoding}/tags.go | 0 {transport => pkg/transport}/transport.go | 0 pkg/transport/udp/broadcast.go | 38 +++++++++++++++++++++++ 18 files changed, 58 insertions(+), 20 deletions(-) rename apdu.go => pkg/bacnet/apdu.go (100%) rename base.go => pkg/bacnet/base.go (100%) rename bvlc.go => pkg/bacnet/bvlc.go (98%) rename client.go => pkg/bacnet/client.go (100%) rename general.go => pkg/bacnet/general.go (92%) rename iam.go => pkg/bacnet/iam.go (100%) rename network.go => pkg/bacnet/network.go (98%) rename npdu.go => pkg/bacnet/npdu.go (100%) rename object.go => pkg/bacnet/object.go (98%) rename property.go => pkg/bacnet/property.go (100%) rename whois.go => pkg/bacnet/whois.go (96%) rename {encoding => pkg/encoding}/date_time.go (100%) rename {encoding => pkg/encoding}/decoding.go (100%) rename {encoding => pkg/encoding}/encoding.go (100%) rename {encoding => pkg/encoding}/tags.go (100%) rename {transport => pkg/transport}/transport.go (100%) create mode 100644 pkg/transport/udp/broadcast.go diff --git a/example/whois.go b/example/whois.go index f386a03..77e918a 100644 --- a/example/whois.go +++ b/example/whois.go @@ -6,8 +6,9 @@ import ( "net" "time" - "github.com/absmach/bacnet" - "github.com/absmach/bacnet/transport" + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/transport" + "github.com/absmach/bacnet/pkg/transport/udp" ) func main() { @@ -19,10 +20,14 @@ func main() { } whoisBytes := req.Encode() + broads, err := udp.GetBroadcastAddress("127.0.0.6", 47809) + if err != nil { + log.Fatalf("failed to encode npdu with error %v", err) + } netType := bacnet.IPV4 - source := bacnet.NewBACnetAddress(20, []byte{}, "127.0.0.255:47809", &netType) + broads = *bacnet.NewBACnetAddress(0xFFFF, nil, "127.0.0.255:47809", &netType) - npdu := bacnet.NewNPDU(source, nil, nil, nil) + npdu := bacnet.NewNPDU(&broads, nil, nil, nil) npdu.Control.SetNetworkPriority(bacnet.NormalMessage) npduBytes, err := npdu.Encode() if err != nil { @@ -39,16 +44,12 @@ func main() { mes := append(npduBytes, apduBytes...) mes = append(mes, whoisBytes...) - fmt.Println(mes) - blvc := bacnet.NewBVLC(transport.IP) - blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes))) + blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) message := append(blvcBytes, mes...) - fmt.Println(message) - // Define the BACnet broadcast address (255.255.255.255:47808) - remoteAddr, err := net.ResolveUDPAddr("udp", "255.255.255.255:47809") + remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.255:47809") if err != nil { fmt.Println("Error resolving remote address:", err) return @@ -65,8 +66,7 @@ func main() { // Send the WhoIsRequest packet _, err = conn.Write(message) if err != nil { - fmt.Println("Error sending WhoIsRequest:", err) - return + log.Fatal("Error sending WhoIsRequest:", err) } // Wait for responses @@ -78,15 +78,15 @@ func main() { if err != nil { if netErr, ok := err.(net.Error); ok && netErr.Timeout() { // Timeout reached, no more responses - fmt.Println("No more responses received.") + log.Println("No more responses received.") break } - fmt.Println("Error reading response:", err) + log.Println("Error reading response:", err) break } // Process the response (you'll need to parse BACnet responses here) response := buffer[:n] - fmt.Printf("Received response: %X\n", response) + log.Printf("Received response: %X\n", response) } } diff --git a/apdu.go b/pkg/bacnet/apdu.go similarity index 100% rename from apdu.go rename to pkg/bacnet/apdu.go diff --git a/base.go b/pkg/bacnet/base.go similarity index 100% rename from base.go rename to pkg/bacnet/base.go diff --git a/bvlc.go b/pkg/bacnet/bvlc.go similarity index 98% rename from bvlc.go rename to pkg/bacnet/bvlc.go index 332243b..4859b21 100644 --- a/bvlc.go +++ b/pkg/bacnet/bvlc.go @@ -4,7 +4,7 @@ import ( "encoding/binary" "errors" - "github.com/absmach/bacnet/transport" + "github.com/absmach/bacnet/pkg/transport" ) var ( diff --git a/client.go b/pkg/bacnet/client.go similarity index 100% rename from client.go rename to pkg/bacnet/client.go diff --git a/general.go b/pkg/bacnet/general.go similarity index 92% rename from general.go rename to pkg/bacnet/general.go index 3a5d08d..ba24376 100644 --- a/general.go +++ b/pkg/bacnet/general.go @@ -3,7 +3,7 @@ package bacnet import ( "time" - "github.com/absmach/bacnet/encoding" + "github.com/absmach/bacnet/pkg/encoding" ) type DateTime struct { diff --git a/iam.go b/pkg/bacnet/iam.go similarity index 100% rename from iam.go rename to pkg/bacnet/iam.go diff --git a/network.go b/pkg/bacnet/network.go similarity index 98% rename from network.go rename to pkg/bacnet/network.go index 2455cab..0a885d1 100644 --- a/network.go +++ b/pkg/bacnet/network.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/absmach/bacnet/encoding" + "github.com/absmach/bacnet/pkg/encoding" ) type BACnetNetworkType int diff --git a/npdu.go b/pkg/bacnet/npdu.go similarity index 100% rename from npdu.go rename to pkg/bacnet/npdu.go diff --git a/object.go b/pkg/bacnet/object.go similarity index 98% rename from object.go rename to pkg/bacnet/object.go index ffc4b59..487aab1 100644 --- a/object.go +++ b/pkg/bacnet/object.go @@ -3,7 +3,7 @@ package bacnet import ( "encoding/binary" - "github.com/absmach/bacnet/encoding" + "github.com/absmach/bacnet/pkg/encoding" ) type ObjectInstance uint32 diff --git a/property.go b/pkg/bacnet/property.go similarity index 100% rename from property.go rename to pkg/bacnet/property.go diff --git a/whois.go b/pkg/bacnet/whois.go similarity index 96% rename from whois.go rename to pkg/bacnet/whois.go index 1ea2d51..e3a0826 100644 --- a/whois.go +++ b/pkg/bacnet/whois.go @@ -1,6 +1,6 @@ package bacnet -import "github.com/absmach/bacnet/encoding" +import "github.com/absmach/bacnet/pkg/encoding" type WhoIs struct { HighLimit, LowLimit *uint32 diff --git a/encoding/date_time.go b/pkg/encoding/date_time.go similarity index 100% rename from encoding/date_time.go rename to pkg/encoding/date_time.go diff --git a/encoding/decoding.go b/pkg/encoding/decoding.go similarity index 100% rename from encoding/decoding.go rename to pkg/encoding/decoding.go diff --git a/encoding/encoding.go b/pkg/encoding/encoding.go similarity index 100% rename from encoding/encoding.go rename to pkg/encoding/encoding.go diff --git a/encoding/tags.go b/pkg/encoding/tags.go similarity index 100% rename from encoding/tags.go rename to pkg/encoding/tags.go diff --git a/transport/transport.go b/pkg/transport/transport.go similarity index 100% rename from transport/transport.go rename to pkg/transport/transport.go diff --git a/pkg/transport/udp/broadcast.go b/pkg/transport/udp/broadcast.go new file mode 100644 index 0000000..3de1911 --- /dev/null +++ b/pkg/transport/udp/broadcast.go @@ -0,0 +1,38 @@ +package udp + +import ( + "net" + "strconv" + + "github.com/absmach/bacnet/pkg/bacnet" +) + +func GetBroadcastAddress(localEndpoint string, port int) (bacnet.BACnetAddress, error) { + broadcast := "255.255.255.255" + + interfaces, err := net.Interfaces() + if err != nil { + return bacnet.BACnetAddress{}, err + } + + for _, iface := range interfaces { + addrs, err := iface.Addrs() + if err != nil { + return bacnet.BACnetAddress{}, err + } + + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { + ipAddress := ipnet.IP.String() + if ipAddress == localEndpoint { + if iface.Flags&net.FlagBroadcast != 0 { + broadcast = ipnet.IP.Mask(ipnet.IP.DefaultMask()).String() + } + } + } + } + } + netType := bacnet.IPV4 + return *bacnet.NewBACnetAddress(0xFFFF, nil, broadcast+":"+strconv.Itoa(port), &netType), nil + +} From 292c9496475b76d3f321549924f5950263b47be5 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Fri, 8 Sep 2023 16:28:43 +0300 Subject: [PATCH 12/25] add read property request Signed-off-by: SammyOina --- example/whois.go | 3 +- pkg/bacnet/base.go | 477 -------- pkg/bacnet/client.go | 10 +- pkg/bacnet/network.go | 23 +- pkg/bacnet/object.go | 72 +- pkg/bacnet/property.go | 66 +- pkg/encoding/property.go | 1853 ++++++++++++++++++++++++++++++++ pkg/encoding/tags.go | 10 +- pkg/transport/udp/broadcast.go | 3 +- 9 files changed, 1940 insertions(+), 577 deletions(-) create mode 100644 pkg/encoding/property.go diff --git a/example/whois.go b/example/whois.go index 77e918a..11a6b07 100644 --- a/example/whois.go +++ b/example/whois.go @@ -7,6 +7,7 @@ import ( "time" "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" "github.com/absmach/bacnet/pkg/transport" "github.com/absmach/bacnet/pkg/transport/udp" ) @@ -24,7 +25,7 @@ func main() { if err != nil { log.Fatalf("failed to encode npdu with error %v", err) } - netType := bacnet.IPV4 + netType := encoding.IPV4 broads = *bacnet.NewBACnetAddress(0xFFFF, nil, "127.0.0.255:47809", &netType) npdu := bacnet.NewNPDU(&broads, nil, nil, nil) diff --git a/pkg/bacnet/base.go b/pkg/bacnet/base.go index 697354f..7a550e4 100644 --- a/pkg/bacnet/base.go +++ b/pkg/bacnet/base.go @@ -275,480 +275,3 @@ const ( DuplicateEntry InvalidValueInThisState ) - -// BACnetPropertyIdentifier represents BACnet property identifiers. -type PropertyIdentifier int - -const ( - AckedTransitions PropertyIdentifier = 0 - AckRequired PropertyIdentifier = 1 - Action PropertyIdentifier = 2 - ActionText PropertyIdentifier = 3 - ActiveText PropertyIdentifier = 4 - ActiveVtSessions PropertyIdentifier = 5 - AlarmValue PropertyIdentifier = 6 - AlarmValues PropertyIdentifier = 7 - All PropertyIdentifier = 8 - AllWritesSuccessful PropertyIdentifier = 9 - ApduSegmentTimeout PropertyIdentifier = 10 - ApduTimeout PropertyIdentifier = 11 - ApplicationSoftwareVersion PropertyIdentifier = 12 - Archive PropertyIdentifier = 13 - Bias PropertyIdentifier = 14 - ChangeOfStateCount PropertyIdentifier = 15 - ChangeOfStateTime PropertyIdentifier = 16 - NotificationClass PropertyIdentifier = 17 - Blank1 PropertyIdentifier = 18 - ControlledVariableReference PropertyIdentifier = 19 - ControlledVariableUnits PropertyIdentifier = 20 - ControlledVariableValue PropertyIdentifier = 21 - CovIncrement PropertyIdentifier = 22 - DateList PropertyIdentifier = 23 - DaylightSavingsStatus PropertyIdentifier = 24 - Deadband PropertyIdentifier = 25 - DerivativeConstant PropertyIdentifier = 26 - DerivativeConstantUnits PropertyIdentifier = 27 - Description PropertyIdentifier = 28 - DescriptionOfHalt PropertyIdentifier = 29 - DeviceAddressBinding PropertyIdentifier = 30 - DeviceType PropertyIdentifier = 31 - EffectivePeriod PropertyIdentifier = 32 - ElapsedActiveTime PropertyIdentifier = 33 - ErrorLimit PropertyIdentifier = 34 - EventEnable PropertyIdentifier = 35 - EventState PropertyIdentifier = 36 - EventType PropertyIdentifier = 37 - ExceptionSchedule PropertyIdentifier = 38 - FaultValues PropertyIdentifier = 39 - FeedbackValue PropertyIdentifier = 40 - FileAccessMethod PropertyIdentifier = 41 - FileSize PropertyIdentifier = 42 - FileType PropertyIdentifier = 43 - FirmwareRevision PropertyIdentifier = 44 - HighLimit PropertyIdentifier = 45 - InactiveText PropertyIdentifier = 46 - InProcess PropertyIdentifier = 47 - InstanceOf PropertyIdentifier = 48 - IntegralConstant PropertyIdentifier = 49 - IntegralConstantUnits PropertyIdentifier = 50 - IssueConfirmedNotifications PropertyIdentifier = 51 - LimitEnable PropertyIdentifier = 52 - ListOfGroupMembers PropertyIdentifier = 53 - ListOfObjectPropertyReferences PropertyIdentifier = 54 - ListOfSessionKeys PropertyIdentifier = 55 - LocalDate PropertyIdentifier = 56 - LocalTime PropertyIdentifier = 57 - Location PropertyIdentifier = 58 - LowLimit PropertyIdentifier = 59 - ManipulatedVariableReference PropertyIdentifier = 60 - MaximumOutput PropertyIdentifier = 61 - MaxApduLengthAccepted PropertyIdentifier = 62 - MaxInfoFrames PropertyIdentifier = 63 - MaxMaster PropertyIdentifier = 64 - MaxPresValue PropertyIdentifier = 65 - MinimumOffTime PropertyIdentifier = 66 - MinimumOnTime PropertyIdentifier = 67 - MinimumOutput PropertyIdentifier = 68 - MinPresValue PropertyIdentifier = 69 - ModelName PropertyIdentifier = 70 - ModificationDate PropertyIdentifier = 71 - NotifyType PropertyIdentifier = 72 - NumberOfApduRetries PropertyIdentifier = 73 - NumberOfStates PropertyIdentifier = 74 - ObjectIdentifierPI PropertyIdentifier = 75 - ObjectList PropertyIdentifier = 76 - ObjectName PropertyIdentifier = 77 - ObjectPropertyReference PropertyIdentifier = 78 - ObjectTypePI PropertyIdentifier = 79 - Optional PropertyIdentifier = 80 - OutOfService PropertyIdentifier = 81 - OutputUnits PropertyIdentifier = 82 - EventParameters PropertyIdentifier = 83 - Polarity PropertyIdentifier = 84 - PresentValue PropertyIdentifier = 85 - Priority PropertyIdentifier = 86 - PriorityArray PropertyIdentifier = 87 - PriorityForWriting PropertyIdentifier = 88 - ProcessIdentifier PropertyIdentifier = 89 - ProgramChange PropertyIdentifier = 90 - ProgramLocation PropertyIdentifier = 91 - ProgramState PropertyIdentifier = 92 - ProportionalConstant PropertyIdentifier = 93 - ProportionalConstantUnits PropertyIdentifier = 94 - ProtocolConformanceClass PropertyIdentifier = 95 - ProtocolObjectTypesSupported PropertyIdentifier = 96 - ProtocolServicesSupported PropertyIdentifier = 97 - ProtocolVersion PropertyIdentifier = 98 - ReadOnly PropertyIdentifier = 99 - ReasonForHalt PropertyIdentifier = 100 - Recipient PropertyIdentifier = 101 - RecipientList PropertyIdentifier = 102 - Reliability PropertyIdentifier = 103 - RelinquishDefault PropertyIdentifier = 104 - Required PropertyIdentifier = 105 - Resolution PropertyIdentifier = 106 - SegmentationSupported PropertyIdentifier = 107 - Setpoint PropertyIdentifier = 108 - SetpointReference PropertyIdentifier = 109 - StateText PropertyIdentifier = 110 - StatusFlags PropertyIdentifier = 111 - SystemStatus PropertyIdentifier = 112 - TimeDelay PropertyIdentifier = 113 - TimeOfActiveTimeReset PropertyIdentifier = 114 - TimeOfStateCountReset PropertyIdentifier = 115 - TimeSynchronizationRecipients PropertyIdentifier = 116 - Units PropertyIdentifier = 117 - UpdateInterval PropertyIdentifier = 118 - UtcOffset PropertyIdentifier = 119 - VendorIdentifier PropertyIdentifier = 120 - VendorName PropertyIdentifier = 121 - VtClassesSupported PropertyIdentifier = 122 - WeeklySchedule PropertyIdentifier = 123 - AttemptedSamples PropertyIdentifier = 124 - AverageValue PropertyIdentifier = 125 - BufferSize PropertyIdentifier = 126 - ClientCovIncrement PropertyIdentifier = 127 - CovResubscriptionInterval PropertyIdentifier = 128 - CurrentNotifyTime PropertyIdentifier = 129 - EventTimeStamps PropertyIdentifier = 130 - LogBuffer PropertyIdentifier = 131 - LogDeviceObjectProperty PropertyIdentifier = 132 - Enable PropertyIdentifier = 133 - LogInterval PropertyIdentifier = 134 - MaximumValue PropertyIdentifier = 135 - MinimumValue PropertyIdentifier = 136 - NotificationThreshold PropertyIdentifier = 137 - PreviousNotifyTime PropertyIdentifier = 138 - ProtocolRevision PropertyIdentifier = 139 - RecordsSinceNotification PropertyIdentifier = 140 - RecordCount PropertyIdentifier = 141 - StartTime PropertyIdentifier = 142 - StopTime PropertyIdentifier = 143 - StopWhenFull PropertyIdentifier = 144 - TotalRecordCount PropertyIdentifier = 145 - ValidSamples PropertyIdentifier = 146 - WindowInterval PropertyIdentifier = 147 - WindowSamples PropertyIdentifier = 148 - MaximumValueTimestamp PropertyIdentifier = 149 - MinimumValueTimestamp PropertyIdentifier = 150 - VarianceValue PropertyIdentifier = 151 - ActiveCovSubscriptions PropertyIdentifier = 152 - BackupFailureTimeout PropertyIdentifier = 153 - ConfigurationFiles PropertyIdentifier = 154 - DatabaseRevision PropertyIdentifier = 155 - DirectReading PropertyIdentifier = 156 - LastRestoreTime PropertyIdentifier = 157 - MaintenanceRequired PropertyIdentifier = 158 - MemberOf PropertyIdentifier = 159 - Mode PropertyIdentifier = 160 - OperationExpected PropertyIdentifier = 161 - Setting PropertyIdentifier = 162 - Silenced PropertyIdentifier = 163 - TrackingValue PropertyIdentifier = 164 - ZoneMembers PropertyIdentifier = 165 - LifeSafetyAlarmValues PropertyIdentifier = 166 - MaxSegmentsAccepted PropertyIdentifier = 167 - ProfileName PropertyIdentifier = 168 - AutoSlaveDiscovery PropertyIdentifier = 169 - ManualSlaveAddressBinding PropertyIdentifier = 170 - SlaveAddressBinding PropertyIdentifier = 171 - SlaveProxyEnable PropertyIdentifier = 172 - LastNotifyRecord PropertyIdentifier = 173 - ScheduleDefault PropertyIdentifier = 174 - AcceptedModes PropertyIdentifier = 175 - AdjustValue PropertyIdentifier = 176 - Count PropertyIdentifier = 177 - CountBeforeChange PropertyIdentifier = 178 - CountChangeTime PropertyIdentifier = 179 - CovPeriod PropertyIdentifier = 180 - InputReference PropertyIdentifier = 181 - LimitMonitoringInterval PropertyIdentifier = 182 - LoggingObject PropertyIdentifier = 183 - LoggingRecord PropertyIdentifier = 184 - Prescale PropertyIdentifier = 185 - PulseRate PropertyIdentifier = 186 - Scale PropertyIdentifier = 187 - ScaleFactor PropertyIdentifier = 188 - UpdateTime PropertyIdentifier = 189 - ValueBeforeChange PropertyIdentifier = 190 - ValueSet PropertyIdentifier = 191 - ValueChangeTime PropertyIdentifier = 192 - AlignIntervals PropertyIdentifier = 193 - IntervalOffset PropertyIdentifier = 195 - LastRestartReason PropertyIdentifier = 196 - LoggingType PropertyIdentifier = 197 - RestartNotificationRecipients PropertyIdentifier = 202 - TimeOfDeviceRestart PropertyIdentifier = 203 - TimeSynchronizationInterval PropertyIdentifier = 204 - Trigger PropertyIdentifier = 205 - UtcTimeSynchronizationRecipients PropertyIdentifier = 206 - NodeSubtype PropertyIdentifier = 207 - NodeType PropertyIdentifier = 208 - StructuredObjectList PropertyIdentifier = 209 - SubordinateAnnotations PropertyIdentifier = 210 - SubordinateList PropertyIdentifier = 211 - ActualShedLevel PropertyIdentifier = 212 - DutyWindow PropertyIdentifier = 213 - ExpectedShedLevel PropertyIdentifier = 214 - FullDutyBaseline PropertyIdentifier = 215 - RequestedShedLevel PropertyIdentifier = 218 - ShedDuration PropertyIdentifier = 219 - ShedLevelDescriptions PropertyIdentifier = 220 - ShedLevels PropertyIdentifier = 221 - StateDescription PropertyIdentifier = 222 - DoorAlarmState PropertyIdentifier = 226 - DoorExtendedPulseTime PropertyIdentifier = 227 - DoorMembers PropertyIdentifier = 228 - DoorOpenTooLongTime PropertyIdentifier = 229 - DoorPulseTime PropertyIdentifier = 230 - DoorStatus PropertyIdentifier = 231 - DoorUnlockDelayTime PropertyIdentifier = 232 - LockStatus PropertyIdentifier = 233 - MaskedAlarmValues PropertyIdentifier = 234 - SecuredStatus PropertyIdentifier = 235 - AbsenteeLimit PropertyIdentifier = 244 - AccessAlarmEvents PropertyIdentifier = 245 - AccessDoors PropertyIdentifier = 246 - AccessEvent PropertyIdentifier = 247 - AccessEventAuthenticationFactor PropertyIdentifier = 248 - AccessEventCredential PropertyIdentifier = 249 - AccessEventTime PropertyIdentifier = 250 - AccessTransactionEvents PropertyIdentifier = 251 - Accompaniment PropertyIdentifier = 252 - AccompanimentTime PropertyIdentifier = 253 - ActivationTime PropertyIdentifier = 254 - ActiveAuthenticationPolicy PropertyIdentifier = 255 - AssignedAccessRights PropertyIdentifier = 256 - AuthenticationFactors PropertyIdentifier = 257 - AuthenticationPolicyList PropertyIdentifier = 258 - AuthenticationPolicyNames PropertyIdentifier = 259 - AuthenticationStatus PropertyIdentifier = 260 - AuthorizationMode PropertyIdentifier = 261 - BelongsTo PropertyIdentifier = 262 - CredentialDisable PropertyIdentifier = 263 - CredentialStatus PropertyIdentifier = 264 - Credentials PropertyIdentifier = 265 - CredentialsInZone PropertyIdentifier = 266 - DaysRemaining PropertyIdentifier = 267 - EntryPoints PropertyIdentifier = 268 - ExitPoints PropertyIdentifier = 269 - ExpiryTime PropertyIdentifier = 270 - ExtendedTimeEnable PropertyIdentifier = 271 - FailedAttemptEvents PropertyIdentifier = 272 - FailedAttempts PropertyIdentifier = 273 - FailedAttemptsTime PropertyIdentifier = 274 - LastAccessEvent PropertyIdentifier = 275 - LastAccessPoint PropertyIdentifier = 276 - LastCredentialAdded PropertyIdentifier = 277 - LastCredentialAddedTime PropertyIdentifier = 278 - LastCredentialRemoved PropertyIdentifier = 279 - LastCredentialRemovedTime PropertyIdentifier = 280 - LastUseTime PropertyIdentifier = 281 - Lockout PropertyIdentifier = 282 - LockoutRelinquishTime PropertyIdentifier = 283 - MasterExemption PropertyIdentifier = 284 - MaxFailedAttempts PropertyIdentifier = 285 - Members PropertyIdentifier = 286 - MusterPoint PropertyIdentifier = 287 - NegativeAccessRules PropertyIdentifier = 288 - NumberOfAuthenticationPolicies PropertyIdentifier = 289 - OccupancyCount PropertyIdentifier = 290 - OccupancyCountAdjust PropertyIdentifier = 291 - OccupancyCountEnable PropertyIdentifier = 292 - OccupancyExemption PropertyIdentifier = 293 - OccupancyLowerLimit PropertyIdentifier = 294 - OccupancyLowerLimitEnforced PropertyIdentifier = 295 - OccupancyState PropertyIdentifier = 296 - OccupancyUpperLimit PropertyIdentifier = 297 - OccupancyUpperLimitEnforced PropertyIdentifier = 298 - PassbackExemption PropertyIdentifier = 299 - PassbackMode PropertyIdentifier = 300 - PassbackTimeout PropertyIdentifier = 301 - PositiveAccessRules PropertyIdentifier = 302 - ReasonForDisable PropertyIdentifier = 303 - SupportedFormats PropertyIdentifier = 304 - SupportedFormatClasses PropertyIdentifier = 305 - ThreatAuthority PropertyIdentifier = 306 - ThreatLevel PropertyIdentifier = 307 - TraceFlag PropertyIdentifier = 308 - TransactionNotificationClass PropertyIdentifier = 309 - UserExternalIdentifier PropertyIdentifier = 310 - UserInformationReference PropertyIdentifier = 311 - UserName PropertyIdentifier = 317 - UserType PropertyIdentifier = 318 - UsesRemaining PropertyIdentifier = 319 - ZoneFrom PropertyIdentifier = 320 - ZoneTo PropertyIdentifier = 321 - AccessEventTag PropertyIdentifier = 322 - GlobalIdentifier PropertyIdentifier = 323 - VerificationTime PropertyIdentifier = 326 - BaseDeviceSecurityPolicy PropertyIdentifier = 327 - DistributionKeyRevision PropertyIdentifier = 328 - DoNotHide PropertyIdentifier = 329 - KeySets PropertyIdentifier = 330 - LastKeyServer PropertyIdentifier = 331 - NetworkAccessSecurityPolicies PropertyIdentifier = 332 - PacketReorderTime PropertyIdentifier = 333 - SecurityPduTimeout PropertyIdentifier = 334 - SecurityTimeWindow PropertyIdentifier = 335 - SupportedSecurityAlgorithm PropertyIdentifier = 336 - UpdateKeySetTimeout PropertyIdentifier = 337 - BackupAndRestoreState PropertyIdentifier = 338 - BackupPreparationTime PropertyIdentifier = 339 - RestoreCompletionTime PropertyIdentifier = 340 - RestorePreparationTime PropertyIdentifier = 341 - BitMask PropertyIdentifier = 342 - BitText PropertyIdentifier = 343 - IsUtc PropertyIdentifier = 344 - GroupMembers PropertyIdentifier = 345 - GroupMemberNames PropertyIdentifier = 346 - MemberStatusFlags PropertyIdentifier = 347 - RequestedUpdateInterval PropertyIdentifier = 348 - CovuPeriod PropertyIdentifier = 349 - CovuRecipients PropertyIdentifier = 350 - EventMessageTexts PropertyIdentifier = 351 - EventMessageTextsConfig PropertyIdentifier = 352 - EventDetectionEnable PropertyIdentifier = 353 - EventAlgorithmInhibit PropertyIdentifier = 354 - EventAlgorithmInhibitRef PropertyIdentifier = 355 - TimeDelayNormal PropertyIdentifier = 356 - ReliabilityEvaluationInhibit PropertyIdentifier = 357 - FaultParameters PropertyIdentifier = 358 - FaultType PropertyIdentifier = 359 - LocalForwardingOnly PropertyIdentifier = 360 - ProcessIdentifierFilter PropertyIdentifier = 361 - SubscribedRecipients PropertyIdentifier = 362 - PortFilter PropertyIdentifier = 363 - AuthorizationExemptions PropertyIdentifier = 364 - AllowGroupDelayInhibit PropertyIdentifier = 365 - ChannelNumber PropertyIdentifier = 366 - ControlGroups PropertyIdentifier = 367 - ExecutionDelay PropertyIdentifier = 368 - LastPriority PropertyIdentifier = 369 - WriteStatus PropertyIdentifier = 370 - PropertyList PropertyIdentifier = 371 - SerialNumber PropertyIdentifier = 372 - BlinkWarnEnable PropertyIdentifier = 373 - DefaultFadeTime PropertyIdentifier = 374 - DefaultRampRate PropertyIdentifier = 375 - DefaultStepIncrement PropertyIdentifier = 376 - EgressTime PropertyIdentifier = 377 - InProgress PropertyIdentifier = 378 - InstantaneousPower PropertyIdentifier = 379 - LightingCommand PropertyIdentifier = 380 - LightingCommandDefaultPriority PropertyIdentifier = 381 - MaxActualValue PropertyIdentifier = 382 - MinActualValue PropertyIdentifier = 383 - Power PropertyIdentifier = 384 - Transition PropertyIdentifier = 385 - EgressActive PropertyIdentifier = 386 - InterfaceValue PropertyIdentifier = 387 - FaultHighLimit PropertyIdentifier = 388 - FaultLowLimit PropertyIdentifier = 389 - LowDiffLimit PropertyIdentifier = 390 - StrikeCount PropertyIdentifier = 391 - TimeOfStrikeCountReset PropertyIdentifier = 392 - DefaultTimeout PropertyIdentifier = 393 - InitialTimeout PropertyIdentifier = 394 - LastStateChange PropertyIdentifier = 395 - StateChangeValues PropertyIdentifier = 396 - TimerRunning PropertyIdentifier = 397 - TimerState PropertyIdentifier = 398 - ApduLength PropertyIdentifier = 399 - IpAddress PropertyIdentifier = 400 - IpDefaultGateway PropertyIdentifier = 401 - IpDhcpEnable PropertyIdentifier = 402 - IpDhcpLeaseTime PropertyIdentifier = 403 - IpDhcpLeaseTimeRemaining PropertyIdentifier = 404 - IpDhcpServer PropertyIdentifier = 405 - IpDnsServer PropertyIdentifier = 406 - BacnetIpGlobalAddress PropertyIdentifier = 407 - BacnetIpMode PropertyIdentifier = 408 - BacnetIpMulticastAddress PropertyIdentifier = 409 - BacnetIpNatTraversal PropertyIdentifier = 410 - IpSubnetMask PropertyIdentifier = 411 - BacnetIpUdpPort PropertyIdentifier = 412 - BbmdAcceptFdRegistrations PropertyIdentifier = 413 - BbmdBroadcastDistributionTable PropertyIdentifier = 414 - BbmdForeignDeviceTable PropertyIdentifier = 415 - ChangesPending PropertyIdentifier = 416 - Command PropertyIdentifier = 417 - FdBbmdAddress PropertyIdentifier = 418 - FdSubscriptionLifetime PropertyIdentifier = 419 - LinkSpeed PropertyIdentifier = 420 - LinkSpeeds PropertyIdentifier = 421 - LinkSpeedAutonegotiate PropertyIdentifier = 422 - MacAddress PropertyIdentifier = 423 - NetworkInterfaceName PropertyIdentifier = 424 - NetworkNumber PropertyIdentifier = 425 - NetworkNumberQuality PropertyIdentifier = 426 - NetworkType PropertyIdentifier = 427 - RoutingTable PropertyIdentifier = 428 - VirtualMacAddressTable PropertyIdentifier = 429 - CommandTimeArray PropertyIdentifier = 430 - CurrentCommandPriority PropertyIdentifier = 431 - LastCommandTime PropertyIdentifier = 432 - ValueSource PropertyIdentifier = 433 - ValueSourceArray PropertyIdentifier = 434 - BacnetIpv6Mode PropertyIdentifier = 435 - Ipv6Address PropertyIdentifier = 436 - Ipv6PrefixLength PropertyIdentifier = 437 - BacnetIpv6UdpPort PropertyIdentifier = 438 - Ipv6DefaultGateway PropertyIdentifier = 439 - BacnetIpv6MulticastAddress PropertyIdentifier = 440 - Ipv6DnsServer PropertyIdentifier = 441 - Ipv6AutoAddressingEnable PropertyIdentifier = 442 - Ipv6DhcpLeaseTime PropertyIdentifier = 443 - Ipv6DhcpLeaseTimeRemaining PropertyIdentifier = 444 - Ipv6DhcpServer PropertyIdentifier = 445 - Ipv6ZoneIndex PropertyIdentifier = 446 - AssignedLandingCalls PropertyIdentifier = 447 - CarAssignedDirection PropertyIdentifier = 448 - CarDoorCommand PropertyIdentifier = 449 - CarDoorStatus PropertyIdentifier = 450 - CarDoorText PropertyIdentifier = 451 - CarDoorZone PropertyIdentifier = 452 - CarDriveStatus PropertyIdentifier = 453 - CarLoad PropertyIdentifier = 454 - CarLoadUnits PropertyIdentifier = 455 - CarMode PropertyIdentifier = 456 - CarMovingDirection PropertyIdentifier = 457 - CarPosition PropertyIdentifier = 458 - ElevatorGroup PropertyIdentifier = 459 - EnergyMeter PropertyIdentifier = 460 - EnergyMeterRef PropertyIdentifier = 461 - EscalatorMode PropertyIdentifier = 462 - FloorText PropertyIdentifier = 464 - GroupId PropertyIdentifier = 465 - GroupMode PropertyIdentifier = 467 - HigherDeck PropertyIdentifier = 468 - InstallationId PropertyIdentifier = 469 - LandingCalls PropertyIdentifier = 470 - LandingCallControl PropertyIdentifier = 471 - LandingDoorStatus PropertyIdentifier = 472 - LowerDeck PropertyIdentifier = 473 - MachineRoomId PropertyIdentifier = 474 - MakingCarCall PropertyIdentifier = 475 - NextStoppingFloor PropertyIdentifier = 476 - OperationDirection PropertyIdentifier = 477 - PassengerAlarm PropertyIdentifier = 478 - PowerMode PropertyIdentifier = 479 - RegisteredCarCall PropertyIdentifier = 480 - ActiveCovMultipleSubscriptions PropertyIdentifier = 481 - ProtocolLevel PropertyIdentifier = 482 - ReferencePort PropertyIdentifier = 483 - DeployedProfileLocation PropertyIdentifier = 484 - ProfileLocation PropertyIdentifier = 485 - Tags PropertyIdentifier = 486 - SubordinateNodeTypes PropertyIdentifier = 487 - SubordinateRelationships PropertyIdentifier = 489 - SubordinateTags PropertyIdentifier = 488 - DefaultSubordinateRelationship PropertyIdentifier = 490 - Represents PropertyIdentifier = 491 - DefaultPresentValue PropertyIdentifier = 492 - PresentStage PropertyIdentifier = 493 - Stages PropertyIdentifier = 494 - StageNames PropertyIdentifier = 495 - TargetReferences PropertyIdentifier = 496 - FaultSignals PropertyIdentifier = 463 -) diff --git a/pkg/bacnet/client.go b/pkg/bacnet/client.go index 98f6c67..3d6f055 100644 --- a/pkg/bacnet/client.go +++ b/pkg/bacnet/client.go @@ -1,22 +1,24 @@ package bacnet +import "github.com/absmach/bacnet/pkg/encoding" + type client struct { // Fields for client configuration } type Client interface { - ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) - WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error + ReadProperty(objectID ObjectIdentifier, propertyID encoding.PropertyIdentifier) (interface{}, error) + WriteProperty(objectID ObjectIdentifier, propertyID encoding.PropertyIdentifier, value interface{}) error } func NewClient(address string, port int) (Client, error) { return &client{}, nil } -func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) { +func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID encoding.PropertyIdentifier) (interface{}, error) { return nil, nil } -func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error { +func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID encoding.PropertyIdentifier, value interface{}) error { return nil } diff --git a/pkg/bacnet/network.go b/pkg/bacnet/network.go index 0a885d1..15650e5 100644 --- a/pkg/bacnet/network.go +++ b/pkg/bacnet/network.go @@ -8,21 +8,6 @@ import ( "github.com/absmach/bacnet/pkg/encoding" ) -type BACnetNetworkType int - -const ( - Ethernet BACnetNetworkType = iota - ARCnet - MSTP - PTP - LonTalk - IPV4 - Zigbee - Virtual - IPV6 - Serial -) - type ApplicationTags int const ( @@ -53,7 +38,7 @@ type BACnetAddress struct { MacAddress []byte } -func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface{}, netType *BACnetNetworkType) *BACnetAddress { +func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface{}, netType *encoding.BACnetNetworkType) *BACnetAddress { addr := &BACnetAddress{ NetworkNumber: networkNumber, MacAddress: macAddress, @@ -63,7 +48,7 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface case string: if address != "" { switch *netType { - case IPV4: + case encoding.IPV4: tmp1 := strings.Split(addr1, ":") parts := strings.Split(tmp1[0], ".") var ipAddr [4]byte @@ -75,7 +60,7 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface var port uint16 fmt.Sscanf(tmp1[1], "%d", &port) addr.MacAddress = append(ipAddr[:], byte(port>>8), byte(port)) - case Ethernet: + case encoding.Ethernet: parts := strings.Split(addr1, "-") for _, part := range parts { val := byte(0) @@ -85,7 +70,7 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface } } case ObjectIdentifier: - if *netType == IPV4 { + if *netType == encoding.IPV4 { addr.MacAddress = make([]byte, 6) binary.LittleEndian.PutUint64(addr.MacAddress, uint64(addr1.Instance)) } diff --git a/pkg/bacnet/object.go b/pkg/bacnet/object.go index 487aab1..a68a104 100644 --- a/pkg/bacnet/object.go +++ b/pkg/bacnet/object.go @@ -8,87 +8,21 @@ import ( type ObjectInstance uint32 -type ObjectType uint16 - -const ( - AnalogInput ObjectType = iota - AnalogOutput - AnalogValue - BinaryInput - BinaryOutput - BinaryValue - Calendar - ObjectTypeCommand - ObjectTypeDevice - EventEnrollment - File - Group - Loop - MultiStateInput - MultiStateOutput - ObjectTypeNotificationClass - Program - Schedule - Averaging - MultiStateValue - TrendLog - LifeSafetyPoint - LifeSafetyZone - Accumulator - PulseConverter - EventLog - GlobalGroup - TrendLogMultiple - LoadControl - StructuredView - AccessDoor - Timer - AccessCredential - AccessPoint - AccessRights - AccessUser - AccessZone - CredentialDataInput - NetworkSecurity - BitStringValue - CharacterStringValue - DatePatternValue - DateValue - DateTimePatternValue - DateTimeValue - IntegerValue - LargeAnalogValue - OctetStringValue - PositiveIntegerValue - TimePatternValue - TimeValue - NotificationForwarder - AlertEnrollment - Channel - LightingOutput - BinaryLightingOutput - NetworkPort - ObjectTypeElevatorGroup - Escalator - Lift - Staging -) - type ObjectIdentifier struct { - Type ObjectType + Type encoding.ObjectType Instance ObjectInstance } func (oi *ObjectIdentifier) Decode(buf []byte, offset, apdulen int) int { len, val := encoding.DecodeUnsigned(buf, offset, 4) oi.Instance = ObjectInstance(val) & ObjectInstance(encoding.MaxInstance) - oi.Type = ObjectType(val >> encoding.InstanceBits & encoding.MaxObject) + oi.Type = encoding.ObjectType(val >> encoding.InstanceBits & encoding.MaxObject) return len } func (oi *ObjectIdentifier) DecodeContext(buf []byte, offset, apdulen int, tagNumber byte) int { len := 0 - if encoding.DecodeIsCOntextTag(buf, offset+len, tagNumber) { + if encoding.IsContextTag(buf, offset+len, tagNumber) { len1, _, lenVal := encoding.DecodeTagNumberAndValue(buf, offset+len) len += len1 len += oi.Decode(buf, offset+len1, int(lenVal)) diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index 08d5908..f7b2124 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -1,8 +1,72 @@ package bacnet +import ( + "github.com/absmach/bacnet/pkg/encoding" +) + type PropertyValue struct { - Identifier PropertyIdentifier + Identifier encoding.PropertyIdentifier Arrayindex *uint32 Value uint32 Priority uint32 } + +type ReadPropertyRequest struct { + ObjectIdentifier *ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex uint32 +} + +func (r ReadPropertyRequest) Encode() []byte { + var ret []byte + + if r.ObjectIdentifier != nil { + ret = append(ret, r.ObjectIdentifier.EncodeContext(0)...) + } + + propID := r.PropertyIdentifier.(int) + + ret = append(ret, encoding.EncodeContextEnumerated(1, uint32(propID))...) + + if r.PropertyArrayIndex != 0 { + ret = append(ret, encoding.EncodeContextUnsigned(2, r.PropertyArrayIndex)...) + } + + return ret +} + +func (r *ReadPropertyRequest) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // objectIdentifier + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + r.ObjectIdentifier = &ObjectIdentifier{} + leng += r.ObjectIdentifier.Decode(buffer, offset+leng, int(lenValue)) + } else { + return -1 + } + + // propertyIdentifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, r.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + } else { + return -1 + } + + // propertyArrayIndex (optional) + if leng < apduLen && encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, r.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + + leng += leng1 + } + + return leng +} diff --git a/pkg/encoding/property.go b/pkg/encoding/property.go new file mode 100644 index 0000000..4428f67 --- /dev/null +++ b/pkg/encoding/property.go @@ -0,0 +1,1853 @@ +package encoding + +type BACnetNetworkType int + +const ( + Ethernet BACnetNetworkType = iota + ARCnet + MSTP + PTP + LonTalk + IPV4 + Zigbee + Virtual + IPV6 + Serial +) + +type ObjectType uint16 + +const ( + AnalogInput ObjectType = iota + AnalogOutput + AnalogValue + BinaryInput + BinaryOutput + BinaryValue + Calendar + ObjectTypeCommand + ObjectTypeDevice + EventEnrollment + File + Group + Loop + MultiStateInput + MultiStateOutput + ObjectTypeNotificationClass + Program + Schedule + Averaging + MultiStateValue + TrendLog + LifeSafetyPoint + LifeSafetyZone + Accumulator + PulseConverter + EventLog + GlobalGroup + TrendLogMultiple + LoadControl + StructuredView + AccessDoor + Timer + AccessCredential + AccessPoint + AccessRights + AccessUser + AccessZone + CredentialDataInput + NetworkSecurity + BitStringValue + CharacterStringValue + DatePatternValue + DateValue + DateTimePatternValue + DateTimeValue + IntegerValue + LargeAnalogValue + OctetStringValue + PositiveIntegerValue + TimePatternValue + TimeValue + NotificationForwarder + AlertEnrollment + Channel + LightingOutput + BinaryLightingOutput + NetworkPort + ObjectTypeElevatorGroup + Escalator + Lift + Staging +) + +// BACnetPropertyIdentifier represents BACnet property identifiers. +type PropertyIdentifier int + +const ( + AckedTransitions PropertyIdentifier = 0 + AckRequired PropertyIdentifier = 1 + Action PropertyIdentifier = 2 + ActionText PropertyIdentifier = 3 + ActiveText PropertyIdentifier = 4 + ActiveVtSessions PropertyIdentifier = 5 + AlarmValue PropertyIdentifier = 6 + AlarmValues PropertyIdentifier = 7 + All PropertyIdentifier = 8 + AllWritesSuccessful PropertyIdentifier = 9 + ApduSegmentTimeout PropertyIdentifier = 10 + ApduTimeout PropertyIdentifier = 11 + ApplicationSoftwareVersion PropertyIdentifier = 12 + Archive PropertyIdentifier = 13 + Bias PropertyIdentifier = 14 + ChangeOfStateCount PropertyIdentifier = 15 + ChangeOfStateTime PropertyIdentifier = 16 + NotificationClass PropertyIdentifier = 17 + Blank1 PropertyIdentifier = 18 + ControlledVariableReference PropertyIdentifier = 19 + ControlledVariableUnits PropertyIdentifier = 20 + ControlledVariableValue PropertyIdentifier = 21 + CovIncrement PropertyIdentifier = 22 + DateList PropertyIdentifier = 23 + DaylightSavingsStatus PropertyIdentifier = 24 + Deadband PropertyIdentifier = 25 + DerivativeConstant PropertyIdentifier = 26 + DerivativeConstantUnits PropertyIdentifier = 27 + Description PropertyIdentifier = 28 + DescriptionOfHalt PropertyIdentifier = 29 + DeviceAddressBinding PropertyIdentifier = 30 + DeviceType PropertyIdentifier = 31 + EffectivePeriod PropertyIdentifier = 32 + ElapsedActiveTime PropertyIdentifier = 33 + ErrorLimit PropertyIdentifier = 34 + EventEnable PropertyIdentifier = 35 + EventState PropertyIdentifier = 36 + EventType PropertyIdentifier = 37 + ExceptionSchedule PropertyIdentifier = 38 + FaultValues PropertyIdentifier = 39 + FeedbackValue PropertyIdentifier = 40 + FileAccessMethod PropertyIdentifier = 41 + FileSize PropertyIdentifier = 42 + FileType PropertyIdentifier = 43 + FirmwareRevision PropertyIdentifier = 44 + HighLimit PropertyIdentifier = 45 + InactiveText PropertyIdentifier = 46 + InProcess PropertyIdentifier = 47 + InstanceOf PropertyIdentifier = 48 + IntegralConstant PropertyIdentifier = 49 + IntegralConstantUnits PropertyIdentifier = 50 + IssueConfirmedNotifications PropertyIdentifier = 51 + LimitEnable PropertyIdentifier = 52 + ListOfGroupMembers PropertyIdentifier = 53 + ListOfObjectPropertyReferences PropertyIdentifier = 54 + ListOfSessionKeys PropertyIdentifier = 55 + LocalDate PropertyIdentifier = 56 + LocalTime PropertyIdentifier = 57 + Location PropertyIdentifier = 58 + LowLimit PropertyIdentifier = 59 + ManipulatedVariableReference PropertyIdentifier = 60 + MaximumOutput PropertyIdentifier = 61 + MaxApduLengthAccepted PropertyIdentifier = 62 + MaxInfoFrames PropertyIdentifier = 63 + MaxMaster PropertyIdentifier = 64 + MaxPresValue PropertyIdentifier = 65 + MinimumOffTime PropertyIdentifier = 66 + MinimumOnTime PropertyIdentifier = 67 + MinimumOutput PropertyIdentifier = 68 + MinPresValue PropertyIdentifier = 69 + ModelName PropertyIdentifier = 70 + ModificationDate PropertyIdentifier = 71 + NotifyType PropertyIdentifier = 72 + NumberOfApduRetries PropertyIdentifier = 73 + NumberOfStates PropertyIdentifier = 74 + ObjectIdentifierPI PropertyIdentifier = 75 + ObjectList PropertyIdentifier = 76 + ObjectName PropertyIdentifier = 77 + ObjectPropertyReference PropertyIdentifier = 78 + ObjectTypePI PropertyIdentifier = 79 + Optional PropertyIdentifier = 80 + OutOfService PropertyIdentifier = 81 + OutputUnits PropertyIdentifier = 82 + EventParameters PropertyIdentifier = 83 + Polarity PropertyIdentifier = 84 + PresentValue PropertyIdentifier = 85 + Priority PropertyIdentifier = 86 + PriorityArray PropertyIdentifier = 87 + PriorityForWriting PropertyIdentifier = 88 + ProcessIdentifier PropertyIdentifier = 89 + ProgramChange PropertyIdentifier = 90 + ProgramLocation PropertyIdentifier = 91 + ProgramState PropertyIdentifier = 92 + ProportionalConstant PropertyIdentifier = 93 + ProportionalConstantUnits PropertyIdentifier = 94 + ProtocolConformanceClass PropertyIdentifier = 95 + ProtocolObjectTypesSupported PropertyIdentifier = 96 + ProtocolServicesSupported PropertyIdentifier = 97 + ProtocolVersion PropertyIdentifier = 98 + ReadOnly PropertyIdentifier = 99 + ReasonForHalt PropertyIdentifier = 100 + Recipient PropertyIdentifier = 101 + RecipientList PropertyIdentifier = 102 + Reliability PropertyIdentifier = 103 + RelinquishDefault PropertyIdentifier = 104 + Required PropertyIdentifier = 105 + Resolution PropertyIdentifier = 106 + SegmentationSupported PropertyIdentifier = 107 + Setpoint PropertyIdentifier = 108 + SetpointReference PropertyIdentifier = 109 + StateText PropertyIdentifier = 110 + StatusFlags PropertyIdentifier = 111 + SystemStatus PropertyIdentifier = 112 + TimeDelay PropertyIdentifier = 113 + TimeOfActiveTimeReset PropertyIdentifier = 114 + TimeOfStateCountReset PropertyIdentifier = 115 + TimeSynchronizationRecipients PropertyIdentifier = 116 + Units PropertyIdentifier = 117 + UpdateInterval PropertyIdentifier = 118 + UtcOffset PropertyIdentifier = 119 + VendorIdentifier PropertyIdentifier = 120 + VendorName PropertyIdentifier = 121 + VtClassesSupported PropertyIdentifier = 122 + WeeklySchedule PropertyIdentifier = 123 + AttemptedSamples PropertyIdentifier = 124 + AverageValue PropertyIdentifier = 125 + BufferSize PropertyIdentifier = 126 + ClientCovIncrement PropertyIdentifier = 127 + CovResubscriptionInterval PropertyIdentifier = 128 + CurrentNotifyTime PropertyIdentifier = 129 + EventTimeStamps PropertyIdentifier = 130 + LogBuffer PropertyIdentifier = 131 + LogDeviceObjectProperty PropertyIdentifier = 132 + Enable PropertyIdentifier = 133 + LogInterval PropertyIdentifier = 134 + MaximumValue PropertyIdentifier = 135 + MinimumValue PropertyIdentifier = 136 + NotificationThreshold PropertyIdentifier = 137 + PreviousNotifyTime PropertyIdentifier = 138 + ProtocolRevision PropertyIdentifier = 139 + RecordsSinceNotification PropertyIdentifier = 140 + RecordCount PropertyIdentifier = 141 + StartTime PropertyIdentifier = 142 + StopTime PropertyIdentifier = 143 + StopWhenFull PropertyIdentifier = 144 + TotalRecordCount PropertyIdentifier = 145 + ValidSamples PropertyIdentifier = 146 + WindowInterval PropertyIdentifier = 147 + WindowSamples PropertyIdentifier = 148 + MaximumValueTimestamp PropertyIdentifier = 149 + MinimumValueTimestamp PropertyIdentifier = 150 + VarianceValue PropertyIdentifier = 151 + ActiveCovSubscriptions PropertyIdentifier = 152 + BackupFailureTimeout PropertyIdentifier = 153 + ConfigurationFiles PropertyIdentifier = 154 + DatabaseRevision PropertyIdentifier = 155 + DirectReading PropertyIdentifier = 156 + LastRestoreTime PropertyIdentifier = 157 + MaintenanceRequired PropertyIdentifier = 158 + MemberOf PropertyIdentifier = 159 + Mode PropertyIdentifier = 160 + OperationExpected PropertyIdentifier = 161 + Setting PropertyIdentifier = 162 + Silenced PropertyIdentifier = 163 + TrackingValue PropertyIdentifier = 164 + ZoneMembers PropertyIdentifier = 165 + LifeSafetyAlarmValues PropertyIdentifier = 166 + MaxSegmentsAccepted PropertyIdentifier = 167 + ProfileName PropertyIdentifier = 168 + AutoSlaveDiscovery PropertyIdentifier = 169 + ManualSlaveAddressBinding PropertyIdentifier = 170 + SlaveAddressBinding PropertyIdentifier = 171 + SlaveProxyEnable PropertyIdentifier = 172 + LastNotifyRecord PropertyIdentifier = 173 + ScheduleDefault PropertyIdentifier = 174 + AcceptedModes PropertyIdentifier = 175 + AdjustValue PropertyIdentifier = 176 + Count PropertyIdentifier = 177 + CountBeforeChange PropertyIdentifier = 178 + CountChangeTime PropertyIdentifier = 179 + CovPeriod PropertyIdentifier = 180 + InputReference PropertyIdentifier = 181 + LimitMonitoringInterval PropertyIdentifier = 182 + LoggingObject PropertyIdentifier = 183 + LoggingRecord PropertyIdentifier = 184 + Prescale PropertyIdentifier = 185 + PulseRate PropertyIdentifier = 186 + Scale PropertyIdentifier = 187 + ScaleFactor PropertyIdentifier = 188 + UpdateTime PropertyIdentifier = 189 + ValueBeforeChange PropertyIdentifier = 190 + ValueSet PropertyIdentifier = 191 + ValueChangeTime PropertyIdentifier = 192 + AlignIntervals PropertyIdentifier = 193 + IntervalOffset PropertyIdentifier = 195 + LastRestartReason PropertyIdentifier = 196 + LoggingType PropertyIdentifier = 197 + RestartNotificationRecipients PropertyIdentifier = 202 + TimeOfDeviceRestart PropertyIdentifier = 203 + TimeSynchronizationInterval PropertyIdentifier = 204 + Trigger PropertyIdentifier = 205 + UtcTimeSynchronizationRecipients PropertyIdentifier = 206 + NodeSubtype PropertyIdentifier = 207 + NodeType PropertyIdentifier = 208 + StructuredObjectList PropertyIdentifier = 209 + SubordinateAnnotations PropertyIdentifier = 210 + SubordinateList PropertyIdentifier = 211 + ActualShedLevel PropertyIdentifier = 212 + DutyWindow PropertyIdentifier = 213 + ExpectedShedLevel PropertyIdentifier = 214 + FullDutyBaseline PropertyIdentifier = 215 + RequestedShedLevel PropertyIdentifier = 218 + ShedDuration PropertyIdentifier = 219 + ShedLevelDescriptions PropertyIdentifier = 220 + ShedLevels PropertyIdentifier = 221 + StateDescription PropertyIdentifier = 222 + DoorAlarmState PropertyIdentifier = 226 + DoorExtendedPulseTime PropertyIdentifier = 227 + DoorMembers PropertyIdentifier = 228 + DoorOpenTooLongTime PropertyIdentifier = 229 + DoorPulseTime PropertyIdentifier = 230 + DoorStatus PropertyIdentifier = 231 + DoorUnlockDelayTime PropertyIdentifier = 232 + LockStatus PropertyIdentifier = 233 + MaskedAlarmValues PropertyIdentifier = 234 + SecuredStatus PropertyIdentifier = 235 + AbsenteeLimit PropertyIdentifier = 244 + AccessAlarmEvents PropertyIdentifier = 245 + AccessDoors PropertyIdentifier = 246 + AccessEvent PropertyIdentifier = 247 + AccessEventAuthenticationFactor PropertyIdentifier = 248 + AccessEventCredential PropertyIdentifier = 249 + AccessEventTime PropertyIdentifier = 250 + AccessTransactionEvents PropertyIdentifier = 251 + Accompaniment PropertyIdentifier = 252 + AccompanimentTime PropertyIdentifier = 253 + ActivationTime PropertyIdentifier = 254 + ActiveAuthenticationPolicy PropertyIdentifier = 255 + AssignedAccessRights PropertyIdentifier = 256 + AuthenticationFactors PropertyIdentifier = 257 + AuthenticationPolicyList PropertyIdentifier = 258 + AuthenticationPolicyNames PropertyIdentifier = 259 + AuthenticationStatus PropertyIdentifier = 260 + AuthorizationMode PropertyIdentifier = 261 + BelongsTo PropertyIdentifier = 262 + CredentialDisable PropertyIdentifier = 263 + CredentialStatus PropertyIdentifier = 264 + Credentials PropertyIdentifier = 265 + CredentialsInZone PropertyIdentifier = 266 + DaysRemaining PropertyIdentifier = 267 + EntryPoints PropertyIdentifier = 268 + ExitPoints PropertyIdentifier = 269 + ExpiryTime PropertyIdentifier = 270 + ExtendedTimeEnable PropertyIdentifier = 271 + FailedAttemptEvents PropertyIdentifier = 272 + FailedAttempts PropertyIdentifier = 273 + FailedAttemptsTime PropertyIdentifier = 274 + LastAccessEvent PropertyIdentifier = 275 + LastAccessPoint PropertyIdentifier = 276 + LastCredentialAdded PropertyIdentifier = 277 + LastCredentialAddedTime PropertyIdentifier = 278 + LastCredentialRemoved PropertyIdentifier = 279 + LastCredentialRemovedTime PropertyIdentifier = 280 + LastUseTime PropertyIdentifier = 281 + Lockout PropertyIdentifier = 282 + LockoutRelinquishTime PropertyIdentifier = 283 + MasterExemption PropertyIdentifier = 284 + MaxFailedAttempts PropertyIdentifier = 285 + Members PropertyIdentifier = 286 + MusterPoint PropertyIdentifier = 287 + NegativeAccessRules PropertyIdentifier = 288 + NumberOfAuthenticationPolicies PropertyIdentifier = 289 + OccupancyCount PropertyIdentifier = 290 + OccupancyCountAdjust PropertyIdentifier = 291 + OccupancyCountEnable PropertyIdentifier = 292 + OccupancyExemption PropertyIdentifier = 293 + OccupancyLowerLimit PropertyIdentifier = 294 + OccupancyLowerLimitEnforced PropertyIdentifier = 295 + OccupancyState PropertyIdentifier = 296 + OccupancyUpperLimit PropertyIdentifier = 297 + OccupancyUpperLimitEnforced PropertyIdentifier = 298 + PassbackExemption PropertyIdentifier = 299 + PassbackMode PropertyIdentifier = 300 + PassbackTimeout PropertyIdentifier = 301 + PositiveAccessRules PropertyIdentifier = 302 + ReasonForDisable PropertyIdentifier = 303 + SupportedFormats PropertyIdentifier = 304 + SupportedFormatClasses PropertyIdentifier = 305 + ThreatAuthority PropertyIdentifier = 306 + ThreatLevel PropertyIdentifier = 307 + TraceFlag PropertyIdentifier = 308 + TransactionNotificationClass PropertyIdentifier = 309 + UserExternalIdentifier PropertyIdentifier = 310 + UserInformationReference PropertyIdentifier = 311 + UserName PropertyIdentifier = 317 + UserType PropertyIdentifier = 318 + UsesRemaining PropertyIdentifier = 319 + ZoneFrom PropertyIdentifier = 320 + ZoneTo PropertyIdentifier = 321 + AccessEventTag PropertyIdentifier = 322 + GlobalIdentifier PropertyIdentifier = 323 + VerificationTime PropertyIdentifier = 326 + BaseDeviceSecurityPolicy PropertyIdentifier = 327 + DistributionKeyRevision PropertyIdentifier = 328 + DoNotHide PropertyIdentifier = 329 + KeySets PropertyIdentifier = 330 + LastKeyServer PropertyIdentifier = 331 + NetworkAccessSecurityPolicies PropertyIdentifier = 332 + PacketReorderTime PropertyIdentifier = 333 + SecurityPduTimeout PropertyIdentifier = 334 + SecurityTimeWindow PropertyIdentifier = 335 + SupportedSecurityAlgorithm PropertyIdentifier = 336 + UpdateKeySetTimeout PropertyIdentifier = 337 + BackupAndRestoreState PropertyIdentifier = 338 + BackupPreparationTime PropertyIdentifier = 339 + RestoreCompletionTime PropertyIdentifier = 340 + RestorePreparationTime PropertyIdentifier = 341 + BitMask PropertyIdentifier = 342 + BitText PropertyIdentifier = 343 + IsUtc PropertyIdentifier = 344 + GroupMembers PropertyIdentifier = 345 + GroupMemberNames PropertyIdentifier = 346 + MemberStatusFlags PropertyIdentifier = 347 + RequestedUpdateInterval PropertyIdentifier = 348 + CovuPeriod PropertyIdentifier = 349 + CovuRecipients PropertyIdentifier = 350 + EventMessageTexts PropertyIdentifier = 351 + EventMessageTextsConfig PropertyIdentifier = 352 + EventDetectionEnable PropertyIdentifier = 353 + EventAlgorithmInhibit PropertyIdentifier = 354 + EventAlgorithmInhibitRef PropertyIdentifier = 355 + TimeDelayNormal PropertyIdentifier = 356 + ReliabilityEvaluationInhibit PropertyIdentifier = 357 + FaultParameters PropertyIdentifier = 358 + FaultType PropertyIdentifier = 359 + LocalForwardingOnly PropertyIdentifier = 360 + ProcessIdentifierFilter PropertyIdentifier = 361 + SubscribedRecipients PropertyIdentifier = 362 + PortFilter PropertyIdentifier = 363 + AuthorizationExemptions PropertyIdentifier = 364 + AllowGroupDelayInhibit PropertyIdentifier = 365 + ChannelNumber PropertyIdentifier = 366 + ControlGroups PropertyIdentifier = 367 + ExecutionDelay PropertyIdentifier = 368 + LastPriority PropertyIdentifier = 369 + WriteStatus PropertyIdentifier = 370 + PropertyList PropertyIdentifier = 371 + SerialNumber PropertyIdentifier = 372 + BlinkWarnEnable PropertyIdentifier = 373 + DefaultFadeTime PropertyIdentifier = 374 + DefaultRampRate PropertyIdentifier = 375 + DefaultStepIncrement PropertyIdentifier = 376 + EgressTime PropertyIdentifier = 377 + InProgress PropertyIdentifier = 378 + InstantaneousPower PropertyIdentifier = 379 + LightingCommand PropertyIdentifier = 380 + LightingCommandDefaultPriority PropertyIdentifier = 381 + MaxActualValue PropertyIdentifier = 382 + MinActualValue PropertyIdentifier = 383 + Power PropertyIdentifier = 384 + Transition PropertyIdentifier = 385 + EgressActive PropertyIdentifier = 386 + InterfaceValue PropertyIdentifier = 387 + FaultHighLimit PropertyIdentifier = 388 + FaultLowLimit PropertyIdentifier = 389 + LowDiffLimit PropertyIdentifier = 390 + StrikeCount PropertyIdentifier = 391 + TimeOfStrikeCountReset PropertyIdentifier = 392 + DefaultTimeout PropertyIdentifier = 393 + InitialTimeout PropertyIdentifier = 394 + LastStateChange PropertyIdentifier = 395 + StateChangeValues PropertyIdentifier = 396 + TimerRunning PropertyIdentifier = 397 + TimerState PropertyIdentifier = 398 + ApduLength PropertyIdentifier = 399 + IpAddress PropertyIdentifier = 400 + IpDefaultGateway PropertyIdentifier = 401 + IpDhcpEnable PropertyIdentifier = 402 + IpDhcpLeaseTime PropertyIdentifier = 403 + IpDhcpLeaseTimeRemaining PropertyIdentifier = 404 + IpDhcpServer PropertyIdentifier = 405 + IpDnsServer PropertyIdentifier = 406 + BacnetIpGlobalAddress PropertyIdentifier = 407 + BacnetIpMode PropertyIdentifier = 408 + BacnetIpMulticastAddress PropertyIdentifier = 409 + BacnetIpNatTraversal PropertyIdentifier = 410 + IpSubnetMask PropertyIdentifier = 411 + BacnetIpUdpPort PropertyIdentifier = 412 + BbmdAcceptFdRegistrations PropertyIdentifier = 413 + BbmdBroadcastDistributionTable PropertyIdentifier = 414 + BbmdForeignDeviceTable PropertyIdentifier = 415 + ChangesPending PropertyIdentifier = 416 + Command PropertyIdentifier = 417 + FdBbmdAddress PropertyIdentifier = 418 + FdSubscriptionLifetime PropertyIdentifier = 419 + LinkSpeed PropertyIdentifier = 420 + LinkSpeeds PropertyIdentifier = 421 + LinkSpeedAutonegotiate PropertyIdentifier = 422 + MacAddress PropertyIdentifier = 423 + NetworkInterfaceName PropertyIdentifier = 424 + NetworkNumber PropertyIdentifier = 425 + NetworkNumberQuality PropertyIdentifier = 426 + NetworkType PropertyIdentifier = 427 + RoutingTable PropertyIdentifier = 428 + VirtualMacAddressTable PropertyIdentifier = 429 + CommandTimeArray PropertyIdentifier = 430 + CurrentCommandPriority PropertyIdentifier = 431 + LastCommandTime PropertyIdentifier = 432 + ValueSource PropertyIdentifier = 433 + ValueSourceArray PropertyIdentifier = 434 + BacnetIpv6Mode PropertyIdentifier = 435 + Ipv6Address PropertyIdentifier = 436 + Ipv6PrefixLength PropertyIdentifier = 437 + BacnetIpv6UdpPort PropertyIdentifier = 438 + Ipv6DefaultGateway PropertyIdentifier = 439 + BacnetIpv6MulticastAddress PropertyIdentifier = 440 + Ipv6DnsServer PropertyIdentifier = 441 + Ipv6AutoAddressingEnable PropertyIdentifier = 442 + Ipv6DhcpLeaseTime PropertyIdentifier = 443 + Ipv6DhcpLeaseTimeRemaining PropertyIdentifier = 444 + Ipv6DhcpServer PropertyIdentifier = 445 + Ipv6ZoneIndex PropertyIdentifier = 446 + AssignedLandingCalls PropertyIdentifier = 447 + CarAssignedDirection PropertyIdentifier = 448 + CarDoorCommand PropertyIdentifier = 449 + CarDoorStatus PropertyIdentifier = 450 + CarDoorText PropertyIdentifier = 451 + CarDoorZone PropertyIdentifier = 452 + CarDriveStatus PropertyIdentifier = 453 + CarLoad PropertyIdentifier = 454 + CarLoadUnits PropertyIdentifier = 455 + CarMode PropertyIdentifier = 456 + CarMovingDirection PropertyIdentifier = 457 + CarPosition PropertyIdentifier = 458 + ElevatorGroup PropertyIdentifier = 459 + EnergyMeter PropertyIdentifier = 460 + EnergyMeterRef PropertyIdentifier = 461 + EscalatorMode PropertyIdentifier = 462 + FloorText PropertyIdentifier = 464 + GroupId PropertyIdentifier = 465 + GroupMode PropertyIdentifier = 467 + HigherDeck PropertyIdentifier = 468 + InstallationId PropertyIdentifier = 469 + LandingCalls PropertyIdentifier = 470 + LandingCallControl PropertyIdentifier = 471 + LandingDoorStatus PropertyIdentifier = 472 + LowerDeck PropertyIdentifier = 473 + MachineRoomId PropertyIdentifier = 474 + MakingCarCall PropertyIdentifier = 475 + NextStoppingFloor PropertyIdentifier = 476 + OperationDirection PropertyIdentifier = 477 + PassengerAlarm PropertyIdentifier = 478 + PowerMode PropertyIdentifier = 479 + RegisteredCarCall PropertyIdentifier = 480 + ActiveCovMultipleSubscriptions PropertyIdentifier = 481 + ProtocolLevel PropertyIdentifier = 482 + ReferencePort PropertyIdentifier = 483 + DeployedProfileLocation PropertyIdentifier = 484 + ProfileLocation PropertyIdentifier = 485 + Tags PropertyIdentifier = 486 + SubordinateNodeTypes PropertyIdentifier = 487 + SubordinateRelationships PropertyIdentifier = 489 + SubordinateTags PropertyIdentifier = 488 + DefaultSubordinateRelationship PropertyIdentifier = 490 + Represents PropertyIdentifier = 491 + DefaultPresentValue PropertyIdentifier = 492 + PresentStage PropertyIdentifier = 493 + Stages PropertyIdentifier = 494 + StageNames PropertyIdentifier = 495 + TargetReferences PropertyIdentifier = 496 + FaultSignals PropertyIdentifier = 463 +) + +type BACnetSegmentation int + +const ( + SEGMENTED_BOTH BACnetSegmentation = iota + SEGMENTED_TRANSMIT + SEGMENTED_RECEIVE + NO_SEGMENTATION +) + +type BACnetEventType int + +const ( + ChangeOfBitstring BACnetEventType = 0 + ChangeOfState BACnetEventType = 1 + ChangeOfValue BACnetEventType = 2 + CommandFailure BACnetEventType = 3 + FloatingLimit BACnetEventType = 4 + OutOfRange BACnetEventType = 5 + Complex BACnetEventType = 6 + ChangeOfLifeSafety BACnetEventType = 8 + Extended BACnetEventType = 9 + BufferReady BACnetEventType = 10 + UnsignedRange BACnetEventType = 11 + BACnetEventTypeAccessEvent BACnetEventType = 13 + DoubleOutOfRange BACnetEventType = 14 + SignedOutOfRange BACnetEventType = 15 + UnsignedOutOfRange BACnetEventType = 16 + ChangeOfCharacterstring BACnetEventType = 17 + ChangeOfStatusFlag BACnetEventType = 18 + ChangeOfReliability BACnetEventType = 19 + None BACnetEventType = 20 + ChangeOfDiscreteValue BACnetEventType = 21 + ChangeOfTimer BACnetEventType = 22 +) + +type BACnetFaultType int + +const ( + BacnetFaultTypeNone BACnetFaultType = iota + FaultCHARACTERSTRING + FaultEXTENDED + FaultLIFE_SAFETY + FaultSTATE + FaultStatusFlags + FaultOutOfRange + FaultListed +) + +type BACnetNotifyType int + +const ( + ALARM BACnetNotifyType = iota + EVENT + ACK_NOTIFICATION +) + +type BACnetEventState int + +const ( + Normal BACnetEventState = iota + Fault + OffNormal + BACnetEventStateHighLimit + BACnetEventStateLowLimit + LifeSafetyAlarm +) + +type BACnetAccessCredentialDisableReason int + +const ( + Disabled BACnetAccessCredentialDisableReason = iota + DisabledNeedsProvisioning + DisabledUnassigned + DisabledNotYetActive + DisabledExpired + DisabledLockout + DisabledMaxDays + DisabledMaxUses + DisabledInactivity + DisabledManual +) + +type BACnetAccessCredentialDisable int + +const ( + BACnetAccessCredentialDisableNone BACnetAccessCredentialDisable = iota + Disable + DisableManual + DisableLockout +) + +type BACnetAccessPassbackMode int + +const ( + PassbackOff BACnetAccessPassbackMode = iota + HardPassback + SoftPassback +) + +type BACnetAccessUserType int + +const ( + Asset BACnetAccessUserType = iota + BACnetAccessUserTypeGroup + Person +) + +type BACnetAccessZoneOccupancyState int + +const ( + BACnetAccessZoneOccupancyStateNormal BACnetAccessZoneOccupancyState = iota + BelowLowerLimit + AtLowerLimit + AtUpperLimit + AboveUpperLimit + BACnetAccessZoneOccupancyStateDisabled + NotSupported +) + +type BACnetAction int + +const ( + Direct BACnetAction = iota + Reverse +) + +type BACnetNetworkNumberQuality int + +const ( + Unknown BACnetNetworkNumberQuality = iota + Learned + LearnedConfigured + Configured +) + +type BACnetBinaryPV int + +const ( + Inactive BACnetBinaryPV = iota + Active +) + +type BACnetDoorValue int + +const ( + Lock BACnetDoorValue = iota + Unlock + PulseUnlock + ExtendedPulseUnlock +) + +type BACnetAuthenticationStatus int + +const ( + NotReady BACnetAuthenticationStatus = iota + Ready + BACnetAuthenticationStatusDisabled + WaitingForAuthenticationFactor + WaitingForAccompaniment + WaitingForVerification + BACnetAuthenticationStatusInProgress +) + +type BACnetAuthorizationExemption int + +const ( + Passback BACnetAuthorizationExemption = iota + OccupancyCheck + BACnetAuthorizationExemptionAccessRights + BACnetAuthorizationExemptionLockout + Deny + Verification + AuthorizationDelay +) + +type BACnetAuthorizationMode int + +const ( + Authorize BACnetAuthorizationMode = iota + GrantActive + DenyAll + VerificationRequired + AuthorizationDelayed + BACnetAuthorizationModeNone +) + +type BACnetBackupState int + +const ( + Idle BACnetBackupState = iota + PreparingForBackup + PreparingForRestor + PerformingABACKUP + PerformingARestor +) + +type BACnetBinaryLightingPV int + +const ( + Off BACnetBinaryLightingPV = iota + On + Warn + WarnOff + WarnRelinquish + Stop +) + +type BACnetDeviceStatus int + +const ( + Operational BACnetDeviceStatus = iota + OperationalReadOnly + DownloadRequired + DownloadInProgress + NonOperational + BackupInProgress +) + +type BACnetDoorAlarmState int + +const ( + BACnetDoorAlarmStateNormal BACnetDoorAlarmState = iota + Alarm + DoorOpenTooLong + ForcedOpen + Tamper + DoorFault + LockDown + FreeAccess + EgressOpen +) + +type BACnetDoorSecuredStatus int + +const ( + Secured BACnetDoorSecuredStatus = iota + UNSecured + BACnetDoorSecuredStatusUnknown +) + +type BACnetDoorStatus int + +const ( + CLOSED BACnetDoorStatus = iota + OPENED + UNKNOWN + DOOR_FAULT + UNUSED + NONE + CLOSING + OPENING + SAFETY_LOCKED + LIMITED_OPENED +) + +type BACnetEngineeringUnits int + +const ( + metersPerSecondPerSecond BACnetEngineeringUnits = 166 + SquareMeters BACnetEngineeringUnits = 0 + SquareCentimeters BACnetEngineeringUnits = 116 + SquareFeet BACnetEngineeringUnits = 1 + SquareInches BACnetEngineeringUnits = 115 + Currency1 BACnetEngineeringUnits = 105 + Currency2 BACnetEngineeringUnits = 106 + Currency3 BACnetEngineeringUnits = 107 + Currency4 BACnetEngineeringUnits = 108 + Currency5 BACnetEngineeringUnits = 109 + Currency6 BACnetEngineeringUnits = 110 + Currency7 BACnetEngineeringUnits = 111 + Currency8 BACnetEngineeringUnits = 112 + Currency9 BACnetEngineeringUnits = 113 + Currency10 BACnetEngineeringUnits = 114 + Milliamperes BACnetEngineeringUnits = 2 + Amperes BACnetEngineeringUnits = 3 + AmperesPerMeter BACnetEngineeringUnits = 167 + AmperesPerSquareMeter BACnetEngineeringUnits = 168 + AmpereSquareMeters BACnetEngineeringUnits = 169 + Decibels BACnetEngineeringUnits = 199 + DecibelsMillivolt BACnetEngineeringUnits = 200 + DecibelsVolt BACnetEngineeringUnits = 201 + Farads BACnetEngineeringUnits = 170 + Henrys BACnetEngineeringUnits = 171 + Ohms BACnetEngineeringUnits = 4 + OhmMeters BACnetEngineeringUnits = 172 + Milliohms BACnetEngineeringUnits = 145 + Kilohms BACnetEngineeringUnits = 122 + Megohms BACnetEngineeringUnits = 123 + Microsiemens BACnetEngineeringUnits = 190 + Millisiemens BACnetEngineeringUnits = 202 + Siemens BACnetEngineeringUnits = 173 + SiemensPerMeter BACnetEngineeringUnits = 174 + Teslas BACnetEngineeringUnits = 175 + Volts BACnetEngineeringUnits = 5 + Millivolts BACnetEngineeringUnits = 124 + Kilovolts BACnetEngineeringUnits = 6 + Megavolts BACnetEngineeringUnits = 7 + VoltAmperes BACnetEngineeringUnits = 8 + KilovoltAmperes BACnetEngineeringUnits = 9 + MegavoltAmperes BACnetEngineeringUnits = 10 + VoltAmperesReactive BACnetEngineeringUnits = 11 + KilovoltAmperesReactive BACnetEngineeringUnits = 12 + MegavoltAmperesReactive BACnetEngineeringUnits = 13 + VoltsPerDegreeKelvin BACnetEngineeringUnits = 176 + VoltsPerMeter BACnetEngineeringUnits = 177 + DegreesPhase BACnetEngineeringUnits = 14 + PowerFactor BACnetEngineeringUnits = 15 + Webers BACnetEngineeringUnits = 178 + Joules BACnetEngineeringUnits = 16 + Kilojoules BACnetEngineeringUnits = 17 + KilojoulesPerKilogram BACnetEngineeringUnits = 125 + Megajoules BACnetEngineeringUnits = 126 + WattHours BACnetEngineeringUnits = 18 + KilowattHours BACnetEngineeringUnits = 19 + MegawattHours BACnetEngineeringUnits = 146 + WattHoursReactive BACnetEngineeringUnits = 203 + KilowattHoursReactive BACnetEngineeringUnits = 204 + MegawattHoursReactive BACnetEngineeringUnits = 205 + Btus BACnetEngineeringUnits = 20 + KiloBtus BACnetEngineeringUnits = 147 + MegaBtus BACnetEngineeringUnits = 148 + Therms BACnetEngineeringUnits = 21 + TonHours BACnetEngineeringUnits = 22 + JoulesPerKilogramDryAir BACnetEngineeringUnits = 23 + KilojoulesPerKilogramDryAir BACnetEngineeringUnits = 149 + MegajoulesPerKilogramDryAir BACnetEngineeringUnits = 150 + BtusPerPoundDryAir BACnetEngineeringUnits = 24 + BtusPerPound BACnetEngineeringUnits = 117 + JoulesPerDegreeKelvin BACnetEngineeringUnits = 127 + KilojoulesPerDegreeKelvin BACnetEngineeringUnits = 151 + MegajoulesPerDegreeKelvin BACnetEngineeringUnits = 152 + JoulesPerKilogramDegreeKelvin BACnetEngineeringUnits = 128 + Newton BACnetEngineeringUnits = 153 + CyclesPerHour BACnetEngineeringUnits = 25 + CyclesPerMinute BACnetEngineeringUnits = 26 + Hertz BACnetEngineeringUnits = 27 + Kilohertz BACnetEngineeringUnits = 129 + Megahertz BACnetEngineeringUnits = 130 + PerHour BACnetEngineeringUnits = 131 + GramsOfWaterPerKilogramDryAir BACnetEngineeringUnits = 28 + PercentRelativeHumidity BACnetEngineeringUnits = 29 + Micrometers BACnetEngineeringUnits = 194 + Millimeters BACnetEngineeringUnits = 30 + Centimeters BACnetEngineeringUnits = 118 + Kilometers BACnetEngineeringUnits = 193 + Meters BACnetEngineeringUnits = 31 + Inches BACnetEngineeringUnits = 32 + Feet BACnetEngineeringUnits = 33 + Candelas BACnetEngineeringUnits = 179 + CandelasPerSquareMeter BACnetEngineeringUnits = 180 + WattsPerSquareFoot BACnetEngineeringUnits = 34 + WattsPerSquareMeter BACnetEngineeringUnits = 35 + Lumens BACnetEngineeringUnits = 36 + Luxes BACnetEngineeringUnits = 37 + FootCandles BACnetEngineeringUnits = 38 + Milligrams BACnetEngineeringUnits = 196 + Grams BACnetEngineeringUnits = 195 + Kilograms BACnetEngineeringUnits = 39 + PoundsMass BACnetEngineeringUnits = 40 + Tons BACnetEngineeringUnits = 41 + GramsPerSecond BACnetEngineeringUnits = 154 + GramsPerMinute BACnetEngineeringUnits = 155 + KilogramsPerSecond BACnetEngineeringUnits = 42 + KilogramsPerMinute BACnetEngineeringUnits = 43 + KilogramsPerHour BACnetEngineeringUnits = 44 + PoundsMassPerSecond BACnetEngineeringUnits = 119 + PoundsMassPerMinute BACnetEngineeringUnits = 45 + PoundsMassPerHour BACnetEngineeringUnits = 46 + TonsPerHour BACnetEngineeringUnits = 156 + Milliwatts BACnetEngineeringUnits = 132 + Watts BACnetEngineeringUnits = 47 + Kilowatts BACnetEngineeringUnits = 48 + Megawatts BACnetEngineeringUnits = 49 + BtusPerHour BACnetEngineeringUnits = 50 + KiloBtusPerHour BACnetEngineeringUnits = 157 + Horsepower BACnetEngineeringUnits = 51 + TonsRefrigeration BACnetEngineeringUnits = 52 + Pascals BACnetEngineeringUnits = 53 + Hectopascals BACnetEngineeringUnits = 133 + Kilopascals BACnetEngineeringUnits = 54 + Millibars BACnetEngineeringUnits = 134 + Bars BACnetEngineeringUnits = 55 + PoundsForcePerSquareInch BACnetEngineeringUnits = 56 + MillimetersOfWater BACnetEngineeringUnits = 206 + CentimetersOfWater BACnetEngineeringUnits = 57 + InchesOfWater BACnetEngineeringUnits = 58 + MillimetersOfMercury BACnetEngineeringUnits = 59 + CentimetersOfMercury BACnetEngineeringUnits = 60 + InchesOfMercury BACnetEngineeringUnits = 61 + DegreesCelsius BACnetEngineeringUnits = 62 + DegreesKelvin BACnetEngineeringUnits = 63 + DegreesKelvinPerHour BACnetEngineeringUnits = 181 + DegreesKelvinPerMinute BACnetEngineeringUnits = 182 + DegreesFahrenheit BACnetEngineeringUnits = 64 + DegreeDaysCelsius BACnetEngineeringUnits = 65 + DegreeDaysFahrenheit BACnetEngineeringUnits = 66 + DeltaDegreesFahrenheit BACnetEngineeringUnits = 120 + DeltaDegreesKelvin BACnetEngineeringUnits = 121 + Years BACnetEngineeringUnits = 67 + Months BACnetEngineeringUnits = 68 + Weeks BACnetEngineeringUnits = 69 + Days BACnetEngineeringUnits = 70 + Hours BACnetEngineeringUnits = 71 + Minutes BACnetEngineeringUnits = 72 + Seconds BACnetEngineeringUnits = 73 + HundredthsSeconds BACnetEngineeringUnits = 158 + Milliseconds BACnetEngineeringUnits = 159 + NewtonMeters BACnetEngineeringUnits = 160 + MillimetersPerSecond BACnetEngineeringUnits = 161 + MillimetersPerMinute BACnetEngineeringUnits = 162 + MetersPerSecond BACnetEngineeringUnits = 74 + MetersPerMinute BACnetEngineeringUnits = 163 + MetersPerHour BACnetEngineeringUnits = 164 + KilometersPerHour BACnetEngineeringUnits = 75 + FeetPerSecond BACnetEngineeringUnits = 76 + FeetPerMinute BACnetEngineeringUnits = 77 + MilesPerHour BACnetEngineeringUnits = 78 + CubicFeet BACnetEngineeringUnits = 79 + CubicMeters BACnetEngineeringUnits = 80 + ImperialGallons BACnetEngineeringUnits = 81 + Milliliters BACnetEngineeringUnits = 197 + Liters BACnetEngineeringUnits = 82 + UsGallons BACnetEngineeringUnits = 83 + CubicFeetPerSecond BACnetEngineeringUnits = 142 + CubicFeetPerMinute BACnetEngineeringUnits = 84 + MillionCubicFeetPerMinute BACnetEngineeringUnits = 254 + CubicFeetPerHour BACnetEngineeringUnits = 191 + StandardCubicFeetPerDay BACnetEngineeringUnits = 47808 + MillionStandardCubicFeetPerDay BACnetEngineeringUnits = 47809 + ThousandCubicFeetPerDay BACnetEngineeringUnits = 47810 + ThousandStandardCubicFeetPerDay BACnetEngineeringUnits = 47811 + PoundsMassPerDay BACnetEngineeringUnits = 47812 + CubicMetersPerSecond BACnetEngineeringUnits = 85 + CubicMetersPerMinute BACnetEngineeringUnits = 165 + CubicMetersPerHour BACnetEngineeringUnits = 135 + ImperialGallonsPerMinute BACnetEngineeringUnits = 86 + MillilitersPerSecond BACnetEngineeringUnits = 198 + LitersPerSecond BACnetEngineeringUnits = 87 + LitersPerMinute BACnetEngineeringUnits = 88 + LitersPerHour BACnetEngineeringUnits = 136 + UsGallonsPerMinute BACnetEngineeringUnits = 89 + UsGallonsPerHour BACnetEngineeringUnits = 192 + DegreesAngular BACnetEngineeringUnits = 90 + DegreesCelsiusPerHour BACnetEngineeringUnits = 91 + DegreesCelsiusPerMinute BACnetEngineeringUnits = 92 + DegreesFahrenheitPerHour BACnetEngineeringUnits = 93 + DegreesFahrenheitPerMinute BACnetEngineeringUnits = 94 + JouleSeconds BACnetEngineeringUnits = 183 + KilogramsPerCubicMeter BACnetEngineeringUnits = 186 + KwHoursPerSquareMeter BACnetEngineeringUnits = 137 + KwHoursPerSquareFoot BACnetEngineeringUnits = 138 + MegajoulesPerSquareMeter BACnetEngineeringUnits = 139 + MegajoulesPerSquareFoot BACnetEngineeringUnits = 140 + NoUnits BACnetEngineeringUnits = 95 + NewtonSeconds BACnetEngineeringUnits = 187 + NewtonsPerMeter BACnetEngineeringUnits = 188 + PartsPerMillion BACnetEngineeringUnits = 96 + PartsPerBillion BACnetEngineeringUnits = 97 + Percent BACnetEngineeringUnits = 98 + PercentObscurationPerFoot BACnetEngineeringUnits = 143 + PercentObscurationPerMeter BACnetEngineeringUnits = 144 + PercentPerSecond BACnetEngineeringUnits = 99 + PerMinute BACnetEngineeringUnits = 100 + PerSecond BACnetEngineeringUnits = 101 + PsiPerDegreeFahrenheit BACnetEngineeringUnits = 102 + Radians BACnetEngineeringUnits = 103 + RadiansPerSecond BACnetEngineeringUnits = 184 + RevolutionsPerMinute BACnetEngineeringUnits = 104 + SquareMetersPerNewton BACnetEngineeringUnits = 185 + WattsPerMeterPerDegreeKelvin BACnetEngineeringUnits = 189 + WattsPerSquareMeterDegreeKelvin BACnetEngineeringUnits = 141 + PerMille BACnetEngineeringUnits = 207 + GramsPerGram BACnetEngineeringUnits = 208 + KilogramsPerKilogram BACnetEngineeringUnits = 209 + GramsPerKilogram BACnetEngineeringUnits = 210 + MilligramsPerGram BACnetEngineeringUnits = 211 + MilligramsPerKilogram BACnetEngineeringUnits = 212 + GramsPerMilliliter BACnetEngineeringUnits = 213 + GramsPerLiter BACnetEngineeringUnits = 214 + MilligramsPerLiter BACnetEngineeringUnits = 215 + MicrogramsPerLiter BACnetEngineeringUnits = 216 + GramsPerCubicMeter BACnetEngineeringUnits = 217 + MilligramsPerCubicMeter BACnetEngineeringUnits = 218 + MicrogramsPerCubicMeter BACnetEngineeringUnits = 219 + NanogramsPerCubicMeter BACnetEngineeringUnits = 220 + GramsPerCubicCentimeter BACnetEngineeringUnits = 221 + Becquerels BACnetEngineeringUnits = 222 + Kilobecquerels BACnetEngineeringUnits = 223 + Megabecquerels BACnetEngineeringUnits = 224 + Gray BACnetEngineeringUnits = 225 + Milligray BACnetEngineeringUnits = 226 + Microgray BACnetEngineeringUnits = 227 + Sieverts BACnetEngineeringUnits = 228 + Millisieverts BACnetEngineeringUnits = 229 + Microsieverts BACnetEngineeringUnits = 230 + MicrosievertsPerHour BACnetEngineeringUnits = 231 + Millirems BACnetEngineeringUnits = 47814 + MilliremsPerHour BACnetEngineeringUnits = 47815 + DecibelsA BACnetEngineeringUnits = 232 + NephelometricTurbidityUnit BACnetEngineeringUnits = 233 + Ph BACnetEngineeringUnits = 234 + GramsPerSquareMeter BACnetEngineeringUnits = 235 + MinutesPerDegreeKelvin BACnetEngineeringUnits = 236 + MeterSquaredPerMeter BACnetEngineeringUnits = 237 + AmpereSeconds BACnetEngineeringUnits = 238 + VoltAmpereHours BACnetEngineeringUnits = 239 + KilovoltAmpereHours BACnetEngineeringUnits = 240 + MegavoltAmpereHours BACnetEngineeringUnits = 241 + VoltAmpereHoursReactive BACnetEngineeringUnits = 242 + KilovoltAmpereHoursReactive BACnetEngineeringUnits = 243 + MegavoltAmpereHoursReactive BACnetEngineeringUnits = 244 + VoltSquareHours BACnetEngineeringUnits = 245 + AmpereSquareHours BACnetEngineeringUnits = 246 + JoulePerHours BACnetEngineeringUnits = 247 + CubicFeetPerDay BACnetEngineeringUnits = 248 + CubicMetersPerDay BACnetEngineeringUnits = 249 + WattHoursPerCubicMeter BACnetEngineeringUnits = 250 + JoulesPerCubicMeter BACnetEngineeringUnits = 251 + MolePercent BACnetEngineeringUnits = 252 + PascalSeconds BACnetEngineeringUnits = 253 + MillionStandardCubicFeetPerMinute BACnetEngineeringUnits = 254 +) + +type BACnetEscalatorMode int + +const ( + BacnetescalatorModeUnknown BACnetEscalatorMode = iota + BacnetescalatorModeStop + BACnetEscalatorModeUp + BACnetEscalatorModeDown + BACnetEscalatorModeInspection + BacnetescalatorModeOutOfService +) + +type BACnetEscalatorOperationDirection int + +const ( + BacnetEscalatorOperationDirectionUnknown BACnetEscalatorOperationDirection = iota + BACnetEscalatorOperationDirectionStopped + UpRatedSpeed + UpReducedSpeed + DownRatedSpeed + DownReducedSpeed +) + +type BACnetFileAccessMethod int + +const ( + RecordAccess BACnetFileAccessMethod = iota + StreamAccess +) + +type BACnetIPMode int + +const ( + BacnetIPModeNormal BACnetIPMode = iota + Foreign + Bbmd +) + +type BACnetLifeSafetyMode int + +const ( + BacnetLifeSafetyModeOff BACnetLifeSafetyMode = iota + Lon + Test + Manned + Unmanned + Armed + Disarmed + Prearmed + Slow + Fast + Disconnected + Enabled + BacnetLifeSafetyModeDisabled + AutomaticReleaseDisabled + Default +) + +type BACnetLifeSafetyOperation int + +const ( + BacnetLifeSafetyOperationNone BACnetLifeSafetyOperation = iota + Silence + SilenceAudible + SilenceVisual + Reset + ResetAlarm + ResetFault + Unsilence + UnsilenceAudible + UnsilenceVisual +) + +type BACnetLifeSafetyState int + +const ( + Quiet BACnetLifeSafetyState = iota + PreAlarm + BacnetlifesafetystateAlarm + BacnetlifesafetystateFault + FaultPreAlarm + FaultAlarm + BacnetlifesafetystateNotReady + BacnetlifesafetystateActive + BacnetlifesafetystateTamper + TestAlarm + TestActive + TestFault + TestFaultAlarm + Holdup + Duress + TamperAlarm + Abnormal + BacnetlifesafetystateEmergencyPower + Delayed + Blocked + LocalAlarm + GeneralAlarm + Supervisory + TestSupervisory +) + +type BACnetLiftCarDirection int + +const ( + BacnetliftcardirectionUnknown BACnetLiftCarDirection = iota + BacnetliftcardirectionNone + Stopped + Up + Down + UpAndDown +) + +type BACnetLiftCarDoorCommand int + +const ( + BACnetLiftCarDoorCommandNone BACnetLiftCarDoorCommand = iota + Open + Close +) + +type BACnetLiftCarDriveStatus int + +const ( + BACnetLiftCarDriveStatusUnknown BACnetLiftCarDriveStatus = iota + Stationary + Braking + Accelerate + Decelerate + RatedSpeed + SingleFloorJump + TwoFloorJump + ThreeFloorJump + MultiFloorJump +) + +type BACnetLiftCarMode int + +const ( + BACnetLiftCarModeUnknown BACnetLiftCarMode = iota + BACnetLiftCarModeNormal + Vip + Homing + Parking + AttendantControl + FirefighterControl + BACnetLiftCarModeEmergencyPower + Inspection + CabinetRecall + EarthquakeOperation + FireOperation + BACnetLiftCarModeOutOfService + OccupantEvacuation +) + +type BACnetLiftFault int + +const ( + ControllerFault BACnetLiftFault = iota + DriveAndMotorFault + GovernorAndSafetyGearFault + LiftShaftDeviceFault + PowerSupplyFault + SafetyInterlockFault + DoorClosingFault + DoorOpeningFault + CarStoppedOutsideLandingZone + CallButtonStuck + StartFailure + ControllerSupplyFault + SelfTestFailure + RuntimeLimitExceeded + PositionLost + DriveTemperatureExceeded + LoadMeasurementFault +) + +type BACnetLiftGroupMode int + +const ( + BACnetLiftGroupModeUnknown BACnetLiftGroupMode = iota + BACnetLiftGroupModeNormal + DownPeak + TwoWay + FourWay + EmergencyPower + UpPeak +) + +type BACnetLoggingType int + +const ( + Polled BACnetLoggingType = iota + Cov + Triggered +) + +type BACnetMaintenance int + +const ( + BACnetMaintenanceNone BACnetMaintenance = iota + PeriodicTest + NeedServiceOperational + NeedServiceInoperative +) + +type BACnetNetworkPortCommand int + +const ( + BACnetNetworkPortCommandIdle BACnetNetworkPortCommand = iota + DiscardChanges + RenewFdRegistration + RestartSlaveDiscovery + RenewDhcp + RestartAutonegotiation + Disconnect + RestartPort +) + +type BACnetNodeType int + +const ( + BACnetNodeTypeUnknown BACnetNodeType = iota + System + Network + BACnetNodeTypeDevice + Organizational + Area + Equipment + Point + Collection + BACnetNodeTypeProperty + Functional + BACnetNodeTypeOther + Subsystem + Building + Floor + Section + Module + Tree + Member + Protocol + Room + Zone +) + +type BACnetRelationship int + +const ( + BACnetRelationshipUnknown BACnetRelationship = iota + BACnetRelationshipDefault + Contains + ContainedBy + Uses + UsedBy + Commands + CommandedBy + Adjusts + AdjustedBy + Ingress + Egress + SuppliesAir + ReceivesAir + SuppliesHotAir + ReceivesHotAir + SuppliesCoolAir + ReceivesCoolAir + SuppliesPower + ReceivesPower + SuppliesGas + ReceivesGas + SuppliesWater + ReceivesWater + SuppliesHotWater + ReceivesHotWater + SuppliesCoolWater + ReceivesCoolWater + SuppliesSteam + ReceivesSteam +) + +type BACnetReliability int + +const ( + NoFaultDetected BACnetReliability = iota + NoSensor + OverRange + UnderRange + OpenLoop + ShortedLoop + NoOutput + UnreliableOther + ProcessError + MultiStateFault + ConfigurationError + CommunicationFailure BACnetReliability = iota + 1 + MemberFault + MonitoredObjectFault + Tripped + LampFailure + ActivationFailure + RenewDhcpFailure + RenewFdRegistrationFailure + RestartAutoNegotiationFailure + RestartFailure + ProprietaryCommandFailure + FaultsListed + ReferencedObjectFault +) + +type BACnetRestartReason int + +const ( + BACnetRestartReasonUnknown BACnetRestartReason = iota + ColdStart + WarmStart + DetectedPowerLost + DetectedPowerOff + HardwareWatchdog + SoftwareWatchdog + Suspended +) + +type BACnetSecurityLevel int + +const ( + Incapable BACnetSecurityLevel = iota + Plain + Signed + Encrypted + SignedEndToEnd + EncryptedEndToEnd +) + +type BACnetPolarity int + +const ( + BACnetPolarityNormal BACnetPolarity = iota + BACnetPolarityReverse +) + +type BACnetProtocolLevel int + +const ( + Physical BACnetProtocolLevel = iota + BACnetProtocolLevelProtocol + BACnetApplication + NonBACnetApplication +) + +type BACnetSilencedState int + +const ( + Unsilenced BACnetSilencedState = iota + AudibleSilenced + VisibleSilenced + AllSilenced +) + +type BACnetTimerState int + +const ( + BACnetTimerStateIdle BACnetTimerState = iota + Running + Expired +) + +type BACnetTimerTransition int + +const ( + BACnetTimerTransitionNone BACnetTimerTransition = iota + IdleToRunning + RunningToIdle + RunningToRunning + RunningToExpired + ForcedToExpired + ExpiredToIdle + ExpiredToRunning +) + +type BACnetVTClass int + +const ( + DefaultTerminal BACnetVTClass = iota + ANSI_X3_64 + DEC_VT52 + DEC_VT100 + DEC_VT220 + HP_700_94 + IBM_3130 +) + +type BACnetAccessEvent int + +const ( + BACnetAccessEventNone BACnetAccessEvent = iota + Granted + Muster + PassbackDetected + BACnetAccessEventDuress + Trace + LockoutMaxAttempts + LockoutOther + LockoutRelinquished + LockedByHigherPriority + BACnetAccessEventOutOfService + OutOfServiceRelinquished + AccompanimentBy + AuthenticationFactorRead + BACnetAccessEventAuthorizationDelayed + BACnetAccessEventVerificationRequired + NoEntryAfterGrant + DeniedDenyAll BACnetAccessEvent = iota + 111 + DeniedUnknownCredential + DeniedAuthenticationUnavailable + DeniedAuthenticationFactorTimeout + DeniedIncorrectAuthenticationFactor + DeniedZoneNoAccessRights + DeniedPointNoAccessRights + DeniedNoAccessRights + DeniedOutOfTimeRange + DeniedThreatLevel + DeniedPassback + DeniedUnexpectedLocationUsage + DeniedMaxAttempts + DeniedLowerOccupancyLimit + DeniedUpperOccupancyLimit + DeniedAuthenticationFactorLost + DeniedAuthenticationFactorStolen + DeniedAuthenticationFactorDamaged + DeniedAuthenticationFactorDestroyed + DeniedAuthenticationFactorDisabled + DeniedAuthenticationFactorError + DeniedCredentialUnassigned + DeniedCredentialNotProvisioned + DeniedCredentialNotYetActive + DeniedCredentialExpired + DeniedCredentialManualDisable + DeniedCredentialLockout + DeniedCredentialMaxDays + DeniedCredentialMaxUses + DeniedCredentialInactivity + DeniedCredentialDisabled + DeniedNoAccompaniment + DeniedIncorrectAccompaniment + DeniedLockout + DeniedVerificationFailed + DeniedVerificationTimeout + DeniedOther +) + +type BACnetLightingInProgress int + +const ( + BACnetLightingInProgressIdle BACnetLightingInProgress = iota + FadeActive + RampActive + NotControlled + BACnetLightingInProgressOther +) + +type BACnetLightingOperation int + +const ( + BACnetLightingOperationNone BACnetLightingOperation = iota + FadeTo + RampTo + StepUp + StepDown + StepOn + StepOff + BACnetLightingOperationWarn + BACnetLightingOperationWarnOff + BACnetLightingOperationWarnRelinquish + BACnetLightingOperationStop +) + +type BACnetLightingTransition int + +const ( + BACnetLightingTransitionNone BACnetLightingTransition = iota + Fade + Ramp +) + +type BACnetLockStatus int + +const ( + Locked BACnetLockStatus = iota + Unlocked + LockFault + Unused + BACnetLockStatusUnknown +) + +type BACnetEscalatorFault int + +const ( + BACnetescalatorfaultControllerFault BACnetEscalatorFault = iota + BACnetescalatorfaultDriveAndMotorFault + MechanicalComponentFault + OverspeedFault + BACnetescalatorfaultPowerSupplyFault + SafetyDeviceFault + BACnetescalatorfaultControllerSupplyFault + BACnetescalatorfaultDriveTemperatureExceeded + CombPlateFault +) + +type BACnetProgramError int + +const ( + BACnetProgramErrorNormal = iota + LoadFailed + Internal + BACnetProgramErrorProgram + BACnetProgramErrorOther +) + +type BACnetProgramRequest int + +const ( + BACnetProgramRequestReady = iota + Load + Run + Halt + Restart + Unload +) + +type BACnetProgramState int + +const ( + BACnetProgramStateIdle BACnetProgramState = iota + Loading + BACnetProgramStateRunning + Waiting + Halted + Unloading +) + +type BACnetShedState int + +const ( + BACnetShedStateInactive BACnetShedState = iota + RequestPending + Compliant + NonCompliant +) + +type BACnetWriteStatus int + +const ( + BACnetWriteStatusIdle BACnetWriteStatus = iota + BACnetWriteStatusInProgress + Successful + Failed +) + +type VendorSpecificValue int + +func DecodeEnumerated(buffer []byte, offset int, lenValue uint32, objType *ObjectType, propID *PropertyIdentifier) (length int, val interface{}) { + leng, value := DecodeUnsigned(buffer, offset, int(lenValue)) + if propID != nil { + switch *propID { + case SegmentationSupported: + val = BACnetSegmentation(value) + case PropertyList: + val = PropertyIdentifier(value) + case EventType: + val = BACnetEventType(value) + case NotifyType: + val = BACnetNotifyType(value) + case FaultType: + val = BACnetFaultType(value) + case EventState: + val = BACnetEventState(value) + case ObjectTypePI: + val = ObjectType(value) + case ReasonForDisable: + val = BACnetAccessCredentialDisableReason(value) + case CredentialDisable: + val = BACnetAccessCredentialDisable(value) + case PassbackMode: + val = BACnetAccessPassbackMode(value) + case UserType: + val = BACnetAccessUserType(value) + case NetworkNumberQuality: + val = BACnetNetworkNumberQuality(value) + case OccupancyState: + val = BACnetAccessZoneOccupancyState(value) + case Action: + if *objType == Loop { + val = BACnetAction(value) + } + case PresentValue, AlarmValue, FeedbackValue, RelinquishDefault: + switch *objType { + case BinaryInput, BinaryOutput, BinaryValue: + val = BACnetBinaryPV(value) + case AccessDoor: + val = BACnetDoorValue(value) + case LifeSafetyPoint, LifeSafetyZone: + val = BACnetLifeSafetyState(value) + case LightingOutput: + val = BACnetBinaryLightingPV(value) + case LoadControl: + val = BACnetShedState(value) + } + case AuthenticationStatus: + val = BACnetAuthenticationStatus(value) + case AuthorizationExemptions: + val = BACnetAuthorizationExemption(value) + case AuthorizationMode: + val = BACnetAuthorizationMode(value) + case BackupAndRestoreState: + val = BACnetBackupState(value) + case SystemStatus: + val = BACnetDeviceStatus(value) + case SecuredStatus: + val = BACnetDoorSecuredStatus(value) + case DoorStatus, CarDoorStatus: + val = BACnetDoorStatus(value) + case Units, CarLoadUnits: + val = BACnetEngineeringUnits(value) + case EscalatorMode: + val = BACnetEscalatorMode(value) + case OperationDirection: + val = BACnetEscalatorOperationDirection(value) + case FileAccessMethod: + val = BACnetFileAccessMethod(value) + case OperationExpected: + val = BACnetLifeSafetyOperation(value) + case CarDoorCommand: + val = BACnetLiftCarDoorCommand(value) + case CarDriveStatus: + val = BACnetLiftCarDriveStatus(value) + case CarMode: + val = BACnetLiftCarMode(value) + case GroupMode: + val = BACnetLiftGroupMode(value) + case LoggingType: + val = BACnetLoggingType(value) + case Reliability: + val = BACnetReliability(value) + case LastRestartReason: + val = BACnetRestartReason(value) + case NetworkType: + val = BACnetNetworkType(value) + case BaseDeviceSecurityPolicy: + val = BACnetSecurityLevel(value) + case CarMovingDirection, CarAssignedDirection: + val = BACnetLiftCarDirection(value) + case BacnetIpMode, BacnetIpv6Mode: + val = BACnetIPMode(value) + case MaintenanceRequired: + val = BACnetMaintenance(value) + case Polarity: + val = BACnetPolarity(value) + case ProtocolLevel: + val = BACnetProtocolLevel(value) + case Silenced: + val = BACnetSilencedState(value) + case AccessEvent, AccessAlarmEvents, AccessTransactionEvents, FailedAttemptEvents: + if *objType == AccessPoint { + val = BACnetAccessEvent(value) + } + case LastAccessEvent: + if *objType == AccessCredential { + val = BACnetAccessEvent(value) + } + case CredentialStatus: + if *objType == AccessCredential { + val = BACnetBinaryPV(value) + } + case LockStatus: + if *objType == AccessDoor { + val = BACnetLockStatus(value) + } + case DoorAlarmState, MaskedAlarmValues, AlarmValues, FaultValues: + switch *objType { + case AccessDoor: + val = BACnetDoorAlarmState(value) + case LifeSafetyPoint, LifeSafetyZone: + val = BACnetLifeSafetyState(value) + case Timer: + val = BACnetTimerState(value) + } + case Mode, AcceptedModes: + if *objType == LifeSafetyPoint || *objType == LifeSafetyZone { + val = BACnetLifeSafetyMode(value) + } + case TrackingValue, LifeSafetyAlarmValues: + if *objType == LifeSafetyPoint || *objType == LifeSafetyZone { + val = BACnetLifeSafetyState(value) + } + case FaultSignals: + switch *objType { + case Escalator: + val = BACnetEscalatorFault(value) + case Lift: + val = BACnetLiftFault(value) + } + case InProgress: + if *objType == LightingOutput { + val = BACnetLightingInProgress(value) + } + case Transition: + if *objType == LightingOutput { + val = BACnetLightingTransition(value) + } + case Command: + if *objType == NetworkPort { + val = BACnetNetworkPortCommand(value) + } + case NodeType, SubordinateNodeTypes: + if *objType == StructuredView { + val = BACnetNodeType(value) + } + case SubordinateRelationships, DefaultSubordinateRelationship: + if *objType == StructuredView { + val = BACnetRelationship(value) + } + case ReasonForHalt: + if *objType == Program { + val = BACnetProgramError(value) + } + case ProgramChange: + if *objType == Program { + val = BACnetProgramRequest(value) + } + case ProgramState: + if *objType == Program { + val = BACnetProgramState(value) + } + case TimerState: + if *objType == Timer { + val = BACnetTimerState(value) + } + case LastStateChange: + if *objType == Timer { + val = BACnetTimerTransition(value) + } + case VtClassesSupported: + val = BACnetVTClass(value) + case WriteStatus: + if *objType == Channel { + val = BACnetWriteStatus(value) + } + default: + val = VendorSpecificValue(value) + } + + return leng, val + } + return leng, value +} + +func EncodeContextEnumerated(tagNumber BACnetApplicationTag, value uint32) []byte { + length := 0 + if value < 0x100 { + length = 1 + } else if value < 0x10000 { + length = 2 + } else if value < 0x1000000 { + length = 3 + } else { + length = 4 + } + + return append(EncodeTag(tagNumber, true, length), EncodeUnsigned(value)...) +} diff --git a/pkg/encoding/tags.go b/pkg/encoding/tags.go index b5888de..0507038 100644 --- a/pkg/encoding/tags.go +++ b/pkg/encoding/tags.go @@ -37,7 +37,7 @@ func isClosingTag(b byte) bool { return (b & 0x07) == 7 } -func isContextSpecific(b byte) bool { +func IsContextSpecific(b byte) bool { return (b & 0x8) == 0x8 } @@ -50,15 +50,15 @@ func decodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { return len, buf[offset] >> 4 } -func DecodeIsCOntextTag(buf []byte, offset int, tagNum byte) bool { +func IsContextTag(buf []byte, offset int, tagNum byte) bool { _, myTagNum := decodeTagNumber(buf, offset) - return isContextSpecific(buf[offset]) && myTagNum == tagNum + return IsContextSpecific(buf[offset]) && myTagNum == tagNum } -func DecodeIsCOntextTagWithLength(buf []byte, offset int, tagNum byte) (int, bool) { +func IsContextTagWithLength(buf []byte, offset int, tagNum byte) (int, bool) { tagLen, myTagNum := decodeTagNumber(buf, offset) - return tagLen, isContextSpecific(buf[offset]) && myTagNum == tagNum + return tagLen, IsContextSpecific(buf[offset]) && myTagNum == tagNum } func DecodeTagNumberAndValue(buf []byte, offset int) (len int, tagNum byte, val uint32) { diff --git a/pkg/transport/udp/broadcast.go b/pkg/transport/udp/broadcast.go index 3de1911..f4edff2 100644 --- a/pkg/transport/udp/broadcast.go +++ b/pkg/transport/udp/broadcast.go @@ -5,6 +5,7 @@ import ( "strconv" "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" ) func GetBroadcastAddress(localEndpoint string, port int) (bacnet.BACnetAddress, error) { @@ -32,7 +33,7 @@ func GetBroadcastAddress(localEndpoint string, port int) (bacnet.BACnetAddress, } } } - netType := bacnet.IPV4 + netType := encoding.IPV4 return *bacnet.NewBACnetAddress(0xFFFF, nil, broadcast+":"+strconv.Itoa(port), &netType), nil } From f231ed7fc677350ff2ecbbafafb9ace9c6c84efe Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 11 Sep 2023 00:33:01 +0300 Subject: [PATCH 13/25] add read property Signed-off-by: SammyOina --- example/readProperty/readProperty.go | 105 +++++++++++++++++++++++++++ example/{ => whoIs}/whois.go | 11 ++- pkg/bacnet/iam.go | 9 +-- pkg/bacnet/network.go | 2 +- pkg/bacnet/property.go | 2 +- pkg/encoding/property.go | 8 +- 6 files changed, 123 insertions(+), 14 deletions(-) create mode 100644 example/readProperty/readProperty.go rename example/{ => whoIs}/whois.go (89%) diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go new file mode 100644 index 0000000..73c31d9 --- /dev/null +++ b/example/readProperty/readProperty.go @@ -0,0 +1,105 @@ +package main + +import ( + "fmt" + "log" + "net" + "time" + + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" + "github.com/absmach/bacnet/pkg/transport" +) + +func main() { + netType := encoding.IPV4 + /*deviceID := bacnet.ObjectIdentifier{ + Type: encoding.AnalogInput, + Instance: 101, + }*/ + destination := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809", &netType) + npdu := bacnet.NewNPDU(destination, nil, nil, nil) + npdu.Control.SetDataExpectingReply(true) + npdu.Control.SetNetworkPriority(bacnet.NormalMessage) + + npduBytes, err := npdu.Encode() + if err != nil { + log.Fatal(err) + } + + apdu := bacnet.APDU{ + PduType: bacnet.PDUTypeConfirmedServiceRequest, + ServiceChoice: byte(bacnet.ReadProperty), + SegmentedResponseAccepted: false, + MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), + //MaxApduLengthAccepted: bacnet.MaxAPDU1476, + InvokeID: 0, + } + + req := bacnet.ReadPropertyRequest{ + PropertyIdentifier: encoding.PresentValue, + ObjectIdentifier: &bacnet.ObjectIdentifier{Type: encoding.AnalogInput, Instance: 10}, + } + + mes := append(npduBytes, apdu.Encode()...) + mes = append(mes, req.Encode()...) + + blvc := bacnet.NewBVLC(transport.IP) + blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) + message := append(blvcBytes, mes...) + + fmt.Println(message) + + message = []byte{129, 10, 0, 17, 1, 4, 0, 5, 1, 12, 12, 0, 0, 0, 10, 25, 85} + fmt.Println(message) + + // Define the BACnet broadcast address (255.255.255.255:47808) + remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") + if err != nil { + fmt.Println("Error resolving remote address:", err) + return + } + + localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + if err != nil { + fmt.Println("Error: ", err) + return + } + + // Create a UDP connectionBACnetAddress + conn, err := net.DialUDP("udp", localAddr, remoteAddr) + if err != nil { + fmt.Println("Error creating UDP connection:", err) + return + } + defer conn.Close() + + // Send the WhoIsRequest packet + _, err = conn.Write(message) + if err != nil { + log.Fatal("Error sending WhoIsRequest:", err) + } + + // Wait for responses + buffer := make([]byte, 1500) + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Set a timeout for responses + + for { + //conn.ReadFromUDPAddrPort() + n, _, err := conn.ReadFromUDP(buffer) + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + // Timeout reached, no more responses + log.Println("No more responses received.") + break + } + log.Println("Error reading response:", err) + break + } + + // Process the response (you'll need to parse BACnet responses here) + response := buffer[:n] + log.Printf("Received response: %X\n", response) + + } +} diff --git a/example/whois.go b/example/whoIs/whois.go similarity index 89% rename from example/whois.go rename to example/whoIs/whois.go index 11a6b07..4310f6f 100644 --- a/example/whois.go +++ b/example/whoIs/whois.go @@ -50,14 +50,20 @@ func main() { message := append(blvcBytes, mes...) // Define the BACnet broadcast address (255.255.255.255:47808) - remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.255:47809") + remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") if err != nil { fmt.Println("Error resolving remote address:", err) return } + localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + if err != nil { + fmt.Println("Error: ", err) + return + } + // Create a UDP connectionBACnetAddress - conn, err := net.DialUDP("udp", nil, remoteAddr) + conn, err := net.DialUDP("udp", localAddr, remoteAddr) if err != nil { fmt.Println("Error creating UDP connection:", err) return @@ -89,5 +95,6 @@ func main() { // Process the response (you'll need to parse BACnet responses here) response := buffer[:n] log.Printf("Received response: %X\n", response) + fmt.Println(response) } } diff --git a/pkg/bacnet/iam.go b/pkg/bacnet/iam.go index bfad5be..4b0707d 100644 --- a/pkg/bacnet/iam.go +++ b/pkg/bacnet/iam.go @@ -1,10 +1,9 @@ package bacnet -/* import ( "errors" - "github.com/absmach/bacnet/encoding" + "github.com/absmach/bacnet/pkg/encoding" ) type IAmRequest struct { @@ -27,8 +26,7 @@ func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, leng = iam.IamDeviceIdentifier.Decode(buffer, offset+leng, int(lenValue)) - if iam.IamDeviceIdentifier.Type != ObjectTypeDevice { - // Handle error or log message + if iam.IamDeviceIdentifier.Type != encoding.ObjectTypeDevice { return -1, errors.New("Got Iam from no device") } @@ -49,7 +47,7 @@ func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, if tagNumber != byte(Enumerated) { return -1, errors.New("Invalid tag number") } - segmentationSupported, err := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, SegmentationSupported) + segmentationSupported, err := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, encoding.SegmentationSupported) if err != nil { return -1, err } @@ -167,4 +165,3 @@ func (youAre *YouAreRequest) ASN1Encode() []byte { return buffer } -*/ diff --git a/pkg/bacnet/network.go b/pkg/bacnet/network.go index 15650e5..aeb5c3c 100644 --- a/pkg/bacnet/network.go +++ b/pkg/bacnet/network.go @@ -71,7 +71,7 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface } case ObjectIdentifier: if *netType == encoding.IPV4 { - addr.MacAddress = make([]byte, 6) + addr.MacAddress = make([]byte, 8) binary.LittleEndian.PutUint64(addr.MacAddress, uint64(addr1.Instance)) } } diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index f7b2124..0a10a46 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -24,7 +24,7 @@ func (r ReadPropertyRequest) Encode() []byte { ret = append(ret, r.ObjectIdentifier.EncodeContext(0)...) } - propID := r.PropertyIdentifier.(int) + propID := r.PropertyIdentifier.(encoding.PropertyIdentifier) ret = append(ret, encoding.EncodeContextEnumerated(1, uint32(propID))...) diff --git a/pkg/encoding/property.go b/pkg/encoding/property.go index 4428f67..2ed48a0 100644 --- a/pkg/encoding/property.go +++ b/pkg/encoding/property.go @@ -561,10 +561,10 @@ const ( type BACnetSegmentation int const ( - SEGMENTED_BOTH BACnetSegmentation = iota - SEGMENTED_TRANSMIT - SEGMENTED_RECEIVE - NO_SEGMENTATION + SegmentedBoth BACnetSegmentation = iota + SegmentedTransmit + SegmentedReceive + NoSegmentation ) type BACnetEventType int From 76792fab135d36b63fea181164bbe99f58097c87 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 11 Sep 2023 10:09:26 +0300 Subject: [PATCH 14/25] add iam and you are Signed-off-by: SammyOina --- go.sum | 0 pkg/bacnet/base.go | 17 +-------- pkg/bacnet/iam.go | 37 +++++++------------ pkg/encoding/decoding.go | 80 ++++++++++++++++++++++++++++++++++++++++ pkg/encoding/encoding.go | 30 +++++++++++++++ 5 files changed, 126 insertions(+), 38 deletions(-) create mode 100644 go.sum diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/pkg/bacnet/base.go b/pkg/bacnet/base.go index 7a550e4..19e857f 100644 --- a/pkg/bacnet/base.go +++ b/pkg/bacnet/base.go @@ -55,8 +55,8 @@ const ( NegativeAck // TODO SegmentResponseAccepted - MORE_FOLLOWS - SEGMENTED_MESSAGE BacnetPduTypes = 8 + MoreFollows + SegmentedMessage BacnetPduTypes = 8 PDUTypeUnconfirmedServiceRequest BacnetPduTypes = 0x10 PDUTypeSimpleAck BacnetPduTypes = 0x20 PDUTypeComplexAck BacnetPduTypes = 0x30 @@ -67,19 +67,6 @@ const ( PDUTypeMask BacnetPduTypes = 0xF0 ) -type BacnetCharacterStringEncodings int - -const ( - CharacterANSIX34 BacnetCharacterStringEncodings = 0 - CharacterUTF8 BacnetCharacterStringEncodings = 0 - CharacterMSDBCS BacnetCharacterStringEncodings = 1 - CharacterJISC6226 BacnetCharacterStringEncodings = 2 - CharacterJISX0208 BacnetCharacterStringEncodings = 2 - CharacterUCS4 BacnetCharacterStringEncodings = 3 - CharacterUCS2 BacnetCharacterStringEncodings = 4 - CharacterISO8859 BacnetCharacterStringEncodings = 5 -) - type BacnetNpduControl int const ( diff --git a/pkg/bacnet/iam.go b/pkg/bacnet/iam.go index 4b0707d..e53723c 100644 --- a/pkg/bacnet/iam.go +++ b/pkg/bacnet/iam.go @@ -9,11 +9,11 @@ import ( type IAmRequest struct { IamDeviceIdentifier ObjectIdentifier MaxAPDULengthAccepted uint32 - SegmentationSupported int + SegmentationSupported interface{} VendorID uint32 } -func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, error) { +func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, error) { leng := 0 iam.IamDeviceIdentifier = ObjectIdentifier{} // OBJECT ID - object id @@ -47,10 +47,8 @@ func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, if tagNumber != byte(Enumerated) { return -1, errors.New("Invalid tag number") } - segmentationSupported, err := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, encoding.SegmentationSupported) - if err != nil { - return -1, err - } + propID := encoding.SegmentationSupported + leng1, segmentationSupported := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) leng += leng1 iam.SegmentationSupported = segmentationSupported @@ -63,9 +61,6 @@ func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, } leng1, decodedValue = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) - if err != nil { - return -1, err - } leng += leng1 if decodedValue > 0xFFFF { @@ -76,9 +71,10 @@ func (iam *IAmRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, return leng, nil } -func (iam *IAmRequest) ASN1Encode() []byte { +func (iam IAmRequest) Encode() []byte { tmp := iam.IamDeviceIdentifier.Encode() - return append(append(append(append([]byte{}, encoding.EncodeTag(encoding.BACnetApplicationTag(BACnetObjectIdentifier), false, len(tmp))...), tmp...), encoding.EncodeApplicationUnsigned(iam.MaxAPDULengthAccepted)...), encoding.EncodeApplicationEnumerated(iam.SegmentationSupported, SegmentationSupported), encoding.EncodeApplicationUnsigned(iam.VendorID)...) + propID := iam.SegmentationSupported.(encoding.PropertyIdentifier) + return append(append(append(append(encoding.EncodeTag(encoding.BACnetApplicationTag(BACnetObjectIdentifier), false, len(tmp)), tmp...), encoding.EncodeApplicationUnsigned(iam.MaxAPDULengthAccepted)...), encoding.EncodeApplicationEnumerated(uint32(propID))...), encoding.EncodeApplicationUnsigned(iam.VendorID)...) } type YouAreRequest struct { @@ -89,7 +85,7 @@ type YouAreRequest struct { DeviceMACAddress []byte } -func (youAre *YouAreRequest) ASN1Decode(buffer []byte, offset int, apduLen int) (int, error) { +func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int, error) { leng := 0 leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) @@ -105,11 +101,9 @@ func (youAre *YouAreRequest) ASN1Decode(buffer []byte, offset int, apduLen int) leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 if tagNumber == byte(CharacterString) { - decodedValue, err := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, lenValue) - if err != nil { - return -1, err - } - leng += decodedValue + leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + + leng += leng1 youAre.ModelName = decodedValue } else { return -1, errors.New("Invalid tag number") @@ -118,11 +112,8 @@ func (youAre *YouAreRequest) ASN1Decode(buffer []byte, offset int, apduLen int) leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 if tagNumber == byte(CharacterString) { - decodedValue, err := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, lenValue) - if err != nil { - return -1, err - } - leng += decodedValue + leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + leng += leng1 youAre.SerialNumber = decodedValue } else { return -1, errors.New("Invalid tag number") @@ -150,7 +141,7 @@ func (youAre *YouAreRequest) ASN1Decode(buffer []byte, offset int, apduLen int) return leng, nil } -func (youAre *YouAreRequest) ASN1Encode() []byte { +func (youAre YouAreRequest) Encode() []byte { buffer := append(append(append([]byte{}, encoding.EncodeApplicationUnsigned(youAre.VendorID)...), encoding.EncodeApplicationCharacterString(youAre.ModelName)...), encoding.EncodeApplicationCharacterString(youAre.SerialNumber)...) diff --git a/pkg/encoding/decoding.go b/pkg/encoding/decoding.go index bcdeb14..52065e5 100644 --- a/pkg/encoding/decoding.go +++ b/pkg/encoding/decoding.go @@ -1,5 +1,18 @@ package encoding +type BacnetCharacterStringEncodings int + +const ( + CharacterANSIX34 BacnetCharacterStringEncodings = 0 + CharacterUTF8 BacnetCharacterStringEncodings = 0 + CharacterMSDBCS BacnetCharacterStringEncodings = 1 + CharacterJISC6226 BacnetCharacterStringEncodings = 2 + CharacterJISX0208 BacnetCharacterStringEncodings = 2 + CharacterUCS4 BacnetCharacterStringEncodings = 3 + CharacterUCS2 BacnetCharacterStringEncodings = 4 + CharacterISO8859 BacnetCharacterStringEncodings = 5 +) + func DecodeUnsigned(buffer []byte, offset, len int) (int, uint32) { value := uint32(0) for i := 0; i < len; i++ { @@ -13,3 +26,70 @@ func DecodeOctetString(buf []byte, offset, lenVal int) (int, []byte) { copy(tmp, buf[offset:offset+lenVal]) return len(tmp), tmp } + +// multiCharsetCharacterstringDecode decodes a multi-character set character string. +func multiCharsetCharacterStringDecode(buffer []byte, offset, maxLength int, encoding BacnetCharacterStringEncodings, length int) (bool, string) { + var charString, enc string + + switch encoding { + case CharacterUCS2: + enc = "utf-16" + case CharacterUCS4: + enc = "utf-32" + case CharacterISO8859: + enc = "latin-1" + case CharacterJISX0208: + enc = "shift_jisx0213" + case CharacterMSDBCS: + enc = "dbcs" + default: + enc = "utf-8" + } + + c := make([]byte, 0) + for i := 0; i < length; i++ { + c = append(c, buffer[offset+i]) + } + + if enc == "utf-8" { + charString = string(c) + } else { + charString = string(c) + charString = string([]rune(charString)) // Convert to Unicode + } + + return true, charString +} + +func DecodeCharacterString(buffer []byte, offset, maxLength, lenValue int) (int, string) { + leng := 0 + status := false + var charString string + + status, charString = multiCharsetCharacterStringDecode(buffer, offset+1, maxLength, BacnetCharacterStringEncodings(buffer[offset]), lenValue-1) + if status { + leng = lenValue + } + + return leng, charString +} + +func decodeContextCharacterString(buffer []byte, offset, maxLength int, tagNumber byte) (int, string) { + leng := 0 + status := false + charString := "" + + if IsContextTag(buffer, offset+leng, tagNumber) { + leng1, _, lenValue := DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + + status, charString = multiCharsetCharacterStringDecode(buffer, offset+1+leng, maxLength, BacnetCharacterStringEncodings(buffer[offset+leng]), int(lenValue)-1) + if status { + leng += int(lenValue) + } else { + leng = -1 + } + } + + return leng, charString +} diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go index e7a64b2..5b85e75 100644 --- a/pkg/encoding/encoding.go +++ b/pkg/encoding/encoding.go @@ -76,3 +76,33 @@ func EncodeContextUnsigned(tagNum BACnetApplicationTag, val uint32) []byte { } return append(EncodeTag(tagNum, true, len), EncodeUnsigned(val)...) } + +// EncodeApplicationUnsigned encodes an unsigned integer value as a BACnet application tag. +func EncodeApplicationUnsigned(value uint32) []byte { + tmp := EncodeUnsigned(value) + tag := EncodeTag(UnsignedInt, false, len(tmp)) + return append(tag, tmp...) +} + +func EncodeApplicationEnumerated(value uint32) []byte { + tmp := EncodeUnsigned(value) + return append(EncodeTag(Enumerated, false, len(tmp)), tmp...) +} + +func EncodeApplicationOctetString(octetString []byte, octetOffset, octetCount int) []byte { + tag := EncodeTag(OctetString, false, octetCount) + octetStringSegment := octetString[octetOffset : octetOffset+octetCount] + return append(tag, octetStringSegment...) +} + +func EncodeApplicationCharacterString(value string) []byte { + tmp := encodeBACnetCharacterString(value) + tag := EncodeTag(CharacterString, false, len(tmp)) + return append(tag, tmp...) +} + +func encodeBACnetCharacterString(value string) []byte { + encoding := []byte{byte(CharacterUTF8)} + encodedValue := []byte(value) + return append(encoding, encodedValue...) +} From 6b2822edede83fe55751d213d6401fd53eb79736 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 12 Sep 2023 18:17:24 +0300 Subject: [PATCH 15/25] add bacnet Value Signed-off-by: SammyOina --- example/whoIs/whois.go | 23 + pkg/bacnet/bvlc.go | 11 +- pkg/bacnet/iam.go | 7 +- pkg/bacnet/property.go | 1168 +++++++++++++++++++++++++++++++++++++- pkg/encoding/decoding.go | 51 ++ pkg/encoding/encoding.go | 33 ++ pkg/encoding/tags.go | 22 +- 7 files changed, 1293 insertions(+), 22 deletions(-) diff --git a/example/whoIs/whois.go b/example/whoIs/whois.go index 4310f6f..cd52b4d 100644 --- a/example/whoIs/whois.go +++ b/example/whoIs/whois.go @@ -95,6 +95,29 @@ func main() { // Process the response (you'll need to parse BACnet responses here) response := buffer[:n] log.Printf("Received response: %X\n", response) + blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} + headerLength, function, msgLength, err := blvc.Decode(response, 0) + if err != nil { + log.Fatal(err) + } fmt.Println(response) + fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) + fmt.Println("blvc", blvc) + fmt.Println(response[headerLength:]) + npdu := bacnet.NPDU{Version: 1} + npduLen := npdu.Decode(response, headerLength) + fmt.Println("npdu", npdu) + fmt.Println(response[headerLength+npduLen:]) + apdu := bacnet.APDU{} + apduLen := apdu.Decode(response, headerLength+npduLen) + fmt.Println("apdu", apdu) + fmt.Println(response[headerLength+npduLen+apduLen:]) + iam := bacnet.IAmRequest{} + iamLen, err := iam.Decode(response, headerLength+npduLen+apduLen) + if err != nil { + log.Fatal(err) + } + fmt.Println("iam", iam) + fmt.Println(response[headerLength+npduLen+apduLen+iamLen:]) } } diff --git a/pkg/bacnet/bvlc.go b/pkg/bacnet/bvlc.go index 4859b21..5591d9f 100644 --- a/pkg/bacnet/bvlc.go +++ b/pkg/bacnet/bvlc.go @@ -3,6 +3,7 @@ package bacnet import ( "encoding/binary" "errors" + "fmt" "github.com/absmach/bacnet/pkg/transport" ) @@ -13,6 +14,7 @@ var ( errUnsupportedFunction = errors.New("unsupported BVLC function") ) +//go:generate stringer -type=BVLCFunctions type BVLCFunctions int const ( @@ -60,22 +62,23 @@ func NewBVLC(transprt transport.Transport) *BVLC { return bvlc } -func (bvlc *BVLC) Decode(buffer []byte, offset int) (int, byte, uint16, error) { +func (bvlc *BVLC) Decode(buffer []byte, offset int) (int, BVLCFunctions, uint16, error) { msgType := buffer[0] function := BVLCFunctions(buffer[1]) msgLength := binary.BigEndian.Uint16(buffer[2:4]) if msgType != bvlc.BVLLTypeBACnetIP || msgLength != uint16(len(buffer)) { + fmt.Println(msgType, bvlc.BVLLTypeBACnetIP, msgLength, uint16(len(buffer))) return 0, 0, 0, errUnsupportedTransport } switch function { case BVLCResult: - return 4, byte(function), msgLength, nil + return 4, function, msgLength, nil case BVLCOriginalUnicastNPDU: - return 4, byte(function), msgLength, nil + return 4, function, msgLength, nil case BVLCOriginalBroadcastNPDU: - return 4, byte(function), msgLength, nil + return 4, function, msgLength, nil case BVLCForwardedNPDU: // Handle this case case BVLCDistributeBroadcastToNetwok: diff --git a/pkg/bacnet/iam.go b/pkg/bacnet/iam.go index e53723c..551c63b 100644 --- a/pkg/bacnet/iam.go +++ b/pkg/bacnet/iam.go @@ -2,6 +2,7 @@ package bacnet import ( "errors" + "fmt" "github.com/absmach/bacnet/pkg/encoding" ) @@ -13,7 +14,7 @@ type IAmRequest struct { VendorID uint32 } -func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, error) { +func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { leng := 0 iam.IamDeviceIdentifier = ObjectIdentifier{} // OBJECT ID - object id @@ -21,6 +22,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, erro leng += leng1 if tagNumber != byte(BACnetObjectIdentifier) { + fmt.Println("tag num object id", tagNumber) return -1, errors.New("Invalid tag number") } @@ -34,6 +36,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, erro leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 if tagNumber != byte(UnsignedInt) { + fmt.Println("tag num max apdu", tagNumber) return -1, errors.New("Invalid tag number") } @@ -45,6 +48,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, erro leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 if tagNumber != byte(Enumerated) { + fmt.Println("tag num segmentation", tagNumber) return -1, errors.New("Invalid tag number") } propID := encoding.SegmentationSupported @@ -57,6 +61,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int, apduLen int) (int, erro leng += leng1 if tagNumber != byte(UnsignedInt) { + fmt.Println("tag num vendor ID", tagNumber) return -1, errors.New("Invalid tag number") } diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index 0a10a46..f91fe9e 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -1,16 +1,15 @@ package bacnet import ( + "errors" + "fmt" + "log" + "time" + + "github.com/absmach/bacnet/internal" "github.com/absmach/bacnet/pkg/encoding" ) -type PropertyValue struct { - Identifier encoding.PropertyIdentifier - Arrayindex *uint32 - Value uint32 - Priority uint32 -} - type ReadPropertyRequest struct { ObjectIdentifier *ObjectIdentifier PropertyIdentifier interface{} @@ -70,3 +69,1158 @@ func (r *ReadPropertyRequest) Decode(buffer []byte, offset, apduLen int) int { return leng } + +type ReadPropertyACK struct { + ObjectIdentifier ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex uint32 + PropertyValue []BACnetValue +} + +func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + // 0 object_identifier + if encoding.IsContextTag(buffer, offset+leng, 0) { + r.ObjectIdentifier = ObjectIdentifier{} + leng1 := r.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + leng += leng1 + } else { + return -1, errors.New("ASN.1 decoding error for object_identifier") + } + + // 2 propertyidentifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + PropertyID := encoding.PropertyList + leng1, propID := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &PropertyID) + r.PropertyIdentifier = propID + leng += leng1 + } else { + return -1, errors.New("ASN.1 decoding error for property_identifier") + } + + // 2 property_array_index + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, r.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + + // tag 3 property-value + if encoding.IsOpeningTagNumber(buffer, offset+leng, 3) { + leng++ + r.PropertyValue = make([]BACnetValue, 0) + for !encoding.IsClosingTagNumber(buffer, offset+leng, 3) && leng < apduLen { + bValue := BACnetValue{} + propId := r.PropertyIdentifier.(encoding.PropertyIdentifier) + leng1 := bValue.Decode(buffer, offset+leng, apduLen-leng, r.ObjectIdentifier.Type, propId) + leng += leng1 + r.PropertyValue = append(r.PropertyValue, bValue) + } + if encoding.IsClosingTagNumber(buffer, offset+leng, 3) { + leng++ + } else { + return -1, errors.New("ASN.1 decoding error for property_value") + } + } else { + return -1, errors.New("ASN.1 decoding error for property_value") + } + + return leng, nil +} + +type BACnetValue struct { + Tag *ApplicationTags + Value interface{} +} + +func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encoding.ObjectType, propID encoding.PropertyIdentifier) int { + length := 0 + var err error + + if !encoding.IsContextSpecific(buffer[offset]) { + tagLen, tagNumber, lenValueType := encoding.DecodeTagNumberAndValue(buffer, offset) + if tagLen > 0 { + ttag := ApplicationTags(tagNumber) + bv.Tag = &ttag + length += tagLen + + decodeLen := 0 + + switch *bv.Tag { + case Null: + bv.Value = nil + decodeLen = 0 + case Boolean: + if lenValueType > 0 { + bv.Value = true + } else { + bv.Value = false + } + case UnsignedInt: + if propID == encoding.RoutingTable { + bv.Tag = nil + bv.Value = &RouterEntry{} + length-- + decodeLen, err = bv.Value.(*RouterEntry).Decode(buffer, offset+length, apduLen) + if err != nil { + return -1 + } + } else if propID == encoding.ActiveVtSessions { + bv.Tag = nil + bv.Value = &BACnetVTSession{} + length-- + decodeLen = bv.Value.(*BACnetVTSession).Decode(buffer, offset+length, apduLen) + } else if propID == encoding.ThreatLevel || propID == encoding.ThreatAuthority { + bv.Tag = nil + bv.Value = &BACnetAccessThreatLevel{} + length-- + decodeLen = bv.Value.(*BACnetAccessThreatLevel).Decode(buffer, offset+length, apduLen) + } else { + var uintVal uint32 + decodeLen, uintVal = encoding.DecodeUnsigned(buffer, offset+length, int(lenValueType)) + bv.Value = uintVal + } + case SignedInt: + var intValue int + decodeLen, intValue = encoding.DecodeSigned(buffer, offset+length, int(lenValueType)) + bv.Value = intValue + case Real: + var floatValue float32 + decodeLen, floatValue = encoding.DecodeRealSafe(buffer, offset+length, int(lenValueType)) + bv.Value = floatValue + case Double: + var doubleValue float64 + decodeLen, doubleValue = encoding.DecodeDoubleSafe(buffer, offset+length, int(lenValueType)) + bv.Value = doubleValue + case OctetString: + var octetValue []byte + decodeLen, octetValue = encoding.DecodeOctetString(buffer, offset+length, int(lenValueType)) + bv.Value = octetValue + case CharacterString: + var stringValue string + decodeLen, stringValue = encoding.DecodeCharacterString(buffer, offset+length, apduLen, int(lenValueType)) + bv.Value = stringValue + case BitString: + switch propID { + case encoding.RecipientList: + bv.Tag = nil + bv.Value = &BACnetDestination{} + length-- + decodeLen = bv.Value.(*BACnetDestination).Decode(buffer, offset+length, apduLen) + case encoding.StatusFlags: + bv.Tag = nil + bitValue := &BACnetStatusFlags{} + decodeLen = bitValue.Decode(buffer, offset, lenValueType) + bv.Value = bitValue + case encoding.EventEnable, encoding.AckedTransitions: + bv.Tag = nil + bitValue := &BACnetEventTransitionBits{} + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) + bv.Value = bitValue + case encoding.LimitEnable: + bv.Tag = nil + bitValue := &BACnetLimitEnable{} + decodeLen = bitValue.Decode(buffer, offset, lenValueType) + bv.Value = bitValue + case encoding.ProtocolObjectTypesSupported: + bv.Tag = nil + bitValue := &BACnetObjectTypesSupported{} + decodeLen = bitValue.Decode(buffer, offset, lenValueType) + bv.Value = bitValue + case encoding.ProtocolServicesSupported: + bv.Tag = nil + bitValue := &BACnetServicesSupported{} + decodeLen = bitValue.Decode(buffer, offset, lenValueType) + bv.Value = bitValue + default: + bitValue := &BACnetBitString{} + decodeLen = bitValue.Decode(buffer, offset, lenValueType) + bv.Value = bitValue + } + case Enumerated: + decodeLen, uintValue := encoding.DecodeEnumerated(buffer, offset+length, lenValueType, objType, propID) + bv.Value = uintValue + case Date: + switch propID { + case encoding.EffectivePeriod: + bv.Tag = nil + bv.Value = NewBACnetDateRange() + length-- + decodeLen = bv.Value.(*BACnetDateRange).Decode(buffer, offset+length, apduLen) + case encoding.MinimumValueTimestamp, + encoding.MaximumValueTimestamp, + encoding.ChangeOfStateTime, + encoding.TimeOfStateCountReset, + encoding.TimeOfActiveTimeReset, + encoding.ModificationDate, + encoding.UpdateTime, + encoding.CountChangeTime, + encoding.StartTime, + encoding.StopTime, + encoding.LastCredentialAddedTime, + encoding.LastCredentialRemovedTime, + encoding.ActivationTime, + encoding.ExpiryTime, + encoding.LastUseTime, + encoding.TimeOfStrikeCountReset, + encoding.ValueChangeTime: + bv.Tag = nil + bv.Value = &DateTime{} + length-- + decodeLen = bv.Value.(*DateTime).Decode(buffer, offset+length) + default: + decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+length, lenValueType) + bv.Value = dateValue + } + if (objType == encoding.DateTimeValue || objType == encoding.TimePatternValue) && (propID == encoding.PresentValue || propID == encoding.RelinquishDefault) { + decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+length, lenValueType) + bv.Value = dateValue + } + case Time: + decodeLen, timeValue := encoding.DecodeBACnetTimeSafe(buffer, offset+length, lenValueType) + bv.Value = timeValue + case BACnetObjectIdentifier: + if propID == encoding.LastKeyServer || + propID == encoding.ManualSlaveAddressBinding || + propID == encoding.SlaveAddressBinding || + propID == encoding.DeviceAddressBinding { + bv.Tag = nil + bv.Value = NewBACnetAddressBinding() + length-- + decodeLen = bv.Value.(*BACnetAddressBinding).Decode(buffer, offset+length, apduLen) + } else { + decodeLen, objectType, instance := encoding.DecodeObjectIDSafe(buffer, offset+length, lenValueType) + bv.Value = ObjectIdentifier{Type: objectType, Instance: instance} + } + default: + log.Println("Unhandled tag:", bv.Tag) + length = apduLen + } + + if decodeLen < 0 { + return -1 + } + length += decodeLen + } + } else { + switch propID { + case encoding.BacnetIpGlobalAddress, encoding.FdBbmdAddress: + bv.Value = NewBACnetHostNPort() + length += bv.Value.(*BACnetHostNPort).Decode(buffer, offset+length, apduLen-length) + case encoding.UtcTimeSynchronizationRecipients, + encoding.RestartNotificationRecipients, + encoding.TimeSynchronizationRecipients, + encoding.CovuRecipients: + bv.Value = NewBACnetRecipient() + length += bv.Value.(*BACnetRecipient).Decode(buffer, offset+length, apduLen-length) + case encoding.KeySets: + bv.Value = NewBACnetSecurityKeySet() + length += bv.Value.(*BACnetSecurityKeySet).Decode(buffer, offset+length, apduLen-length) + case encoding.EventTimeStamps, + encoding.LastCommandTime, + encoding.CommandTimeArray, + encoding.LastRestoreTime, + encoding.TimeOfDeviceRestart, + encoding.AccessEventTime, + encoding.UpdateTime: + bv.Value = NewBACnetTimeStamp() + length += bv.Value.(*BACnetTimeStamp).Decode(buffer, offset+length, apduLen-length) + case encoding.ListOfGroupMembers: + bv.Value = NewReadAccessSpecification() + length += bv.Value.(*ReadAccessSpecification).Decode(buffer, offset+length, apduLen-length) + case encoding.ListOfObjectPropertyReferences: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case encoding.MemberOf, + encoding.ZoneMembers, + encoding.DoorMembers, + encoding.SubordinateList, + encoding.Represents, + encoding.AccessEventCredential, + encoding.AccessDoors, + encoding.ZoneTo, + encoding.ZoneFrom, + encoding.CredentialsInZone, + encoding.LastCredentialAdded, + encoding.LastCredentialRemoved, + encoding.EntryPoints, + encoding.ExitPoints, + encoding.Members, + encoding.Credentials, + encoding.Accompaniment, + encoding.BelongsTo, + encoding.LastAccessPoint, + encoding.EnergyMeterRef: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case encoding.EventAlgorithmInhibitRef, + encoding.InputReference, + encoding.ManipulatedVariableReference, + encoding.ControlledVariableReference: + bv.Value = NewBACnetObjectPropertyReference() + length += bv.Value.(*BACnetObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case encoding.LoggingRecord: + bv.Value = NewBACnetAccumulatorRecord() + length += bv.Value.(*BACnetAccumulatorRecord).Decode(buffer, offset+length, apduLen-length) + case encoding.Action: + bv.Value = NewBACnetActionList() + length += bv.Value.(*BACnetActionList).Decode(buffer, offset+length, apduLen-length) + case encoding.Scale: + bv.Value = NewBACnetScale() + length += bv.Value.(*BACnetScale).Decode(buffer, offset+length, apduLen-length) + case encoding.LightingCommand: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case encoding.Prescale: + bv.Value = NewBACnetPrescale() + length += bv.Value.(*BACnetPrescale).Decode(buffer, offset+length, apduLen-length) + case encoding.RequestedShedLevel, + encoding.ExpectedShedLevel, + encoding.ActualShedLevel: + bv.Value = NewBACnetShedLevel() + length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) + case encoding.LogBuffer: + if objType == encoding.TrendLog { + bv.Value = NewBACnetLogRecord() + length += bv.Value.(*BACnetLogRecord).Decode(buffer, offset+length, apduLen-length) + } else { + log.Println("Unhandled context-specific tag:", bv.Tag) + length = apduLen + } + case encoding.DateList: + bv.Value = NewBACnetCalendarEntry() + length += bv.Value.(*BACnetCalendarEntry).Decode(buffer, offset+length, apduLen-length) + case encoding.LogBuffer: + if objType == encoding.EventLog { + bv.Value = NewBACnetEventLogRecord() + length += bv.Value.(*BACnetEventLogRecord).Decode(buffer, offset+length, apduLen-length) + } else { + log.Println("Unhandled context-specific tag:", bv.Tag) + length = apduLen + } + case encoding.PresentValue: + if objType == encoding.Group { + bv.Value = NewReadAccessResult() + length += bv.Value.(*ReadAccessResult).Decode(buffer, offset+length, apduLen-length) + } else { + log.Println("Unhandled context-specific tag:", bv.Tag) + length = apduLen + } + case encoding.NegativeAccessRules, + encoding.PositiveAccessRules: + bv.Value = NewBACnetAccessRule() + length += bv.Value.(*BACnetAccessRule).Decode(buffer, offset+length, apduLen-length) + case encoding.Tags: + bv.Value = NewBACnetNameValue() + length += bv.Value.(*BACnetNameValue).Decode(buffer, offset+length, apduLen-length) + case encoding.SubordinateTags: + bv.Value = NewBACnetNameValueCollection() + length += bv.Value.(*BACnetNameValueCollection).Decode(buffer, offset+length, apduLen-length) + case encoding.NetworkAccessSecurityPolicies: + bv.Value = NewBACnetNetworkSecurityPolicy() + length += bv.Value.(*BACnetNetworkSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case encoding.PortFilter: + bv.Value = NewBACnetPortPermission() + length += bv.Value.(*BACnetPortPermission).Decode(buffer, offset+length, apduLen-length) + case encoding.PriorityArray: + bv.Value = NewBACnetPriorityArray() + length += bv.Value.(*BACnetPriorityArray).Decode(buffer, offset+length, apduLen-length) + case encoding.ProcessIdentifierFilter: + bv.Value = NewBACnetProcessIdSelection() + length += bv.Value.(*BACnetProcessIdSelection).Decode(buffer, offset+length, apduLen-length) + case encoding.GlobalGroup && propID == encoding.PresentValue: + bv.Value = NewBACnetPropertyAccessResult() + length += bv.Value.(*BACnetPropertyAccessResult).Decode(buffer, offset+length, apduLen-length) + case encoding.SetpointReference: + bv.Value = NewBACnetSetpointReference() + length += bv.Value.(*BACnetSetpointReference).Decode(buffer, offset+length, apduLen-length) + case encoding.ExceptionSchedule: + bv.Value = NewBACnetSpecialEvent() + length += bv.Value.(*BACnetSpecialEvent).Decode(buffer, offset+length, apduLen-length) + case encoding.StateChangeValues: + bv.Value = NewBACnetTimerStateChangeValue() + length += bv.Value.(*BACnetTimerStateChangeValue).Decode(buffer, offset+length, apduLen-length) + case encoding.ValueSource, encoding.ValueSourceArray: + bv.Value = NewBACnetValueSource() + length += bv.Value.(*BACnetValueSource).Decode(buffer, offset+length, apduLen-length) + case encoding.VirtualMacAddressTable: + bv.Value = NewBACnetVMACEntry() + length += bv.Value.(*BACnetVMACEntry).Decode(buffer, offset+length, apduLen-length) + case encoding.AssignedAccessRights: + bv.Value = NewBACnetAssignedAccessRights() + length += bv.Value.(*BACnetAssignedAccessRights).Decode(buffer, offset+length, apduLen-length) + case encoding.AssignedLandingCalls: + bv.Value = NewBACnetAssignedLandingCalls() + length += bv.Value.(*BACnetAssignedLandingCalls).Decode(buffer, offset+length, apduLen-length) + case encoding.AccessEventAuthenticationFactor, + (objType == encoding.CredentialDataInput && propID == encoding.PresentValue): + bv.Value = NewBACnetAuthenticationFactor() + length += bv.Value.(*BACnetAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) + case encoding.SupportedFormats: + bv.Value = NewBACnetAuthenticationFactorFormat() + length += bv.Value.(*BACnetAuthenticationFactorFormat).Decode(buffer, offset+length, apduLen-length) + case encoding.AuthenticationPolicyList: + bv.Value = NewBACnetAuthenticationPolicy() + length += bv.Value.(*BACnetAuthenticationPolicy).Decode(buffer, offset+length, apduLen-length) + case encoding.Channel && propID == encoding.PresentValue: + bv.Value = NewBACnetChannelValue() + length += bv.Value.(*BACnetChannelValue).Decode(buffer, offset+length, apduLen-length) + case encoding.ActiveCovSubscriptions: + bv.Value = NewBACnetCOVSubscription() + length += bv.Value.(*BACnetCOVSubscription).Decode(buffer, offset+length, apduLen-length) + case encoding.AuthenticationFactors: + bv.Value = NewBACnetCredentialAuthenticationFactor() + length += bv.Value.(*BACnetCredentialAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) + case encoding.WeeklySchedule: + bv.Value = NewBACnetDailySchedule() + length += bv.Value.(*BACnetDailySchedule).Decode(buffer, offset+length, apduLen-length) + case encoding.SubscribedRecipients: + bv.Value = NewBACnetEventNotificationSubscription() + length += bv.Value.(*BACnetEventNotificationSubscription).Decode(buffer, offset+length, apduLen-length) + case encoding.EventParameters: + bv.Value = NewBACnetEventParameter() + length += bv.Value.(*BACnetEventParameter).Decode(buffer, offset+length, apduLen-length) + case encoding.FaultParameters: + bv.Value = NewBACnetFaultParameter() + length += bv.Value.(*BACnetFaultParameter).Decode(buffer, offset+length, apduLen-length) + case encoding.LoggingObject: + bv.Value = NewBACnetLoggingObject() + length += bv.Value.(*BACnetLoggingObject).Decode(buffer, offset+length, apduLen-length) + case encoding.LoggingRecord: + bv.Value = NewBACnetLoggingRecord() + length += bv.Value.(*BACnetLoggingRecord).Decode(buffer, offset+length, apduLen-length) + case encoding.ReinitiateDevice: + bv.Value = NewBACnetReinitializeDevice() + length += bv.Value.(*BACnetReinitializeDevice).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierBACNETIPUDPDATAGRAMCONFIGURATION: + bv.Value = NewBACnetBACnetIPUDPDatagramConfiguration() + length += bv.Value.(*BACnetBACnetIPUDPDatagramConfiguration).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierREINITIALIZEPARAMETERS: + bv.Value = NewBACnetReinitializeParameters() + length += bv.Value.(*BACnetReinitializeParameters).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierDISCONNECTEDONEXCEPTION: + bv.Value = NewBACnetDisconnect() + length += bv.Value.(*BACnetDisconnect).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierNOMINALENCYCLE: + bv.Value = NewBACnetNominalBACnetInterval() + length += bv.Value.(*BACnetNominalBACnetInterval).Decode(buffer, offset+length, apduLen-length) + case encoding.NotifyType: + bv.Value = NewBACnetNotifyType() + length += bv.Value.(*BACnetNotifyType).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierSUMMATION: + bv.Value = NewBACnetEventSummation() + length += bv.Value.(*BACnetEventSummation).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSUSERPASSWORD: + bv.Value = NewBACnetAccessUserPassword() + length += bv.Value.(*BACnetAccessUserPassword).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPULSECONFIGURATION: + bv.Value = NewBACnetPulseConverter() + length += bv.Value.(*BACnetPulseConverter).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPORTPARAMETERS: + bv.Value = NewBACnetPortParameters() + length += bv.Value.(*BACnetPortParameters).Decode(buffer, offset+length, apduLen-length) + case encoding.TimeDelay, + BACnetPropertyIdentifierLIGHTCONTROLDELAY, + BACnetPropertyIdentifierINITIALTIMEDELAY: + bv.Value = NewBACnetTimeValue() + length += bv.Value.(*BACnetTimeValue).Decode(buffer, offset+length, apduLen-length) + case encoding.LightingCommand, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierRADIOFAILUREEVENTS, + BACnetPropertyIdentifierRADIOFAILUREEVENTS2, + BACnetPropertyIdentifierMACADDRESSCHANGEEVENTS: + bv.Value = NewBACnetCOVSubscription() + length += bv.Value.(*BACnetCOVSubscription).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierSHEDLEVELDESCENDANTS: + bv.Value = NewBACnetShedLevelDescendants() + length += bv.Value.(*BACnetShedLevelDescendants).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPERSISTENTDATA: + bv.Value = NewBACnetScheduleObjectPeriod() + length += bv.Value.(*BACnetScheduleObjectPeriod).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierRADIOSTATUS, + BACnetPropertyIdentifierRADIOSTATUS2: + bv.Value = NewBACnetRadioStatus() + length += bv.Value.(*BACnetRadioStatus).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierUSERINFORMATION, + BACnetPropertyIdentifierMACADDRESS: + bv.Value = NewBACnetMACAddress() + length += bv.Value.(*BACnetMACAddress).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierCOVMULTIPLESUBSCRIPTIONSFILTER, + BACnetPropertyIdentifierMONITORMULTIPLEBINARYINPUTS, + BACnetPropertyIdentifierMONITORMULTIPLEBINARYOUTPUTS, + BACnetPropertyIdentifierMULTIPLEMONITORING, + BACnetPropertyIdentifierSTOPWHENFULL, + BACnetPropertyIdentifierLIMITENABLE, + BACnetPropertyIdentifierDATAGROUPS, + BACnetPropertyIdentifierDEVICEADDRESSBINDING: + bv.Value = NewBACnetMultistateValue() + length += bv.Value.(*BACnetMultistateValue).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierLIGHTINGDEVICESTATUSALOGGING, + BACnetPropertyIdentifierLIGHTINGDEVICESTATUSLOGGING, + BACnetPropertyIdentifierDEVICEADDRESSBINDING, + BACnetPropertyIdentifierLIGHTINGDEVICEOPERATIONALMODE, + BACnetPropertyIdentifierLIGHTINGDEVICEOPERATIONALSTATUS, + BACnetPropertyIdentifierLIGHTINGGROUP, + BACnetPropertyIdentifierLIGHTINGGRP, + BACnetPropertyIdentifierLIGHTINGLOGGING: + bv.Value = NewBACnetLightingGroup() + length += bv.Value.(*BACnetLightingGroup).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierEVENTTIMESTAMPS: + bv.Value = NewBACnetTimeStampedValue() + length += bv.Value.(*BACnetTimeStampedValue).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGGROUP: + bv.Value = NewBACnetLightingGroup() + length += bv.Value.(*BACnetLightingGroup).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierNEGATIVEACCESSRULES, + BACnetPropertyIdentifierPOSITIVEACCESSRULES: + bv.Value = NewBACnetAccessRule() + length += bv.Value.(*BACnetAccessRule).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierNETWORKADDRESS, + BACnetPropertyIdentifierMASTERKEY, + encoding.AccessUser, + encoding.AccessZone, + BACnetPropertyIdentifierAREADELIVERING, + BACnetPropertyIdentifierAREARELAY, + BACnetPropertyIdentifierCONTROLLING, + BACnetPropertyIdentifierDELIVERING, + BACnetPropertyIdentifierENROLLING, + BACnetPropertyIdentifierLOWLIMIT, + BACnetPropertyIdentifierMAXIMUMOUTPUT, + BACnetPropertyIdentifierMAXIMUMVALUE, + BACnetPropertyIdentifierMINIMUMOUTPUT, + BACnetPropertyIdentifierMINIMUMVALUE, + BACnetPropertyIdentifierEVENTTIME, + BACnetPropertyIdentifierTIMEDELAY, + BACnetPropertyIdentifierDURATION, + BACnetPropertyIdentifierEXCEPTIONALLIMITS, + BACnetPropertyIdentifierINACTIVESTATE, + BACnetPropertyIdentifierINSTANTANEOUS, + BACnetPropertyIdentifierINVALIDATED, + BACnetPropertyIdentifierLIMITENABLE, + BACnetPropertyIdentifierRELIABILITY, + BACnetPropertyIdentifierSCALE, + BACnetPropertyIdentifierSTEPINCREMENT, + BACnetPropertyIdentifierTIMER, + BACnetPropertyIdentifierLIMIT, + BACnetPropertyIdentifierMAXIMUM, + BACnetPropertyIdentifierMINIMUM, + BACnetPropertyIdentifierPULSESCALING, + BACnetPropertyIdentifierSHUTDOWN, + BACnetPropertyIdentifierBUFFERPROPERTY, + BACnetPropertyIdentifierSTREAMINGTHRESHOLDS: + bv.Value = NewBACnetOctetString() + length += bv.Value.(*BACnetOctetString).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierCOVMULTIPLESUBSCRIPTIONSFILTER, + encoding.MultiStateValue: + bv.Value = NewBACnetMultistateValue() + length += bv.Value.(*BACnetMultistateValue).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierAUTHENTICATIONFACTORSUPPLIED, + BACnetPropertyIdentifierAUTHENTICATIONFACTORFORMAT, + BACnetPropertyIdentifierAUTHENTICATIONPOLICYNAME, + BACnetPropertyIdentifierAUTHENTICATIONPOLICYNAMES, + BACnetPropertyIdentifierPOLICYNAMES: + bv.Value = NewBACnetAuthorization() + length += bv.Value.(*BACnetAuthorization).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierDEVICEIDENTIFICATION: + bv.Value = NewBACnetCharacterString() + length += bv.Value.(*BACnetCharacterString).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierEVENTTIMESTAMPS: + bv.Value = NewBACnetTimeStampedValue() + length += bv.Value.(*BACnetTimeStampedValue).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierDEVICEIDENTIFICATION: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierSHEDLEVELS: + bv.Value = NewBACnetShedLevel() + length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetPortFilter() + length += bv.Value.(*BACnetPortFilter).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPOLICY: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSRULES, + BACnetPropertyIdentifierINVALIDACTIONS, + BACnetPropertyIdentifierLOGGINGOBJECT, + BACnetPropertyIdentifierSAVERESTORESTATE: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACTIVESEQUENCE, + BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPORTFILTER, + BACnetPropertyIdentifierAUTHENTICATIONPOLICIES, + BACnetPropertyIdentifierAUTHENTICATIONPOLICYLIST: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPOLICYPASSWORD, + BACnetPropertyIdentifierPOLICYTYPE, + BACnetPropertyIdentifierTIMEDELAY, + BACnetPropertyIdentifierVERIFYPASSWORD, + BACnetPropertyIdentifierPORTPARAMETERS, + BACnetPropertyIdentifierDEVICEADDRESSBINDING: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierRELINQUISHDEFAULT, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSRULES, + BACnetPropertyIdentifierINVALIDACTIONS, + BACnetPropertyIdentifierLOGGINGOBJECT, + BACnetPropertyIdentifierSAVERESTORESTATE: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSSEQUENCE, + BACnetPropertyIdentifierACTIVESEQUENCE, + BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, + BACnetPropertyIdentifierFEEDINGPIPEINDEX, + BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE, + BACnetPropertyIdentifierSTATUSFLAGS: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICY, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPOLICYNAMES, + BACnetPropertyIdentifierACCESSRULES, + BACnetPropertyIdentifierLIMITDISABLE, + BACnetPropertyIdentifierMANUALSLIDERSETTINGS: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSRULES, + BACnetPropertyIdentifierINVALIDACTIONS, + BACnetPropertyIdentifierLOGGINGOBJECT, + BACnetPropertyIdentifierSAVERESTORESTATE: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPOLICYPASSWORD, + BACnetPropertyIdentifierPOLICYTYPE, + BACnetPropertyIdentifierTIMEDELAY, + BACnetPropertyIdentifierVERIFYPASSWORD: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, + BACnetPropertyIdentifierEXCEPTIONSCHEDULE, + BACnetPropertyIdentifierEVENTTIMESTAMPS: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierALARMVALUES: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierALARMVALUES, + BACnetPropertyIdentifierEVENTTYPE, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierSAFETYPOINTS: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCONTROLMODE: + bv.Value = NewBACnetLightingControlMode() + length += bv.Value.(*BACnetLightingControlMode).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierBACNETIPADDRESS, + BACnetPropertyIdentifierDEFAULTGATEWAY, + BACnetPropertyIdentifierIPADDRESS, + BACnetPropertyIdentifierSUBNETMASK: + bv.Value = NewBACnetIPAddress() + length += bv.Value.(*BACnetIPAddress).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetPortFilter() + length += bv.Value.(*BACnetPortFilter).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSRULES: + bv.Value = NewBACnetDeviceObjectReference() + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierRELIABILITY, + BACnetPropertyIdentifierUNITS: + bv.Value = NewBACnetReliability() + length += bv.Value.(*BACnetReliability).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPARAMETERSETUSAGE: + bv.Value = NewBACnetParameterSetUsage() + length += bv.Value.(*BACnetParameterSetUsage).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, + BACnetPropertyIdentifierEXCEPTIONSCHEDULE, + BACnetPropertyIdentifierEXCEPTIONSCHEDULEDEFAULT, + BACnetPropertyIdentifierLIGHTINGCOMMAND: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGINGDEFAULT, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierEVENTCODE, + BACnetPropertyIdentifierLONMARKRESOURCE, + BACnetPropertyIdentifierPOLICIES: + bv.Value = NewBACnetUnsignedInteger() + length += bv.Value.(*BACnetUnsignedInteger).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCOMMAND: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, + BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: + bv.Value = NewBACnetLightingCommand() + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierSAFETYPOINTS: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPOLICYPASSWORD, + BACnetPropertyIdentifierPOLICYTYPE, + BACnetPropertyIdentifierTIMEDELAY, + BACnetPropertyIdentifierVERIFYPASSWORD: + bv.Value = NewBACnetDeviceObjectPropertyReference() + length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICY, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierDEVICEIDENTIFICATION: + bv.Value = NewBACnetCharacterString() + length += bv.Value.(*BACnetCharacterString).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICY, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case encoding.AccessEvent, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case BACnetPropertyIdentifierACCESSEVENTS, + BACnetPropertyIdentifierPOLICIES, + BACnetPropertyIdentifierPORTFILTER: + bv.Value = NewBACnetDeviceSecurityPolicy() + length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + default: + bv.Value = nil + } + } + return length, nil +} + +func (bv *BACnetValue) Encode() []byte { + if bv.Tag == nil { + // Handle NULL case + } else { + switch *bv.Tag { + case Boolean: + return encoding.EncodeApplicationBoolean(bv.Value.(bool)) + case UnsignedInt: + return encoding.EncodeApplicationUnsigned(bv.Value.(uint32)) + case SignedInt: + return encoding.EncodeApplicationSigned(bv.Value.(int32)) + case Real: + return encoding.EncodeApplicationReal(bv.Value.(float32)) + case Double: + return encoding.EncodeApplicationDouble(bv.Value.(float64)) + case OctetString: + return encoding.EncodeApplicationOctetString(bv.Value.([]byte), 0, len(bv.Value.([]byte))) + case CharacterString: + return encoding.EncodeApplicationCharacterString(bv.Value.(string)) + case BitString: + return encoding.EncodeApplicationBitString(b) + // Handle BIT_STRING case + // Add your code here... + case Enumerated: + // Handle ENUMERATED case + // Add your code here... + case Date: + // Handle DATE case + // Add your code here... + case Time: + // Handle TIME case + // Add your code here... + case BACnetObjectIdentifier: + // Handle BACNETOBJECTIDENTIFIER case + // Add your code here... + default: + log.Printf("Unsupported BACnetApplicationTag: %v", bv.Tag) + // Handle other BACnetApplicationTags as needed... + } + } + + return nil +} + +// BACnetRouterEntryStatus is an enumeration for the status of BACnetRouterEntry. +type BACnetRouterEntryStatus int + +const ( + Available BACnetRouterEntryStatus = iota + BACnetRouterEntryStatusBusy + Disconnected +) + +// BACnetRouterEntry represents a BACnet router entry. +type RouterEntry struct { + NetworkNumber uint32 + MACAddress []byte + Status BACnetRouterEntryStatus + PerformanceIndex uint32 +} + +// Decode decodes a RouterEntry from an encoded byte buffer. +func (entry *RouterEntry) Decode(buffer []byte, offset, apduLen int) (int, error) { + var length int + + // network_number + length1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset) + if tagNumber != byte(encoding.UnsignedInt) { + return -1, errors.New("Error decoding network_number") + } + length += length1 + length1, entry.NetworkNumber = encoding.DecodeUnsigned(buffer, offset+length, int(lenValue)) + length += length1 + + // mac_address + length1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+length) + if tagNumber != byte(encoding.OctetString) { + return -1, errors.New("Error decoding mac_address") + } + length += length1 + length1, entry.MACAddress = encoding.DecodeOctetString(buffer, offset+length, int(lenValue)) + length += length1 + + // status + length1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+length) + if tagNumber != byte(encoding.Enumerated) { + return -1, errors.New("Error decoding status") + } + length += length1 + length1, Val := encoding.DecodeUnsigned(buffer, offset+length, int(lenValue)) + length += length1 + entry.Status = BACnetRouterEntryStatus(Val) + + // performance_index (optional) + if offset < apduLen { + length1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+length) + if tagNumber != byte(encoding.UnsignedInt) { + length += length1 + length1, entry.PerformanceIndex = encoding.DecodeUnsigned(buffer, offset+length, int(lenValue)) + length += length1 + } + } + + return length, nil +} + +type BACnetVTSession struct { + LocalVTSessionID int + RemoteVTSessionID int + RemoteVTAddress BACnetAddress +} + +// decode method for BACnetVTSession +func (b *BACnetVTSession) Decode(buffer []byte, offset, apduLen int) int { + // Implement decode logic here + return -1 +} + +// BACnetAccessThreatLevel struct definition +type BACnetAccessThreatLevel struct { + Value int +} + +// decode method for BACnetAccessThreatLevel +func (b *BACnetAccessThreatLevel) Decode(buffer []byte, offset, apduLen int) int { + // Implement decode logic here + return -1 +} + +type BACnetDestination struct { + ValidDays *BACnetDaysOfWeek + FromTime time.Time + ToTime time.Time + Recipient *BACnetRecipient + ProcessIdentifier uint32 + IssueConfirmedNotifications bool + Transitions *BACnetEventTransitionBits +} + +func (b *BACnetDestination) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.BitString) { + return -1 + } + leng += leng1 + b.ValidDays = &BACnetDaysOfWeek{} + + leng1 = b.ValidDays.Decode(buffer, offset+leng, int(lenValue)) + + if leng1 < 0 { + return -1 + } + leng += leng1 + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.Time) { + return -1 + } + leng += leng1 + leng1, b.FromTime = encoding.DecodeBACnetTimeSafe(buffer, offset+leng, int(lenValue)) + + leng += leng1 + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.Time) { + return -1 + } + leng += leng1 + leng1, b.ToTime = encoding.DecodeBACnetTimeSafe(buffer, offset+leng, int(lenValue)) + + leng += leng1 + + b.Recipient = &BACnetRecipient{} + leng1 = b.Recipient.Decode(buffer, offset+leng, apduLen-leng) + + if leng1 < 0 { + return -1 + } + leng += leng1 + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.UnsignedInt) { + return -1 + } + leng += leng1 + leng1, b.ProcessIdentifier = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + + leng += leng1 + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.Boolean) { + return -1 + } + leng += leng1 + if lenValue > 0 { + b.IssueConfirmedNotifications = true + } else { + b.IssueConfirmedNotifications = false + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != byte(encoding.BitString) { + return -1 + } + leng += leng1 + + b.Transitions = &BACnetEventTransitionBits{} + leng1 = b.Transitions.Decode(buffer, offset+leng, int(lenValue)) + if leng1 < 0 { + return -1 + } + leng += leng1 + + return leng +} + +type BACnetDaysOfWeek struct { + unusedBits byte + bitString BACnetBitString + monday bool + tuesday bool + wednesday bool + thursday bool + friday bool + saturday bool + sunday bool +} + +func NewBACnetDaysOfWeek() *BACnetDaysOfWeek { + return &BACnetDaysOfWeek{ + unusedBits: 1, + bitString: *NewBACnetBitString(1, *internal.NewBitArray(8)), + } +} + +func (d *BACnetDaysOfWeek) String() string { + return fmt.Sprintf("%08b", d.bitString.Value) +} + +func (d *BACnetDaysOfWeek) Decode(buffer []byte, offset int, apduLen int) int { + d.bitString = BACnetBitString{} + return d.bitString.Decode(buffer, offset, apduLen) +} + +func (d *BACnetDaysOfWeek) SetDay(day int, value bool) error { + if day < 0 || day > 6 { + return fmt.Errorf("Day index out of range") + } + d.bitString.Value.Set(day, value) + return nil +} + +func (d *BACnetDaysOfWeek) GetDay(day int) (bool, error) { + if day < 0 || day > 6 { + return false, fmt.Errorf("Day index out of range") + } + return d.bitString.Value.Get(day) == true, nil +} + +type BACnetBitString struct { + UnusedBits byte + Value internal.BitArray +} + +func NewBACnetBitString(unusedBits byte, value internal.BitArray) *BACnetBitString { + return &BACnetBitString{ + UnusedBits: unusedBits, + Value: value, + } +} + +func (b *BACnetBitString) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + if apduLen > 0 { + b.UnusedBits = buffer[offset] + leng += 1 + b.Value = *internal.NewBitArray((apduLen - 1) * 8) + bit := 0 + for i := 1; i < apduLen; i++ { + for i2 := 0; i2 < 8; i2++ { + b.Value.Set(bit, buffer[offset+i]&(1<<(7-i2)) != 0) + bit++ + } + } + } + return apduLen +} + +type BACnetRecipient struct { + Value interface{} +} + +func (br *BACnetRecipient) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + + if tagNumber == 0 { + // device_identifier + leng += leng1 + br.Value = &ObjectIdentifier{} + leng += br.Value.(*ObjectIdentifier).Decode(buffer, offset+leng, int(lenValue)) + } else if tagNumber == 1 { + // address + br.Value = &BACnetAddress{} + leng += br.Value.(*BACnetAddress).Decode(buffer, offset+leng, int(lenValue)) + } else { + return -1 + } + + return leng +} + +// BACnetEventTransitionBits represents a BACnet event transition bits structure. +type BACnetEventTransitionBits struct { + UnusedBits uint8 + BitString *BACnetBitString +} + +// NewBACnetEventTransitionBits creates a new BACnet event transition bits instance. +func NewBACnetEventTransitionBits() *BACnetEventTransitionBits { + return &BACnetEventTransitionBits{ + UnusedBits: 5, + BitString: NewBACnetBitString(5, *internal.NewBitArray(5)), + } +} + +// Decode decodes the bit string from a buffer. +func (e *BACnetEventTransitionBits) Decode(buffer []byte, offset, apduLen int) int { + bitString := NewBACnetBitString(0, internal.BitArray{}) + decodedLen := bitString.Decode(buffer, offset, apduLen) + + e.BitString = bitString + return decodedLen +} + +// ToOffNormal returns the value of ToOffNormal property. +func (e *BACnetEventTransitionBits) ToOffNormal() bool { + return e.BitString.Value.Get(0) == true +} + +// SetToOffNormal sets the value of ToOffNormal property. +func (e *BACnetEventTransitionBits) SetToOffNormal(a bool) { + e.BitString.Value.Set(0, a) +} + +// ToFault returns the value of ToFault property. +func (e *BACnetEventTransitionBits) ToFault() bool { + return e.BitString.Value.Get(1) == true +} + +// SetToFault sets the value of ToFault property. +func (e *BACnetEventTransitionBits) SetToFault(a bool) { + e.BitString.Value.Set(1, a) +} + +// ToNormal returns the value of ToNormal property. +func (e *BACnetEventTransitionBits) ToNormal() bool { + return e.BitString.Value.Get(2) == true +} + +// SetToNormal sets the value of ToNormal property. +func (e *BACnetEventTransitionBits) SetToNormal(a bool) { + e.BitString.Value.Set(2, a) +} diff --git a/pkg/encoding/decoding.go b/pkg/encoding/decoding.go index 52065e5..84b8717 100644 --- a/pkg/encoding/decoding.go +++ b/pkg/encoding/decoding.go @@ -1,5 +1,11 @@ package encoding +import ( + "encoding/binary" + "math" + "time" +) + type BacnetCharacterStringEncodings int const ( @@ -93,3 +99,48 @@ func decodeContextCharacterString(buffer []byte, offset, maxLength int, tagNumbe return leng, charString } + +func DecodeSigned(buffer []byte, offset int, lenValue int) (int, int) { + value := 0 + for i := 0; i < lenValue; i++ { + value += int(buffer[offset+i]) << uint(8*(lenValue-i-1)) + } + // Check if it is negative + if value > int(math.Pow(256, float64(lenValue))/2)-1 { + value = -(int(math.Pow(256, float64(lenValue))) - value) + } + return lenValue, value +} + +func decodeReal(buffer []byte, offset int) (int, float32) { + value := binary.BigEndian.Uint32(buffer[offset : offset+4]) + return 4, math.Float32frombits(value) +} + +func DecodeRealSafe(buffer []byte, offset int, lenValue int) (int, float32) { + if lenValue != 4 { + value := float32(0.0) + return lenValue, value + } + return decodeReal(buffer, offset) +} + +func decodeDouble(buffer []byte, offset int) (int, float64) { + value := binary.BigEndian.Uint64(buffer[offset : offset+8]) + return 8, math.Float64frombits(value) +} + +func DecodeDoubleSafe(buffer []byte, offset int, lenValue int) (int, float64) { + if lenValue != 8 { + value := float64(0.0) + return lenValue, value + } + return decodeDouble(buffer, offset) +} + +func DecodeBACnetTimeSafe(buf []byte, offset, lenVal int) (int, time.Time) { + if lenVal != 4 { + return lenVal, time.Time{} + } + return decodeBACnetTime(buf, offset) +} diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go index 5b85e75..b537c83 100644 --- a/pkg/encoding/encoding.go +++ b/pkg/encoding/encoding.go @@ -3,6 +3,7 @@ package encoding import ( "bytes" "encoding/binary" + "math" ) const ( @@ -106,3 +107,35 @@ func encodeBACnetCharacterString(value string) []byte { encodedValue := []byte(value) return append(encoding, encodedValue...) } + +func EncodeApplicationBoolean(val bool) []byte { + if val { + return EncodeTag(Boolean, false, 1) + } + return EncodeTag(Boolean, false, 0) +} + +func EncodeApplicationSigned(val int32) []byte { + tmp := EncodeSigned(val) + return append(EncodeTag(SignedInt, false, len(tmp)), tmp...) +} + +func EncodeApplicationReal(val float32) []byte { + return append(EncodeTag(Real, false, 4), encodeBACnetReal(val)...) +} + +func EncodeApplicationDouble(val float64) []byte { + return append(EncodeTag(Double, false, 8), encodeBACnetDouble(val)...) +} + +func encodeBACnetReal(value float32) []byte { + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, math.Float32bits(value)) + return buf +} + +func encodeBACnetDouble(value float64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, math.Float64bits(value)) + return buf +} diff --git a/pkg/encoding/tags.go b/pkg/encoding/tags.go index 0507038..8bd9239 100644 --- a/pkg/encoding/tags.go +++ b/pkg/encoding/tags.go @@ -41,15 +41,6 @@ func IsContextSpecific(b byte) bool { return (b & 0x8) == 0x8 } -func decodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { - len = 1 - - if isExtendedTagNumber(buf[offset]) { - return len + 1, buf[offset+len] - } - return len, buf[offset] >> 4 -} - func IsContextTag(buf []byte, offset int, tagNum byte) bool { _, myTagNum := decodeTagNumber(buf, offset) return IsContextSpecific(buf[offset]) && myTagNum == tagNum @@ -90,8 +81,9 @@ func DecodeTagNumberAndValue(buf []byte, offset int) (len int, tagNum byte, val } -func DecodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { +func decodeTagNumber(buf []byte, offset int) (len int, tagNum byte) { len = 1 + if isExtendedTagNumber(buf[offset]) { return len + 1, buf[offset+len] } @@ -132,3 +124,13 @@ func EncodeTag(tagNum BACnetApplicationTag, ctxSpecific bool, lenVal int) []byte return append(tag, EncodeUnsigned(uint32(lenVal))...) } } + +func IsOpeningTagNumber(buf []byte, offset int, tagNum byte) bool { + _, myTagNum := decodeTagNumber(buf, offset) + return isOpeningTag(buf[offset]) && myTagNum == tagNum +} + +func IsClosingTagNumber(buf []byte, offset int, tagNum byte) bool { + _, myTagNum := decodeTagNumber(buf, offset) + return isClosingTag(buf[offset]) && myTagNum == tagNum +} From 0c7837d5faeb4f2803b28e1a3910f67f9e35b714 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Wed, 13 Sep 2023 19:06:03 +0300 Subject: [PATCH 16/25] Fix error handling in BACnetValue.Decode() The BACnetValue.Decode() method now returns an error to handle decoding errors properly. - The method signature has been updated to return (int, error) - If there is an error during decoding, the method now returns (-1, err) This change ensures that decoding errors are properly handled and allows for better error reporting and debugging. Signed-off-by: SammyOina --- pkg/bacnet/property.go | 284 +++++++++++++++++++++++++++++++++++--- pkg/encoding/date_time.go | 2 +- 2 files changed, 266 insertions(+), 20 deletions(-) diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index f91fe9e..f4d950d 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -137,7 +137,7 @@ type BACnetValue struct { Value interface{} } -func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encoding.ObjectType, propID encoding.PropertyIdentifier) int { +func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encoding.ObjectType, propID encoding.PropertyIdentifier) (int, error) { length := 0 var err error @@ -167,7 +167,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi length-- decodeLen, err = bv.Value.(*RouterEntry).Decode(buffer, offset+length, apduLen) if err != nil { - return -1 + return -1, err } } else if propID == encoding.ActiveVtSessions { bv.Tag = nil @@ -214,7 +214,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi case encoding.StatusFlags: bv.Tag = nil bitValue := &BACnetStatusFlags{} - decodeLen = bitValue.Decode(buffer, offset, lenValueType) + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue case encoding.EventEnable, encoding.AckedTransitions: bv.Tag = nil @@ -224,33 +224,35 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi case encoding.LimitEnable: bv.Tag = nil bitValue := &BACnetLimitEnable{} - decodeLen = bitValue.Decode(buffer, offset, lenValueType) + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue case encoding.ProtocolObjectTypesSupported: bv.Tag = nil bitValue := &BACnetObjectTypesSupported{} - decodeLen = bitValue.Decode(buffer, offset, lenValueType) + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue case encoding.ProtocolServicesSupported: bv.Tag = nil bitValue := &BACnetServicesSupported{} - decodeLen = bitValue.Decode(buffer, offset, lenValueType) + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue default: bitValue := &BACnetBitString{} - decodeLen = bitValue.Decode(buffer, offset, lenValueType) + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue } case Enumerated: - decodeLen, uintValue := encoding.DecodeEnumerated(buffer, offset+length, lenValueType, objType, propID) - bv.Value = uintValue + decodeLen, bv.Value = encoding.DecodeEnumerated(buffer, offset+length, lenValueType, &objType, &propID) case Date: switch propID { case encoding.EffectivePeriod: bv.Tag = nil - bv.Value = NewBACnetDateRange() + bv.Value = &BACnetDateRange{} length-- - decodeLen = bv.Value.(*BACnetDateRange).Decode(buffer, offset+length, apduLen) + decodeLen, err = bv.Value.(*BACnetDateRange).Decode(buffer, offset+length, apduLen) + if err != nil { + return -1, err + } case encoding.MinimumValueTimestamp, encoding.MaximumValueTimestamp, encoding.ChangeOfStateTime, @@ -273,23 +275,20 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi length-- decodeLen = bv.Value.(*DateTime).Decode(buffer, offset+length) default: - decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+length, lenValueType) - bv.Value = dateValue + decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) } if (objType == encoding.DateTimeValue || objType == encoding.TimePatternValue) && (propID == encoding.PresentValue || propID == encoding.RelinquishDefault) { - decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+length, lenValueType) - bv.Value = dateValue + decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) } case Time: - decodeLen, timeValue := encoding.DecodeBACnetTimeSafe(buffer, offset+length, lenValueType) - bv.Value = timeValue + decodeLen, bv.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+length, int(lenValueType)) case BACnetObjectIdentifier: if propID == encoding.LastKeyServer || propID == encoding.ManualSlaveAddressBinding || propID == encoding.SlaveAddressBinding || propID == encoding.DeviceAddressBinding { bv.Tag = nil - bv.Value = NewBACnetAddressBinding() + bv.Value = &BACnetAddressBinding{} length-- decodeLen = bv.Value.(*BACnetAddressBinding).Decode(buffer, offset+length, apduLen) } else { @@ -302,7 +301,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi } if decodeLen < 0 { - return -1 + return -1, fmt.Errorf("no tags decoded") } length += decodeLen } @@ -1224,3 +1223,250 @@ func (e *BACnetEventTransitionBits) ToNormal() bool { func (e *BACnetEventTransitionBits) SetToNormal(a bool) { e.BitString.Value.Set(2, a) } + +// BACnetStatusFlags represents a BACnet status flags. +type BACnetStatusFlags struct { + unusedbits int + bitstring BACnetBitString + inalarm bool + fault bool + overridden bool + outofservice bool +} + +// NewBACnetStatusFlags creates a new BACnetStatusFlags instance. +func NewBACnetStatusFlags() *BACnetStatusFlags { + return &BACnetStatusFlags{ + unusedbits: 4, + bitstring: *NewBACnetBitString(4, *internal.NewBitArrayFromByte(0x00)), + inalarm: false, + fault: false, + overridden: false, + outofservice: false, + } +} + +// ASN1decode decodes BACnetStatusFlags from a buffer. +func (s *BACnetStatusFlags) Decode(buffer []byte, offset, apduLen int) int { + s.bitstring = *NewBACnetBitString(byte(s.unusedbits), *internal.NewBitArrayFromByte(0x00)) + return s.bitstring.Decode(buffer, offset, apduLen) +} + +// InAlarm returns the inalarm property. +func (s *BACnetStatusFlags) InAlarm() bool { + return s.bitstring.Value.Get(0) +} + +// SetInAlarm sets the inalarm property. +func (s *BACnetStatusFlags) SetInAlarm(a bool) { + s.bitstring.Value.Set(0, a) +} + +// Fault returns the fault property. +func (s *BACnetStatusFlags) Fault() bool { + return s.bitstring.Value.Get(1) +} + +// SetFault sets the fault property. +func (s *BACnetStatusFlags) SetFault(a bool) { + s.bitstring.Value.Set(1, a) +} + +// Overridden returns the overridden property. +func (s *BACnetStatusFlags) Overridden() bool { + return s.bitstring.Value.Get(2) +} + +// SetOverridden sets the overridden property. +func (s *BACnetStatusFlags) SetOverridden(a bool) { + s.bitstring.Value.Set(2, a) +} + +// OutOfService returns the outofservice property. +func (s *BACnetStatusFlags) OutOfService() bool { + return s.bitstring.Value.Get(3) +} + +// SetOutOfService sets the outofservice property. +func (s *BACnetStatusFlags) SetOutOfService(a bool) { + s.bitstring.Value.Set(3, a) +} + +type BACnetLimitEnable struct { + unusedBits uint8 + bitString BACnetBitString + lowLimitEnable bool + highLimitEnable bool +} + +func NewBACnetLimitEnable() *BACnetLimitEnable { + return &BACnetLimitEnable{ + unusedBits: 6, + bitString: *NewBACnetBitString(6, *internal.NewBitArrayFromByte(0x00)), + lowLimitEnable: false, + highLimitEnable: false, + } +} + +func (b *BACnetLimitEnable) Decode(buffer []byte, offset, apduLen int) int { + b.bitString = *NewBACnetBitString(0, *internal.NewBitArrayFromByte(0x00)) + return b.bitString.Decode(buffer, offset, apduLen) +} + +func (b *BACnetLimitEnable) LowLimitEnable() bool { + return b.bitString.Value.Get(0) +} + +func (b *BACnetLimitEnable) SetLowLimitEnable(a bool) { + b.bitString.Value.Set(0, a) +} + +func (b *BACnetLimitEnable) HighLimitEnable() bool { + return b.bitString.Value.Get(1) +} + +func (b *BACnetLimitEnable) SetHighLimitEnable(a bool) { + b.bitString.Value.Set(1, a) +} + +type BACnetObjectTypesSupported struct { + unusedbits uint8 + bitstring BACnetBitString +} + +type ObjectTypesSupportedProperty int + +const ( + AnalogInput ObjectTypesSupportedProperty = iota + AanalofOutput + AnalogValue + BinaryInput + BinaryOutput + // TODO Add other properties here +) + +func NewBACnetObjectTypesSupported() *BACnetObjectTypesSupported { + return &BACnetObjectTypesSupported{ + unusedbits: 3, + bitstring: *NewBACnetBitString(3, *internal.NewBitArray(64)), + } +} + +func (b *BACnetObjectTypesSupported) Set(property ObjectTypesSupportedProperty, value bool) { + b.bitstring.Value.Set(int(property), value) +} + +func (b *BACnetObjectTypesSupported) Get(property ObjectTypesSupportedProperty) bool { + return b.bitstring.Value.Get(int(property)) +} + +func (b *BACnetObjectTypesSupported) Decode(buf []byte, offset, apduLen int) int { + return b.bitstring.Decode(buf, offset, apduLen) +} + +type BACnetServicesSupported struct { + unusedbits uint8 + bitstring BACnetBitString +} + +type ServicesSupportedProperty int + +// TODO check index sequence +const ( + acknowledgeAlarm ServicesSupportedProperty = iota + confirmedCOVNotification + confirmedCOVNotificationMultiple + confirmedEventNotification + getAlarmSummary + getEnrollmentSummary + getEventInformation + lifeSafetyOperation + subscribeCOV + subscribeCOVProperty + subscribeCOVPropertyMultiple + atomicReadFile + atomicWriteFile + addListElement + removeListElement + createObject + deleteObject + readProperty + readPropertyMultiple + readRange + writeGroup + writeProperty + writePropertyMultiple + deviceCommunicationControl + confirmedPrivateTransfer + confirmedTextMessage + reinitializeDevice + vtOpen + vtClose + vtData + whoAmI + youAre + iAm + iHave + unconfirmedCOVNotification + unconfirmedCOVNotificationMultiple + unconfirmedEventNotification + unconfirmedPrivateTransfer + unconfirmedTextMessage + timeSynchronization + utcTimeSynchronization + whoHas + whoIs +) + +func NewBACnetServicesSupported() *BACnetServicesSupported { + return &BACnetServicesSupported{ + unusedbits: 7, + bitstring: *NewBACnetBitString(7, *internal.NewBitArrayFromByte(0x00000000000000)), + } +} + +func (b *BACnetServicesSupported) Set(property ServicesSupportedProperty, value bool) { + b.bitstring.Value.Set(int(property), value) +} + +func (b *BACnetServicesSupported) Get(property ServicesSupportedProperty) bool { + return b.bitstring.Value.Get(int(property)) +} + +func (b *BACnetServicesSupported) Decode(buf []byte, offset, apduLen int) int { + return b.bitstring.Decode(buf, offset, apduLen) +} + +// BACnetDateRange is a struct representing a date range in BACnet. +type BACnetDateRange struct { + StartDate time.Time + EndDate time.Time +} + +// Decode decodes a BACnetDateRange from the given buffer, offset, and length. +func (dr *BACnetDateRange) Decode(buffer []byte, offset, apduLen int) (int, error) { + var leng int + + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(Date) { + leng += leng1 + leng1, startDate := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) + dr.StartDate = startDate + leng += leng1 + } else { + return -1, fmt.Errorf("Unexpected tag number: %v", tagNumber) + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber == byte(Date) { + leng += leng1 + leng1, endDate := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) + + dr.EndDate = endDate + leng += leng1 + } else { + return -1, fmt.Errorf("Unexpected tag number: %v", tagNumber) + } + + return leng, nil +} diff --git a/pkg/encoding/date_time.go b/pkg/encoding/date_time.go index a1a7250..a47f305 100644 --- a/pkg/encoding/date_time.go +++ b/pkg/encoding/date_time.go @@ -31,7 +31,7 @@ func decodeDate(buf []byte, offset int) (int, time.Time) { return 4, time.Date(int(year)+1900, time.Month(month), int(day), 0, 0, 0, 0, nil) } -func decodeDateSafe(buf []byte, offset, lenVal int) (int, time.Time) { +func DecodeDateSafe(buf []byte, offset, lenVal int) (int, time.Time) { if lenVal != 4 { return lenVal, time.Time{} } From 42fcc509ce0d05eb4c55d45957fc689813fb35fb Mon Sep 17 00:00:00 2001 From: SammyOina Date: Thu, 14 Sep 2023 00:17:01 +0300 Subject: [PATCH 17/25] Fix decoding error for object_identifier and property_identifier in ReadPropertyACK.Decode() method The decoding error for object_identifier and property_identifier in the ReadPropertyACK.Decode() method has been fixed. This change ensures that the error message accurately reflects the cause of the decoding failure. This commit addresses the following changes: - Updated error message for object_identifier decoding error - Updated error message for property_identifier decoding error Signed-off-by: SammyOina --- pkg/bacnet/property.go | 305 ++++++++++++++++++++++++++++++++++++--- pkg/encoding/decoding.go | 20 +++ 2 files changed, 308 insertions(+), 17 deletions(-) diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index f4d950d..52c3483 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -86,7 +86,7 @@ func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error leng1 := r.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) leng += leng1 } else { - return -1, errors.New("ASN.1 decoding error for object_identifier") + return -1, errors.New("decoding error for object_identifier") } // 2 propertyidentifier @@ -98,7 +98,7 @@ func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error r.PropertyIdentifier = propID leng += leng1 } else { - return -1, errors.New("ASN.1 decoding error for property_identifier") + return -1, errors.New("decoding error for property_identifier") } // 2 property_array_index @@ -116,17 +116,20 @@ func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error for !encoding.IsClosingTagNumber(buffer, offset+leng, 3) && leng < apduLen { bValue := BACnetValue{} propId := r.PropertyIdentifier.(encoding.PropertyIdentifier) - leng1 := bValue.Decode(buffer, offset+leng, apduLen-leng, r.ObjectIdentifier.Type, propId) + leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng, r.ObjectIdentifier.Type, propId) + if err != nil { + return -1, err + } leng += leng1 r.PropertyValue = append(r.PropertyValue, bValue) } if encoding.IsClosingTagNumber(buffer, offset+leng, 3) { leng++ } else { - return -1, errors.New("ASN.1 decoding error for property_value") + return -1, errors.New("decoding error for property_value") } } else { - return -1, errors.New("ASN.1 decoding error for property_value") + return -1, errors.New("decoding error for property_value") } return leng, nil @@ -292,8 +295,10 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi length-- decodeLen = bv.Value.(*BACnetAddressBinding).Decode(buffer, offset+length, apduLen) } else { - decodeLen, objectType, instance := encoding.DecodeObjectIDSafe(buffer, offset+length, lenValueType) - bv.Value = ObjectIdentifier{Type: objectType, Instance: instance} + var objectType encoding.ObjectType + var instance uint32 + decodeLen, objectType, instance = encoding.DecodeObjectIDSafe(buffer, offset+length, lenValueType) + bv.Value = ObjectIdentifier{Type: objectType, Instance: ObjectInstance(instance)} } default: log.Println("Unhandled tag:", bv.Tag) @@ -308,17 +313,25 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi } else { switch propID { case encoding.BacnetIpGlobalAddress, encoding.FdBbmdAddress: - bv.Value = NewBACnetHostNPort() - length += bv.Value.(*BACnetHostNPort).Decode(buffer, offset+length, apduLen-length) + bv.Value = &BACnetHostNPort{} + length1, err := bv.Value.(*BACnetHostNPort).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } + length += length1 case encoding.UtcTimeSynchronizationRecipients, encoding.RestartNotificationRecipients, encoding.TimeSynchronizationRecipients, encoding.CovuRecipients: - bv.Value = NewBACnetRecipient() + bv.Value = &BACnetRecipient{} length += bv.Value.(*BACnetRecipient).Decode(buffer, offset+length, apduLen-length) case encoding.KeySets: - bv.Value = NewBACnetSecurityKeySet() - length += bv.Value.(*BACnetSecurityKeySet).Decode(buffer, offset+length, apduLen-length) + bv.Value = &BACnetSecurityKeySet{} + length1, err := bv.Value.(*BACnetSecurityKeySet).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } + length += length1 case encoding.EventTimeStamps, encoding.LastCommandTime, encoding.CommandTimeArray, @@ -326,13 +339,17 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi encoding.TimeOfDeviceRestart, encoding.AccessEventTime, encoding.UpdateTime: - bv.Value = NewBACnetTimeStamp() - length += bv.Value.(*BACnetTimeStamp).Decode(buffer, offset+length, apduLen-length) + bv.Value = &BACnetTimeStamp{} + length1, err := bv.Value.(*BACnetTimeStamp).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } + length += length1 case encoding.ListOfGroupMembers: - bv.Value = NewReadAccessSpecification() + bv.Value = &ReadAccessSpecification{} length += bv.Value.(*ReadAccessSpecification).Decode(buffer, offset+length, apduLen-length) case encoding.ListOfObjectPropertyReferences: - bv.Value = NewBACnetDeviceObjectPropertyReference() + bv.Value = &BACnetDeviceObjectPropertyReference{} length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) case encoding.MemberOf, encoding.ZoneMembers, @@ -1246,7 +1263,7 @@ func NewBACnetStatusFlags() *BACnetStatusFlags { } } -// ASN1decode decodes BACnetStatusFlags from a buffer. +// decode decodes BACnetStatusFlags from a buffer. func (s *BACnetStatusFlags) Decode(buffer []byte, offset, apduLen int) int { s.bitstring = *NewBACnetBitString(byte(s.unusedbits), *internal.NewBitArrayFromByte(0x00)) return s.bitstring.Decode(buffer, offset, apduLen) @@ -1470,3 +1487,257 @@ func (dr *BACnetDateRange) Decode(buffer []byte, offset, apduLen int) (int, erro return leng, nil } + +type BACnetAddressBinding struct { + DeviceIdentifier ObjectIdentifier + DeviceAddress BACnetAddress +} + +func (binding *BACnetAddressBinding) Decode(buffer []byte, offset int, apduLen int) int { + length := 0 + length1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+length) + + // device_identifier + if tagNumber == byte(BACnetObjectIdentifier) { + length += length1 + binding.DeviceIdentifier = ObjectIdentifier{} + length += binding.DeviceIdentifier.Decode(buffer, offset+length, int(lenValue)) + } else { + return -1 + } + + length1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+length) + + if tagNumber == byte(UnsignedInt) { + binding.DeviceAddress = BACnetAddress{} + length += binding.DeviceAddress.Decode(buffer, offset+length, int(lenValue)) + } else { + return -1 + } + + return length +} + +type BACnetHostNPort struct { + Host *BACnetHostAddress + Port uint32 +} + +func (b *BACnetHostNPort) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + if !encoding.IsOpeningTagNumber(buffer, offset+leng, 0) { + return -1, errors.New("Invalid opening tag") + } + leng++ + b.Host = &BACnetHostAddress{} + hostLen, err := b.Host.Decode(buffer, offset+leng, apduLen-leng) + if err != nil { + return -1, err + } + leng += hostLen + leng++ + + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 1 { + leng1, b.Port = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Invalid tag number") + } + } else { + return -1, errors.New("Invalid context tag") + } + + return leng, nil +} + +type BACnetHostAddress struct { + Value interface{} +} + +func (b *BACnetHostAddress) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + + switch tagNumber { + case byte(Null): + leng += leng1 + b.Value = nil + case byte(OctetString): + leng += leng1 + leng1, octetString := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + b.Value = octetString + leng += leng1 + case byte(CharacterString): + leng += leng1 + leng1, characterString := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + b.Value = characterString + leng += leng1 + default: + return -1, errors.New("Invalid tag number") + } + + return leng, nil +} + +type BACnetSecurityKeySet struct { + KeyRevision uint32 + ActivationTime *DateTime + ExpirationTime *DateTime + KeyIDs []*BACnetKeyIdentifier +} + +func (b *BACnetSecurityKeySet) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + // key_revision + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 0 { + leng1, b.KeyRevision = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Invalid tag number") + } + } else { + return -1, errors.New("Invalid context tag") + } + + // activation_time + if encoding.IsOpeningTagNumber(buffer, offset+leng, 1) { + leng++ + b.ActivationTime = &DateTime{} + leng1 := b.ActivationTime.Decode(buffer, offset+leng) + leng += leng1 + } else { + return -1, errors.New("Invalid opening tag for activation_time") + } + + // expiration_time + if encoding.IsOpeningTagNumber(buffer, offset+leng, 2) { + leng++ + b.ExpirationTime = &DateTime{} + leng1 := b.ExpirationTime.Decode(buffer, offset+leng) + leng += leng1 + } else { + return -1, errors.New("Invalid opening tag for expiration_time") + } + + b.KeyIDs = make([]*BACnetKeyIdentifier, 0) + if encoding.IsOpeningTagNumber(buffer, offset+leng, 3) && leng < apduLen { + leng++ + for !encoding.IsClosingTagNumber(buffer, offset+leng, 3) { + bValue := &BACnetKeyIdentifier{} + leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng) + if err != nil { + return -1, err + } + b.KeyIDs = append(b.KeyIDs, bValue) + leng += leng1 + } + leng++ + } else { + return -1, errors.New("Invalid opening tag for key_ids or unexpected end of data") + } + + return leng, nil +} + +type BACnetKeyIdentifier struct { + Algorithm uint32 + KeyID uint32 +} + +func (b *BACnetKeyIdentifier) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + // algorithm + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 0 { + leng1, b.Algorithm = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Invalid tag number for algorithm") + } + } else { + return -1, errors.New("Invalid context tag for algorithm") + } + + // key_id + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 1 { + leng1, b.KeyID = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Invalid tag number for key_id") + } + } else { + return -1, errors.New("Invalid context tag for key_id") + } + + return leng, nil +} + +type BACnetTimeStamp struct { + Value interface{} +} + +func (b *BACnetTimeStamp) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + if encoding.IsContextTag(buffer, offset+leng, 2) { + // BACnetDateTime + leng1, tagNumber, _ := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 2 { + b.Value = &DateTime{} + leng1 := b.Value.(*DateTime).Decode(buffer, offset+leng) + leng += leng1 + } else { + return -1, errors.New("Invalid tag number for BACnetDateTime") + } + } else if encoding.IsContextTag(buffer, offset+leng, 1) { + // sequence number + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 1 { + leng1, seqNum := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + b.Value = seqNum + leng += leng1 + } else { + return -1, errors.New("Invalid tag number for sequence number") + } + } else if encoding.IsContextTag(buffer, offset+leng, 0) { + // time + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 0 { + leng1, bacnetTime := encoding.DecodeBACnetTimeSafe(buffer, offset+leng, int(lenValue)) + b.Value = bacnetTime + leng += leng1 + } else { + return -1, errors.New("Invalid tag number for time") + } + } else { + return -1, errors.New("Invalid context tag") + } + + return leng, nil +} + +func (b *BACnetTimeStamp) Encode() []byte { + // Implement the encoding logic as needed for your specific application. + return nil +} + +func (b *BACnetTimeStamp) EncodeContext(tagNumber encoding.BACnetApplicationTag) []byte { + tmp := b.Encode() + return append(encoding.EncodeTag(tagNumber, true, len(tmp)), tmp...) +} diff --git a/pkg/encoding/decoding.go b/pkg/encoding/decoding.go index 84b8717..0cf82de 100644 --- a/pkg/encoding/decoding.go +++ b/pkg/encoding/decoding.go @@ -144,3 +144,23 @@ func DecodeBACnetTimeSafe(buf []byte, offset, lenVal int) (int, time.Time) { } return decodeBACnetTime(buf, offset) } + +func decodeObjectID(buffer []byte, offset int) (int, ObjectType, uint32) { + var value uint32 + var leng int + + leng, value = DecodeUnsigned(buffer, offset, 4) + + objectInstance := value & MaxInstance + + objectType := ObjectType((int(value) >> InstanceBits) & MaxObject) + + return leng, objectType, objectInstance +} + +func DecodeObjectIDSafe(buffer []byte, offset int, lenValue uint32) (int, ObjectType, uint32) { + if lenValue != 4 { + return 0, 0, 0 + } + return decodeObjectID(buffer, offset) +} From b8a8db0e0f62792056569fab8e830844d06999e5 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Fri, 15 Sep 2023 00:59:58 +0300 Subject: [PATCH 18/25] Fix decoding error in BACnetValue.Decode() The BACnetValue.Decode() method was updated to handle decoding errors in the ReadAccessSpecification and BACnetDeviceObjectPropertyReference types. This ensures that the decoding process is accurate and reliable. Signed-off-by: SammyOina --- pkg/bacnet/property.go | 434 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 423 insertions(+), 11 deletions(-) diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index 52c3483..907a249 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -347,10 +347,16 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi length += length1 case encoding.ListOfGroupMembers: bv.Value = &ReadAccessSpecification{} - length += bv.Value.(*ReadAccessSpecification).Decode(buffer, offset+length, apduLen-length) + length, err = bv.Value.(*ReadAccessSpecification).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } case encoding.ListOfObjectPropertyReferences: bv.Value = &BACnetDeviceObjectPropertyReference{} - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + length, err = bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } case encoding.MemberOf, encoding.ZoneMembers, encoding.DoorMembers, @@ -371,33 +377,36 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi encoding.BelongsTo, encoding.LastAccessPoint, encoding.EnergyMeterRef: - bv.Value = NewBACnetDeviceObjectReference() + bv.Value = &BACnetDeviceObjectReference{} length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) case encoding.EventAlgorithmInhibitRef, encoding.InputReference, encoding.ManipulatedVariableReference, encoding.ControlledVariableReference: - bv.Value = NewBACnetObjectPropertyReference() + bv.Value = &BACnetObjectPropertyReference{} length += bv.Value.(*BACnetObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) case encoding.LoggingRecord: - bv.Value = NewBACnetAccumulatorRecord() - length += bv.Value.(*BACnetAccumulatorRecord).Decode(buffer, offset+length, apduLen-length) + bv.Value = &BACnetAccumulatorRecord{} + length, err = bv.Value.(*BACnetAccumulatorRecord).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } case encoding.Action: - bv.Value = NewBACnetActionList() + bv.Value = &BACnetActionList{} length += bv.Value.(*BACnetActionList).Decode(buffer, offset+length, apduLen-length) case encoding.Scale: - bv.Value = NewBACnetScale() + bv.Value = &BACnetScale{} length += bv.Value.(*BACnetScale).Decode(buffer, offset+length, apduLen-length) case encoding.LightingCommand: - bv.Value = NewBACnetLightingCommand() + bv.Value = &BACnetLightingCommand{} length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) case encoding.Prescale: - bv.Value = NewBACnetPrescale() + bv.Value = &BACnetPrescale{} length += bv.Value.(*BACnetPrescale).Decode(buffer, offset+length, apduLen-length) case encoding.RequestedShedLevel, encoding.ExpectedShedLevel, encoding.ActualShedLevel: - bv.Value = NewBACnetShedLevel() + bv.Value = &BACnetShedLevel{} length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) case encoding.LogBuffer: if objType == encoding.TrendLog { @@ -1741,3 +1750,406 @@ func (b *BACnetTimeStamp) EncodeContext(tagNumber encoding.BACnetApplicationTag) tmp := b.Encode() return append(encoding.EncodeTag(tagNumber, true, len(tmp)), tmp...) } + +// ReadAccessSpecification represents a BACnet Read Access Specification. +type ReadAccessSpecification struct { + ObjectIdentifier ObjectIdentifier + ListOfPropertyReferences []BACnetPropertyReference +} + +// Decode decodes the ReadAccessSpecification from the buffer. +func (ras *ReadAccessSpecification) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + // ObjectIdentifier + ras.ObjectIdentifier = ObjectIdentifier{} + leng += ras.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + + // ListOfPropertyReferences + if buffer[offset+leng] == 0x30 { // Check for opening tag (0x30) + leng++ + + ras.ListOfPropertyReferences = make([]BACnetPropertyReference, 0) + + for apduLen-leng > 1 && buffer[offset+leng] != 0x00 { // Check for closing tag (0x00) + bValue := BACnetPropertyReference{} + leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng) + if err != nil { + return -1, err + } + leng += leng1 + + ras.ListOfPropertyReferences = append(ras.ListOfPropertyReferences, bValue) + } + } else { + return -1, errors.New("Invalid opening tag for ListOfPropertyReferences") + } + + leng++ + + return leng, nil +} + +// BACnetPropertyReference represents a BACnet property reference. +type BACnetPropertyReference struct { + PropertyIdentifier interface{} + PropertyArrayIndex uint32 +} + +// Decode decodes the BACnetPropertyReference from the buffer. +func (ref *BACnetPropertyReference) Decode(buffer []byte, offset, apduLen int) (int, error) { + leng := 0 + + // propertyIdentifier + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, ref.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + } else { + return -1, errors.New("Missing context tag for PropertyIdentifier") + } + + if leng < apduLen { + if encoding.IsContextTag(buffer, offset+leng, 1) && !encoding.IsClosingTagNumber(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, ref.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + return leng, nil +} + +type BACnetDeviceObjectPropertyReference struct { + ObjectIdentifier ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex uint32 + DeviceIdentifier ObjectIdentifier +} + +func (bdopr *BACnetDeviceObjectPropertyReference) Decode(buffer []byte, offset int, apduLen int) (int, error) { + leng := 0 + + // tag 0 objectidentifier + bdopr.ObjectIdentifier = ObjectIdentifier{} + leng1 := bdopr.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + if leng1 < 0 { + return -1, errors.New("failed to decode object identifier") + } + leng += leng1 + + // tag 1 propertyidentifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, bdopr.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + } else { + return -1, errors.New("Missing tag property Identifier") + } + + if leng < apduLen { + // tag 2 property-array-index optional + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bdopr.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // tag 3 device-identifier optional + bdopr.DeviceIdentifier = ObjectIdentifier{} + leng1 := bdopr.DeviceIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 3) + if leng1 < 0 { + return -1, errors.New("failed to decode device identifier") + } + leng += leng1 + } + + return leng, nil +} + +type BACnetDeviceObjectReference struct { + DeviceIdentifier ObjectIdentifier + ObjectIdentifier ObjectIdentifier +} + +func (bdor *BACnetDeviceObjectReference) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // tag 0 device-identifier optional + bdor.DeviceIdentifier = ObjectIdentifier{} + leng1 := bdor.DeviceIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + if leng1 > 0 { + leng += leng1 + } + + // tag 1 objectidentifier + bdor.ObjectIdentifier = ObjectIdentifier{} + leng1 = bdor.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 1) + if leng1 < 0 { + return -1 + } + leng += leng1 + + return leng +} + +type BACnetObjectPropertyReference struct { + ObjectIdentifier ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex uint32 +} + +func (bopr *BACnetObjectPropertyReference) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // tag 0 objectidentifier + bopr.ObjectIdentifier = ObjectIdentifier{} + leng1 := bopr.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + if leng1 < 0 { + return -1 + } + leng += leng1 + + // tag 1 propertyidentifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, bopr.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + } else { + return -1 + } + + if leng < apduLen { + // tag 2 property-array-index optional + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bopr.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + return leng +} + +type BACnetAccumulatorRecord struct { + Timestamp BACnetTimeStamp + PresentValue uint32 + AccumulatedValue uint32 + AccumulatorStatus BACnetAccumulatorStatus +} + +type BACnetAccumulatorStatus int + +const ( + AccumulatorStatusNormal BACnetAccumulatorStatus = iota + AccumulatorStatusStarting + AccumulatorStatusRecovered + AccumulatorStatusAbnormal + AccumulatorStatusFailed +) + +func (bar *BACnetAccumulatorRecord) Decode(buffer []byte, offset int, apduLen int) (int, error) { + leng := 0 + + // 0 timestamp + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + bar.Timestamp = BACnetTimeStamp{} + leng1, err := bar.Timestamp.Decode(buffer, offset+leng, int(lenValue)) + if err != nil { + return -1, errors.New("failed to decode timestamp") + } + } else { + return -1, errors.New("Missing tag 0") + } + + // 1 present-value + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bar.PresentValue = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Missing tag 1") + } + + // 2 accumulated-value + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bar.AccumulatedValue = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Missing tag 2") + } + + // 3 accumulator-status + if encoding.IsContextTag(buffer, offset+leng, 3) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, statusValue := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + bar.AccumulatorStatus = BACnetAccumulatorStatus(statusValue) + leng += leng1 + } else { + return -1, errors.New("Missing tag 3") + } + + return leng, nil +} + +type BACnetActionList struct { + Action []BACnetActionCommand +} + +func (bal *BACnetActionList) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // SEQUENCE OF BACnetActionCommand + if encoding.IsOpeningTagNumber(buffer, offset+leng, 0) { + leng += 1 + bal.Action = make([]BACnetActionCommand, 0) + for !encoding.IsClosingTagNumber(buffer, offset+leng, 0) { + bac := BACnetActionCommand{} + leng1 := bac.Decode(buffer, offset+leng, apduLen-leng) + if leng1 < 0 { + return -1 + } + leng += leng1 + bal.Action = append(bal.Action, bac) + } + leng += 1 + } + + return leng +} + +type BACnetActionCommand struct { + DeviceIdentifier ObjectIdentifier + ObjectIdentifier ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex int + PropertyValue []BACnetValue + Priority int + PostDelay int + QuitOnFailure bool + WriteSuccessful bool +} + +func (bac *BACnetActionCommand) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // 0 device_identifier optional + if encoding.IsContextTag(buffer, offset+leng, 0) { + bac.DeviceIdentifier = ObjectIdentifier{} + leng += bac.DeviceIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + } + + // 1 object_identifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + bac.ObjectIdentifier = ObjectIdentifier{} + leng += bac.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 1) + } else { + return -1 + } + + // 2 property_identifier + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, bac.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + if leng1 < 0 { + return -1 + } + leng += leng1 + } else { + return -1 + } + + // 3 property_array_index + if encoding.IsContextTag(buffer, offset+leng, 3) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + bac.PropertyArrayIndex, _ = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + + // tag 4 property-value + if encoding.IsOpeningTagNumber(buffer, offset+leng, 4) { + leng += 1 + bac.PropertyValue = []BACnetValue{} + for !encoding.IsClosingTagNumber(buffer, offset+leng, 4) && leng < apduLen { + bv := BACnetValue{} + propID := bac.PropertyIdentifier.(encoding.PropertyIdentifier) + leng1, _ := bv.Decode(buffer, offset+leng, apduLen-leng, bac.ObjectIdentifier.Type, propID) + if leng1 < 0 { + return -1 + } + leng += leng1 + bac.PropertyValue = append(bac.PropertyValue, bv) + } + if encoding.IsClosingTagNumber(buffer, offset+leng, 4) { + leng += 1 + } else { + return -1 + } + } else { + return -1 + } + + if leng < apduLen { + // tag 5 priority optional + if encoding.IsContextTag(buffer, offset+leng, 5) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + bac.Priority, _ = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // tag 6 post-delay optional + if encoding.IsContextTag(buffer, offset+leng, 6) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + bac.PostDelay, _ = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // tag 7 quit-on-failure optional + if encoding.IsContextTag(buffer, offset+leng, 7) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + uVal, _ := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bac.QuitOnFailure = uVal > 0 + } + } + + if leng < apduLen { + // tag 8 write-successful optional + if encoding.IsContextTag(buffer, offset+leng, 8) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + uVal, _ := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bac.WriteSuccessful = uVal > 0 + } + } + + return leng +} From 079d92bce616094d26a55b3a6bb19821a57e2973 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Fri, 15 Sep 2023 17:04:18 +0300 Subject: [PATCH 19/25] finish implementing bacnet value Signed-off-by: SammyOina --- pkg/bacnet/property.go | 1503 ++++++++++++++++++++++++++------------ pkg/encoding/encoding.go | 5 + 2 files changed, 1059 insertions(+), 449 deletions(-) diff --git a/pkg/bacnet/property.go b/pkg/bacnet/property.go index 907a249..98c3ce6 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/property.go @@ -116,7 +116,7 @@ func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error for !encoding.IsClosingTagNumber(buffer, offset+leng, 3) && leng < apduLen { bValue := BACnetValue{} propId := r.PropertyIdentifier.(encoding.PropertyIdentifier) - leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng, r.ObjectIdentifier.Type, propId) + leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng, &r.ObjectIdentifier.Type, &propId) if err != nil { return -1, err } @@ -140,7 +140,7 @@ type BACnetValue struct { Value interface{} } -func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encoding.ObjectType, propID encoding.PropertyIdentifier) (int, error) { +func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType *encoding.ObjectType, propID *encoding.PropertyIdentifier) (int, error) { length := 0 var err error @@ -164,7 +164,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi bv.Value = false } case UnsignedInt: - if propID == encoding.RoutingTable { + if *propID == encoding.RoutingTable { bv.Tag = nil bv.Value = &RouterEntry{} length-- @@ -172,12 +172,12 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi if err != nil { return -1, err } - } else if propID == encoding.ActiveVtSessions { + } else if *propID == encoding.ActiveVtSessions { bv.Tag = nil bv.Value = &BACnetVTSession{} length-- decodeLen = bv.Value.(*BACnetVTSession).Decode(buffer, offset+length, apduLen) - } else if propID == encoding.ThreatLevel || propID == encoding.ThreatAuthority { + } else if *propID == encoding.ThreatLevel || *propID == encoding.ThreatAuthority { bv.Tag = nil bv.Value = &BACnetAccessThreatLevel{} length-- @@ -208,7 +208,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi decodeLen, stringValue = encoding.DecodeCharacterString(buffer, offset+length, apduLen, int(lenValueType)) bv.Value = stringValue case BitString: - switch propID { + switch *propID { case encoding.RecipientList: bv.Tag = nil bv.Value = &BACnetDestination{} @@ -245,9 +245,9 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi bv.Value = bitValue } case Enumerated: - decodeLen, bv.Value = encoding.DecodeEnumerated(buffer, offset+length, lenValueType, &objType, &propID) + decodeLen, bv.Value = encoding.DecodeEnumerated(buffer, offset+length, lenValueType, objType, propID) case Date: - switch propID { + switch *propID { case encoding.EffectivePeriod: bv.Tag = nil bv.Value = &BACnetDateRange{} @@ -280,16 +280,16 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi default: decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) } - if (objType == encoding.DateTimeValue || objType == encoding.TimePatternValue) && (propID == encoding.PresentValue || propID == encoding.RelinquishDefault) { + if (*objType == encoding.DateTimeValue || *objType == encoding.TimePatternValue) && (*propID == encoding.PresentValue || *propID == encoding.RelinquishDefault) { decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) } case Time: decodeLen, bv.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+length, int(lenValueType)) case BACnetObjectIdentifier: - if propID == encoding.LastKeyServer || - propID == encoding.ManualSlaveAddressBinding || - propID == encoding.SlaveAddressBinding || - propID == encoding.DeviceAddressBinding { + if *propID == encoding.LastKeyServer || + *propID == encoding.ManualSlaveAddressBinding || + *propID == encoding.SlaveAddressBinding || + *propID == encoding.DeviceAddressBinding { bv.Tag = nil bv.Value = &BACnetAddressBinding{} length-- @@ -311,7 +311,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi length += decodeLen } } else { - switch propID { + switch *propID { case encoding.BacnetIpGlobalAddress, encoding.FdBbmdAddress: bv.Value = &BACnetHostNPort{} length1, err := bv.Value.(*BACnetHostNPort).Decode(buffer, offset+length, apduLen-length) @@ -409,473 +409,102 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi bv.Value = &BACnetShedLevel{} length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) case encoding.LogBuffer: - if objType == encoding.TrendLog { - bv.Value = NewBACnetLogRecord() - length += bv.Value.(*BACnetLogRecord).Decode(buffer, offset+length, apduLen-length) - } else { - log.Println("Unhandled context-specific tag:", bv.Tag) - length = apduLen + switch *objType { + case encoding.TrendLog: + bv.Value = &BACnetLogRecord{} + length += bv.Value.(*BACnetLogRecord).Decode(buffer, offset+length, apduLen-length, nil, nil) + case encoding.EventLog: + bv.Value = &BACnetEventLogRecord{} + length += bv.Value.(*BACnetEventLogRecord).Decode(buffer, offset+length, apduLen-length) } case encoding.DateList: - bv.Value = NewBACnetCalendarEntry() + bv.Value = &BACnetCalendarEntry{} length += bv.Value.(*BACnetCalendarEntry).Decode(buffer, offset+length, apduLen-length) - case encoding.LogBuffer: - if objType == encoding.EventLog { - bv.Value = NewBACnetEventLogRecord() - length += bv.Value.(*BACnetEventLogRecord).Decode(buffer, offset+length, apduLen-length) - } else { - log.Println("Unhandled context-specific tag:", bv.Tag) - length = apduLen - } case encoding.PresentValue: - if objType == encoding.Group { - bv.Value = NewReadAccessResult() + switch *objType { + case encoding.Group: + bv.Value = &ReadAccessResult{} length += bv.Value.(*ReadAccessResult).Decode(buffer, offset+length, apduLen-length) - } else { - log.Println("Unhandled context-specific tag:", bv.Tag) - length = apduLen + case encoding.Channel: + bv.Value = &BACnetChannelValue{} + length += bv.Value.(*BACnetChannelValue).Decode(buffer, offset+length, apduLen-length) + case encoding.GlobalGroup: + bv.Value = &BACnetPropertyAccessResult{} + length += bv.Value.(*BACnetPropertyAccessResult).Decode(buffer, offset+length, apduLen-length) + case encoding.CredentialDataInput: + bv.Value = &BACnetAuthenticationFactor{} + length += bv.Value.(*BACnetAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) } case encoding.NegativeAccessRules, encoding.PositiveAccessRules: - bv.Value = NewBACnetAccessRule() + bv.Value = &BACnetAccessRule{} length += bv.Value.(*BACnetAccessRule).Decode(buffer, offset+length, apduLen-length) case encoding.Tags: - bv.Value = NewBACnetNameValue() + bv.Value = &BACnetNameValue{} length += bv.Value.(*BACnetNameValue).Decode(buffer, offset+length, apduLen-length) case encoding.SubordinateTags: - bv.Value = NewBACnetNameValueCollection() + bv.Value = &BACnetNameValueCollection{} length += bv.Value.(*BACnetNameValueCollection).Decode(buffer, offset+length, apduLen-length) case encoding.NetworkAccessSecurityPolicies: - bv.Value = NewBACnetNetworkSecurityPolicy() + bv.Value = &BACnetNetworkSecurityPolicy{} length += bv.Value.(*BACnetNetworkSecurityPolicy).Decode(buffer, offset+length, apduLen-length) case encoding.PortFilter: - bv.Value = NewBACnetPortPermission() + bv.Value = &BACnetPortPermission{} length += bv.Value.(*BACnetPortPermission).Decode(buffer, offset+length, apduLen-length) case encoding.PriorityArray: - bv.Value = NewBACnetPriorityArray() + bv.Value = &BACnetPriorityArray{} length += bv.Value.(*BACnetPriorityArray).Decode(buffer, offset+length, apduLen-length) case encoding.ProcessIdentifierFilter: - bv.Value = NewBACnetProcessIdSelection() + bv.Value = &BACnetProcessIdSelection{} length += bv.Value.(*BACnetProcessIdSelection).Decode(buffer, offset+length, apduLen-length) - case encoding.GlobalGroup && propID == encoding.PresentValue: - bv.Value = NewBACnetPropertyAccessResult() - length += bv.Value.(*BACnetPropertyAccessResult).Decode(buffer, offset+length, apduLen-length) case encoding.SetpointReference: - bv.Value = NewBACnetSetpointReference() + bv.Value = &BACnetSetpointReference{} length += bv.Value.(*BACnetSetpointReference).Decode(buffer, offset+length, apduLen-length) case encoding.ExceptionSchedule: - bv.Value = NewBACnetSpecialEvent() + bv.Value = &BACnetSpecialEvent{} length += bv.Value.(*BACnetSpecialEvent).Decode(buffer, offset+length, apduLen-length) case encoding.StateChangeValues: - bv.Value = NewBACnetTimerStateChangeValue() + bv.Value = &BACnetTimerStateChangeValue{} length += bv.Value.(*BACnetTimerStateChangeValue).Decode(buffer, offset+length, apduLen-length) case encoding.ValueSource, encoding.ValueSourceArray: - bv.Value = NewBACnetValueSource() + bv.Value = &BACnetValueSource{} length += bv.Value.(*BACnetValueSource).Decode(buffer, offset+length, apduLen-length) case encoding.VirtualMacAddressTable: - bv.Value = NewBACnetVMACEntry() + bv.Value = &BACnetVMACEntry{} length += bv.Value.(*BACnetVMACEntry).Decode(buffer, offset+length, apduLen-length) case encoding.AssignedAccessRights: - bv.Value = NewBACnetAssignedAccessRights() + bv.Value = &BACnetAssignedAccessRights{} length += bv.Value.(*BACnetAssignedAccessRights).Decode(buffer, offset+length, apduLen-length) case encoding.AssignedLandingCalls: - bv.Value = NewBACnetAssignedLandingCalls() + bv.Value = &BACnetAssignedLandingCalls{} length += bv.Value.(*BACnetAssignedLandingCalls).Decode(buffer, offset+length, apduLen-length) - case encoding.AccessEventAuthenticationFactor, - (objType == encoding.CredentialDataInput && propID == encoding.PresentValue): - bv.Value = NewBACnetAuthenticationFactor() + case encoding.AccessEventAuthenticationFactor: + bv.Value = &BACnetAuthenticationFactor{} length += bv.Value.(*BACnetAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) case encoding.SupportedFormats: - bv.Value = NewBACnetAuthenticationFactorFormat() + bv.Value = &BACnetAuthenticationFactorFormat{} length += bv.Value.(*BACnetAuthenticationFactorFormat).Decode(buffer, offset+length, apduLen-length) case encoding.AuthenticationPolicyList: - bv.Value = NewBACnetAuthenticationPolicy() + bv.Value = &BACnetAuthenticationPolicy{} length += bv.Value.(*BACnetAuthenticationPolicy).Decode(buffer, offset+length, apduLen-length) - case encoding.Channel && propID == encoding.PresentValue: - bv.Value = NewBACnetChannelValue() - length += bv.Value.(*BACnetChannelValue).Decode(buffer, offset+length, apduLen-length) case encoding.ActiveCovSubscriptions: - bv.Value = NewBACnetCOVSubscription() + bv.Value = &BACnetCOVSubscription{} length += bv.Value.(*BACnetCOVSubscription).Decode(buffer, offset+length, apduLen-length) case encoding.AuthenticationFactors: - bv.Value = NewBACnetCredentialAuthenticationFactor() + bv.Value = &BACnetCredentialAuthenticationFactor{} length += bv.Value.(*BACnetCredentialAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) case encoding.WeeklySchedule: - bv.Value = NewBACnetDailySchedule() + bv.Value = &BACnetDailySchedule{} length += bv.Value.(*BACnetDailySchedule).Decode(buffer, offset+length, apduLen-length) case encoding.SubscribedRecipients: - bv.Value = NewBACnetEventNotificationSubscription() + bv.Value = &BACnetEventNotificationSubscription{} length += bv.Value.(*BACnetEventNotificationSubscription).Decode(buffer, offset+length, apduLen-length) case encoding.EventParameters: - bv.Value = NewBACnetEventParameter() + bv.Value = &BACnetEventParameter{} length += bv.Value.(*BACnetEventParameter).Decode(buffer, offset+length, apduLen-length) case encoding.FaultParameters: - bv.Value = NewBACnetFaultParameter() + bv.Value = &BACnetFaultParameter{} length += bv.Value.(*BACnetFaultParameter).Decode(buffer, offset+length, apduLen-length) - case encoding.LoggingObject: - bv.Value = NewBACnetLoggingObject() - length += bv.Value.(*BACnetLoggingObject).Decode(buffer, offset+length, apduLen-length) - case encoding.LoggingRecord: - bv.Value = NewBACnetLoggingRecord() - length += bv.Value.(*BACnetLoggingRecord).Decode(buffer, offset+length, apduLen-length) - case encoding.ReinitiateDevice: - bv.Value = NewBACnetReinitializeDevice() - length += bv.Value.(*BACnetReinitializeDevice).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierBACNETIPUDPDATAGRAMCONFIGURATION: - bv.Value = NewBACnetBACnetIPUDPDatagramConfiguration() - length += bv.Value.(*BACnetBACnetIPUDPDatagramConfiguration).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierREINITIALIZEPARAMETERS: - bv.Value = NewBACnetReinitializeParameters() - length += bv.Value.(*BACnetReinitializeParameters).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierDISCONNECTEDONEXCEPTION: - bv.Value = NewBACnetDisconnect() - length += bv.Value.(*BACnetDisconnect).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierNOMINALENCYCLE: - bv.Value = NewBACnetNominalBACnetInterval() - length += bv.Value.(*BACnetNominalBACnetInterval).Decode(buffer, offset+length, apduLen-length) - case encoding.NotifyType: - bv.Value = NewBACnetNotifyType() - length += bv.Value.(*BACnetNotifyType).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierSUMMATION: - bv.Value = NewBACnetEventSummation() - length += bv.Value.(*BACnetEventSummation).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSUSERPASSWORD: - bv.Value = NewBACnetAccessUserPassword() - length += bv.Value.(*BACnetAccessUserPassword).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPULSECONFIGURATION: - bv.Value = NewBACnetPulseConverter() - length += bv.Value.(*BACnetPulseConverter).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPORTPARAMETERS: - bv.Value = NewBACnetPortParameters() - length += bv.Value.(*BACnetPortParameters).Decode(buffer, offset+length, apduLen-length) - case encoding.TimeDelay, - BACnetPropertyIdentifierLIGHTCONTROLDELAY, - BACnetPropertyIdentifierINITIALTIMEDELAY: - bv.Value = NewBACnetTimeValue() - length += bv.Value.(*BACnetTimeValue).Decode(buffer, offset+length, apduLen-length) - case encoding.LightingCommand, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierRADIOFAILUREEVENTS, - BACnetPropertyIdentifierRADIOFAILUREEVENTS2, - BACnetPropertyIdentifierMACADDRESSCHANGEEVENTS: - bv.Value = NewBACnetCOVSubscription() - length += bv.Value.(*BACnetCOVSubscription).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierSHEDLEVELDESCENDANTS: - bv.Value = NewBACnetShedLevelDescendants() - length += bv.Value.(*BACnetShedLevelDescendants).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPERSISTENTDATA: - bv.Value = NewBACnetScheduleObjectPeriod() - length += bv.Value.(*BACnetScheduleObjectPeriod).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierRADIOSTATUS, - BACnetPropertyIdentifierRADIOSTATUS2: - bv.Value = NewBACnetRadioStatus() - length += bv.Value.(*BACnetRadioStatus).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierUSERINFORMATION, - BACnetPropertyIdentifierMACADDRESS: - bv.Value = NewBACnetMACAddress() - length += bv.Value.(*BACnetMACAddress).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierCOVMULTIPLESUBSCRIPTIONSFILTER, - BACnetPropertyIdentifierMONITORMULTIPLEBINARYINPUTS, - BACnetPropertyIdentifierMONITORMULTIPLEBINARYOUTPUTS, - BACnetPropertyIdentifierMULTIPLEMONITORING, - BACnetPropertyIdentifierSTOPWHENFULL, - BACnetPropertyIdentifierLIMITENABLE, - BACnetPropertyIdentifierDATAGROUPS, - BACnetPropertyIdentifierDEVICEADDRESSBINDING: - bv.Value = NewBACnetMultistateValue() - length += bv.Value.(*BACnetMultistateValue).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierLIGHTINGDEVICESTATUSALOGGING, - BACnetPropertyIdentifierLIGHTINGDEVICESTATUSLOGGING, - BACnetPropertyIdentifierDEVICEADDRESSBINDING, - BACnetPropertyIdentifierLIGHTINGDEVICEOPERATIONALMODE, - BACnetPropertyIdentifierLIGHTINGDEVICEOPERATIONALSTATUS, - BACnetPropertyIdentifierLIGHTINGGROUP, - BACnetPropertyIdentifierLIGHTINGGRP, - BACnetPropertyIdentifierLIGHTINGLOGGING: - bv.Value = NewBACnetLightingGroup() - length += bv.Value.(*BACnetLightingGroup).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierEVENTTIMESTAMPS: - bv.Value = NewBACnetTimeStampedValue() - length += bv.Value.(*BACnetTimeStampedValue).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGGROUP: - bv.Value = NewBACnetLightingGroup() - length += bv.Value.(*BACnetLightingGroup).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierNEGATIVEACCESSRULES, - BACnetPropertyIdentifierPOSITIVEACCESSRULES: - bv.Value = NewBACnetAccessRule() - length += bv.Value.(*BACnetAccessRule).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierNETWORKADDRESS, - BACnetPropertyIdentifierMASTERKEY, - encoding.AccessUser, - encoding.AccessZone, - BACnetPropertyIdentifierAREADELIVERING, - BACnetPropertyIdentifierAREARELAY, - BACnetPropertyIdentifierCONTROLLING, - BACnetPropertyIdentifierDELIVERING, - BACnetPropertyIdentifierENROLLING, - BACnetPropertyIdentifierLOWLIMIT, - BACnetPropertyIdentifierMAXIMUMOUTPUT, - BACnetPropertyIdentifierMAXIMUMVALUE, - BACnetPropertyIdentifierMINIMUMOUTPUT, - BACnetPropertyIdentifierMINIMUMVALUE, - BACnetPropertyIdentifierEVENTTIME, - BACnetPropertyIdentifierTIMEDELAY, - BACnetPropertyIdentifierDURATION, - BACnetPropertyIdentifierEXCEPTIONALLIMITS, - BACnetPropertyIdentifierINACTIVESTATE, - BACnetPropertyIdentifierINSTANTANEOUS, - BACnetPropertyIdentifierINVALIDATED, - BACnetPropertyIdentifierLIMITENABLE, - BACnetPropertyIdentifierRELIABILITY, - BACnetPropertyIdentifierSCALE, - BACnetPropertyIdentifierSTEPINCREMENT, - BACnetPropertyIdentifierTIMER, - BACnetPropertyIdentifierLIMIT, - BACnetPropertyIdentifierMAXIMUM, - BACnetPropertyIdentifierMINIMUM, - BACnetPropertyIdentifierPULSESCALING, - BACnetPropertyIdentifierSHUTDOWN, - BACnetPropertyIdentifierBUFFERPROPERTY, - BACnetPropertyIdentifierSTREAMINGTHRESHOLDS: - bv.Value = NewBACnetOctetString() - length += bv.Value.(*BACnetOctetString).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierCOVMULTIPLESUBSCRIPTIONSFILTER, - encoding.MultiStateValue: - bv.Value = NewBACnetMultistateValue() - length += bv.Value.(*BACnetMultistateValue).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierAUTHENTICATIONFACTORSUPPLIED, - BACnetPropertyIdentifierAUTHENTICATIONFACTORFORMAT, - BACnetPropertyIdentifierAUTHENTICATIONPOLICYNAME, - BACnetPropertyIdentifierAUTHENTICATIONPOLICYNAMES, - BACnetPropertyIdentifierPOLICYNAMES: - bv.Value = NewBACnetAuthorization() - length += bv.Value.(*BACnetAuthorization).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierDEVICEIDENTIFICATION: - bv.Value = NewBACnetCharacterString() - length += bv.Value.(*BACnetCharacterString).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierEVENTTIMESTAMPS: - bv.Value = NewBACnetTimeStampedValue() - length += bv.Value.(*BACnetTimeStampedValue).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierDEVICEIDENTIFICATION: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierSHEDLEVELS: - bv.Value = NewBACnetShedLevel() - length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetPortFilter() - length += bv.Value.(*BACnetPortFilter).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPOLICY: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSRULES, - BACnetPropertyIdentifierINVALIDACTIONS, - BACnetPropertyIdentifierLOGGINGOBJECT, - BACnetPropertyIdentifierSAVERESTORESTATE: - bv.Value = NewBACnetDeviceObjectReference() - length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACTIVESEQUENCE, - BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPORTFILTER, - BACnetPropertyIdentifierAUTHENTICATIONPOLICIES, - BACnetPropertyIdentifierAUTHENTICATIONPOLICYLIST: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPOLICYPASSWORD, - BACnetPropertyIdentifierPOLICYTYPE, - BACnetPropertyIdentifierTIMEDELAY, - BACnetPropertyIdentifierVERIFYPASSWORD, - BACnetPropertyIdentifierPORTPARAMETERS, - BACnetPropertyIdentifierDEVICEADDRESSBINDING: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierRELINQUISHDEFAULT, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSRULES, - BACnetPropertyIdentifierINVALIDACTIONS, - BACnetPropertyIdentifierLOGGINGOBJECT, - BACnetPropertyIdentifierSAVERESTORESTATE: - bv.Value = NewBACnetDeviceObjectReference() - length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSSEQUENCE, - BACnetPropertyIdentifierACTIVESEQUENCE, - BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, - BACnetPropertyIdentifierFEEDINGPIPEINDEX, - BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE, - BACnetPropertyIdentifierSTATUSFLAGS: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICY, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPOLICYNAMES, - BACnetPropertyIdentifierACCESSRULES, - BACnetPropertyIdentifierLIMITDISABLE, - BACnetPropertyIdentifierMANUALSLIDERSETTINGS: - bv.Value = NewBACnetDeviceObjectReference() - length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSRULES, - BACnetPropertyIdentifierINVALIDACTIONS, - BACnetPropertyIdentifierLOGGINGOBJECT, - BACnetPropertyIdentifierSAVERESTORESTATE: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPOLICYPASSWORD, - BACnetPropertyIdentifierPOLICYTYPE, - BACnetPropertyIdentifierTIMEDELAY, - BACnetPropertyIdentifierVERIFYPASSWORD: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, - BACnetPropertyIdentifierEXCEPTIONSCHEDULE, - BACnetPropertyIdentifierEVENTTIMESTAMPS: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierALARMVALUES: - bv.Value = NewBACnetDeviceObjectReference() - length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierALARMVALUES, - BACnetPropertyIdentifierEVENTTYPE, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierSAFETYPOINTS: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCONTROLMODE: - bv.Value = NewBACnetLightingControlMode() - length += bv.Value.(*BACnetLightingControlMode).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierBACNETIPADDRESS, - BACnetPropertyIdentifierDEFAULTGATEWAY, - BACnetPropertyIdentifierIPADDRESS, - BACnetPropertyIdentifierSUBNETMASK: - bv.Value = NewBACnetIPAddress() - length += bv.Value.(*BACnetIPAddress).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetPortFilter() - length += bv.Value.(*BACnetPortFilter).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSRULES: - bv.Value = NewBACnetDeviceObjectReference() - length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierRELIABILITY, - BACnetPropertyIdentifierUNITS: - bv.Value = NewBACnetReliability() - length += bv.Value.(*BACnetReliability).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPARAMETERSETUSAGE: - bv.Value = NewBACnetParameterSetUsage() - length += bv.Value.(*BACnetParameterSetUsage).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierCONTROLSEQUENCEOFOPERATION, - BACnetPropertyIdentifierEXCEPTIONSCHEDULE, - BACnetPropertyIdentifierEXCEPTIONSCHEDULEDEFAULT, - BACnetPropertyIdentifierLIGHTINGCOMMAND: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGINGDEFAULT, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierEVENTCODE, - BACnetPropertyIdentifierLONMARKRESOURCE, - BACnetPropertyIdentifierPOLICIES: - bv.Value = NewBACnetUnsignedInteger() - length += bv.Value.(*BACnetUnsignedInteger).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCOMMAND: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierLIGHTINGCONTROLOVERLOADALOGGING, - BACnetPropertyIdentifierRELOCATELIGHTINGOUTPUTDEVICE: - bv.Value = NewBACnetLightingCommand() - length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierSAFETYPOINTS: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPOLICYPASSWORD, - BACnetPropertyIdentifierPOLICYTYPE, - BACnetPropertyIdentifierTIMEDELAY, - BACnetPropertyIdentifierVERIFYPASSWORD: - bv.Value = NewBACnetDeviceObjectPropertyReference() - length += bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICY, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierDEVICEIDENTIFICATION: - bv.Value = NewBACnetCharacterString() - length += bv.Value.(*BACnetCharacterString).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICY, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case encoding.AccessEvent, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) - case BACnetPropertyIdentifierACCESSEVENTS, - BACnetPropertyIdentifierPOLICIES, - BACnetPropertyIdentifierPORTFILTER: - bv.Value = NewBACnetDeviceSecurityPolicy() - length += bv.Value.(*BACnetDeviceSecurityPolicy).Decode(buffer, offset+length, apduLen-length) default: bv.Value = nil } @@ -885,7 +514,7 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType encodi func (bv *BACnetValue) Encode() []byte { if bv.Tag == nil { - // Handle NULL case + return nil } else { switch *bv.Tag { case Boolean: @@ -903,28 +532,24 @@ func (bv *BACnetValue) Encode() []byte { case CharacterString: return encoding.EncodeApplicationCharacterString(bv.Value.(string)) case BitString: - return encoding.EncodeApplicationBitString(b) - // Handle BIT_STRING case - // Add your code here... + return encoding.EncodeApplicationBitString(bv.Value) case Enumerated: - // Handle ENUMERATED case - // Add your code here... + return encoding.EncodeApplicationEnumerated(bv.Value.(uint32)) case Date: - // Handle DATE case - // Add your code here... + return encoding.EncodeApplicationDate(bv.Value.(time.Time)) case Time: - // Handle TIME case - // Add your code here... + return encoding.EncodeApplicationTime(bv.Value.(time.Time)) case BACnetObjectIdentifier: - // Handle BACNETOBJECTIDENTIFIER case - // Add your code here... + return bv.Value.(*ObjectIdentifier).EncodeApp() default: + switch bv.Value.(type) { + case int: + return encoding.EncodeApplicationEnumerated(uint32(bv.Value.(int))) + } log.Printf("Unsupported BACnetApplicationTag: %v", bv.Tag) - // Handle other BACnetApplicationTags as needed... + return nil } } - - return nil } // BACnetRouterEntryStatus is an enumeration for the status of BACnetRouterEntry. @@ -1120,10 +745,6 @@ func NewBACnetDaysOfWeek() *BACnetDaysOfWeek { } } -func (d *BACnetDaysOfWeek) String() string { - return fmt.Sprintf("%08b", d.bitString.Value) -} - func (d *BACnetDaysOfWeek) Decode(buffer []byte, offset int, apduLen int) int { d.bitString = BACnetBitString{} return d.bitString.Decode(buffer, offset, apduLen) @@ -2093,7 +1714,7 @@ func (bac *BACnetActionCommand) Decode(buffer []byte, offset int, apduLen int) i for !encoding.IsClosingTagNumber(buffer, offset+leng, 4) && leng < apduLen { bv := BACnetValue{} propID := bac.PropertyIdentifier.(encoding.PropertyIdentifier) - leng1, _ := bv.Decode(buffer, offset+leng, apduLen-leng, bac.ObjectIdentifier.Type, propID) + leng1, _ := bv.Decode(buffer, offset+leng, apduLen-leng, &bac.ObjectIdentifier.Type, &propID) if leng1 < 0 { return -1 } @@ -2153,3 +1774,987 @@ func (bac *BACnetActionCommand) Decode(buffer []byte, offset int, apduLen int) i return leng } + +type BACnetScale struct { + Value interface{} +} + +func (bs *BACnetScale) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + if encoding.IsContextTag(buffer, offset+leng, 0) { + // float-scale + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bs.Value = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else if encoding.IsContextTag(buffer, offset+leng, 1) { + // integer-scale + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bs.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + return leng +} + +type BACnetLightingCommand struct { + Operation BACnetLightingOperation + TargetLevel float32 + RampRate float32 + StepIncrement float32 + FadeTime uint32 + Priority uint32 +} + +type BACnetLightingOperation uint32 + +const ( + LightingOperationUnknown BACnetLightingOperation = iota + LightingOperationOff + LightingOperationOn + LightingOperationToggle + LightingOperationDecrement + LightingOperationIncrement +) + +func (blc *BACnetLightingCommand) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // operation + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, uVal := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + blc.Operation = BACnetLightingOperation(uVal) + } else { + return -1 + } + + if leng < apduLen { + // target-level + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, blc.TargetLevel = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // ramp-rate + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, blc.RampRate = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // step-increment + if encoding.IsContextTag(buffer, offset+leng, 3) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, blc.StepIncrement = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // fade-time + if encoding.IsContextTag(buffer, offset+leng, 4) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, blc.FadeTime = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + if leng < apduLen { + // priority + if encoding.IsContextTag(buffer, offset+leng, 5) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, blc.Priority = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + return leng +} + +type BACnetPrescale struct { + Multiplier uint32 + ModuloDivide uint32 +} + +func (bp *BACnetPrescale) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + // multiplier + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bp.Multiplier = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + // modulo_divide + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bp.ModuloDivide = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + return leng +} + +type BACnetShedLevelChoice int + +const ( + BACnetShedLevelChoicePercent BACnetShedLevelChoice = iota + BACnetShedLevelChoiceLevel + BACnetShedLevelChoiceAmount +) + +type BACnetShedLevel struct { + Choice BACnetShedLevelChoice + Value interface{} +} + +func (bsl *BACnetShedLevel) Decode(buffer []byte, offset int, apduLen int) int { + leng := 0 + + if encoding.IsContextTag(buffer, offset+leng, 0) { + // percent + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bsl.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bsl.Choice = BACnetShedLevelChoicePercent + } else if encoding.IsContextTag(buffer, offset+leng, 1) { + // level + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bsl.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bsl.Choice = BACnetShedLevelChoiceLevel + } else if encoding.IsContextTag(buffer, offset+leng, 2) { + // amount + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, bsl.Value = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + bsl.Choice = BACnetShedLevelChoiceAmount + } else { + return -1 + } + + return leng +} + +type BACnetLogRecordChoice int + +const ( + BACnetLogRecordChoiceLogStatus BACnetLogRecordChoice = iota + BACnetLogRecordChoiceBooleanValue + BACnetLogRecordChoiceRealValue + BACnetLogRecordChoiceEnumeratedValue + BACnetLogRecordChoiceUnsignedValue + BACnetLogRecordChoiceIntegerValue + BACnetLogRecordChoiceBitstringValue + BACnetLogRecordChoiceNullValue + BACnetLogRecordChoiceFailure + BACnetLogRecordChoiceTimeChange + BACnetLogRecordChoiceAnyValue +) + +type BACnetLogRecord struct { + Timestamp BACnetTimeStamp + LogDatum interface{} + StatusFlags BACnetStatusFlags +} + +func (blr *BACnetLogRecord) Decode(buffer []byte, offset, apduLen int, objType *encoding.ObjectType, propID *encoding.PropertyIdentifier) int { + leng := 0 + + // timestamp + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + blr.Timestamp = BACnetTimeStamp{} + leng1, err := blr.Timestamp.Decode(buffer, offset+leng, int(lenValue)) + if err != nil { + return -1 + } + leng += leng1 + } else { + return -1 + } + + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + + switch BACnetLogRecordChoice(tagNumber) { + case BACnetLogRecordChoiceLogStatus: + blr.LogDatum = &BACnetLogStatus{} + leng += blr.LogDatum.(*BACnetLogStatus).Decode(buffer, offset+leng, int(lenValue)) + case BACnetLogRecordChoiceBooleanValue: + blr.LogDatum = buffer[offset+leng] > 0 + leng++ + case BACnetLogRecordChoiceRealValue: + leng1, logValue := encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + blr.LogDatum = logValue + case BACnetLogRecordChoiceEnumeratedValue: + leng1, logValue := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) + leng += leng1 + blr.LogDatum = logValue + case BACnetLogRecordChoiceUnsignedValue: + leng1, logValue := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + blr.LogDatum = logValue + case BACnetLogRecordChoiceIntegerValue: + leng1, logValue := encoding.DecodeSigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + blr.LogDatum = logValue + case BACnetLogRecordChoiceBitstringValue: + blr.LogDatum = &BACnetBitString{} + leng += blr.LogDatum.(*BACnetBitString).Decode(buffer, offset+leng, int(lenValue)) + case BACnetLogRecordChoiceNullValue: + blr.LogDatum = nil + leng++ + case BACnetLogRecordChoiceFailure: + blr.LogDatum = &BACnetError{} + leng += blr.LogDatum.(*BACnetError).Decode(buffer, offset+leng, apduLen-leng) + if encoding.IsClosingTagNumber(buffer, offset+leng, byte(BACnetLogRecordChoiceFailure)) { + leng++ + } else { + return -1 + } + case BACnetLogRecordChoiceTimeChange: + leng1, logValue := encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + blr.LogDatum = logValue + case BACnetLogRecordChoiceAnyValue: + blr.LogDatum = []BACnetValue{} + for !encoding.IsClosingTagNumber(buffer, offset+leng, byte(BACnetLogRecordChoiceAnyValue)) && leng < apduLen { + bValue := BACnetValue{} + leng1, _ := bValue.Decode(buffer, offset+leng, apduLen-leng, objType, propID) + if leng1 < 0 { + return -1 + } + leng += leng1 + blr.LogDatum = append(blr.LogDatum.([]BACnetValue), bValue) + } + if encoding.IsClosingTagNumber(buffer, offset+leng, byte(BACnetLogRecordChoiceAnyValue)) { + leng++ + } else { + return -1 + } + default: + return -1 + } + } else { + return -1 + } + + if leng < apduLen { + // status-flags optional + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + blr.StatusFlags = BACnetStatusFlags{} + leng += blr.StatusFlags.Decode(buffer, offset+leng, int(lenValue)) + } + } + + return leng +} + +type BACnetLogStatus struct { + UnusedBits uint8 + BitString *BACnetBitString +} + +func NewBACnetLogStatus() BACnetLogStatus { + return BACnetLogStatus{ + UnusedBits: 5, + BitString: NewBACnetBitString(5, *internal.NewBitArrayFromByte(0x00)), + } +} + +func (bls *BACnetLogStatus) Decode(buffer []byte, offset, apduLen int) int { + bls.BitString = NewBACnetBitString(5, *internal.NewBitArrayFromByte(0x00)) + return bls.BitString.Decode(buffer, offset, apduLen) +} + +func (bls *BACnetLogStatus) SetLogDisabled(a bool) { + bls.BitString.Value.Set(0, a) +} + +func (bls *BACnetLogStatus) SetBufferPurged(a bool) { + bls.BitString.Value.Set(1, a) +} + +func (bls *BACnetLogStatus) SetLogInterrupted(a bool) { + bls.BitString.Value.Set(2, a) +} + +func (bls *BACnetLogStatus) LogDisabled() bool { + return bls.BitString.Value.Get(0) +} + +func (bls *BACnetLogStatus) BufferPurged() bool { + return bls.BitString.Value.Get(1) +} + +func (bls *BACnetLogStatus) LogInterrupted() bool { + return bls.BitString.Value.Get(2) +} + +type BACnetError struct { + ErrorClass ErrorClassEnum + ErrorCode ErrorCodeEnum +} + +func (be *BACnetError) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // Decode error_class + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(Enumerated) { + leng1, eVal := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) + leng += leng1 + be.ErrorClass = ErrorClassEnum(eVal.(uint32)) + } else { + return -1 + } + + // Decode error_code + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(Enumerated) { + leng1, eVal := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) + leng += leng1 + be.ErrorCode = ErrorCodeEnum(eVal.(uint32)) + } else { + return -1 + } + + return leng +} + +type BACnetCalendarEntry struct { + Value interface{} +} + +func (ce *BACnetCalendarEntry) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == 0 { + leng1, ce.Value = encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else if tagNumber == 1 { + ce.Value = &BACnetDateRange{} + leng1, err := ce.Value.(*BACnetDateRange).Decode(buffer, offset+leng, int(lenValue)) + if err != nil { + return -1 + } + leng += leng1 + } else if tagNumber == 2 { + ce.Value = &BACnetWeekNDay{} + leng += ce.Value.(*BACnetWeekNDay).Decode(buffer, offset+leng, int(lenValue)) + } else { + return -1 + } + + return leng +} + +type BACnetEventLogRecord struct { + Timestamp DateTime + LogDatum interface{} +} + +func (elr *BACnetEventLogRecord) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, _ := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + elr.Timestamp = DateTime{} + leng += elr.Timestamp.Decode(buffer, offset+leng) + } else { + return -1 + } + + return leng +} + +type BACnetWeekNDay struct { + Month int + WeekOfMonth int + DayOfWeek int +} + +func (wnd *BACnetWeekNDay) Decode(buffer []byte, offset, apduLen int) int { + if apduLen >= 3 { + wnd.Month = int(buffer[offset]) + wnd.WeekOfMonth = int(buffer[offset+1]) + wnd.DayOfWeek = int(buffer[offset+2]) + } else { + return -1 + } + return 3 +} + +type ReadAccessResultReadResult struct { + PropertyIdentifier encoding.PropertyIdentifier + PropertyArrayIndex uint32 + ReadResult interface{} // Either BACnetValue or BACnetError +} + +func (rarr *ReadAccessResultReadResult) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + // 2 propertyidentifier + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, propertyIdentifier := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + rarr.PropertyIdentifier = encoding.PropertyIdentifier(propertyIdentifier.(uint32)) + } else { + return -1 + } + + // 3 property_array_index + if encoding.IsContextTag(buffer, offset+leng, 3) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, propertyArrayIndex := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + rarr.PropertyArrayIndex = propertyArrayIndex + } + + if leng < apduLen { + if encoding.IsOpeningTagNumber(buffer, offset+leng, 4) { + rarr.ReadResult = &BACnetValue{} + leng1, err := rarr.ReadResult.(*BACnetValue).Decode(buffer, offset+leng, apduLen-leng, nil, nil) + if err != nil { + return -1 + } + leng += leng1 + if encoding.IsClosingTagNumber(buffer, offset+leng, 4) { + leng += 1 + } else { + return -1 + } + } else if encoding.IsOpeningTagNumber(buffer, offset+leng, 5) { + rarr.ReadResult = &BACnetError{} + leng += rarr.ReadResult.(*BACnetError).Decode(buffer, offset+leng, apduLen-leng) + if encoding.IsClosingTagNumber(buffer, offset+leng, 5) { + leng += 1 + } else { + return -1 + } + } + } + return leng +} + +type ReadAccessResult struct { + ObjectIdentifier ObjectIdentifier + ListOfResults []ReadAccessResultReadResult +} + +func (rar *ReadAccessResult) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + // tag 0 objectidentifier + rar.ObjectIdentifier = ObjectIdentifier{} + if encoding.IsClosingTagNumber(buffer, offset+leng, 0) { + leng += rar.ObjectIdentifier.DecodeContext(buffer, offset+leng, apduLen-leng, 0) + } else { + return -1 + } + + if encoding.IsOpeningTagNumber(buffer, offset+leng, 1) { + leng += 1 + rar.ListOfResults = make([]ReadAccessResultReadResult, 0) + + for (apduLen-leng) > 1 && !encoding.IsClosingTagNumber(buffer, offset+leng, 1) { + bValue := ReadAccessResultReadResult{} + leng += bValue.Decode(buffer, offset+leng, apduLen-leng) + + rar.ListOfResults = append(rar.ListOfResults, bValue) + } + + if encoding.IsClosingTagNumber(buffer, offset+leng, 1) { + leng += 1 + } else { + return -1 + } + } else { + return -1 + } + + return leng +} + +type BACnetAccessRule struct { + TimeRangeSpecifier BACnetTimeRangeSpecifierChoice + TimeRange BACnetDeviceObjectPropertyReference + LocationSpecifier BACnetLocationSpecifierChoice + Location BACnetDeviceObjectReference + Enable bool +} + +type BACnetTimeRangeSpecifierChoice int + +const ( + Specified BACnetTimeRangeSpecifierChoice = iota + Always +) + +type BACnetLocationSpecifierChoice int + +const ( + SpecifiedLocation BACnetLocationSpecifierChoice = iota + All +) + +func (bar *BACnetAccessRule) Decode(buffer []byte, offset, apduLen int) int { + return -1 +} + +type BACnetNameValue struct { + Name string + Value BACnetValue +} + +func (bnv *BACnetNameValue) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // Name + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != 0 { + return -1 + } + leng += leng1 + leng1, bnv.Name = encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + leng += leng1 + + // Decode value + decodeLen := 0 + if leng < apduLen { + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + + switch ApplicationTags(tagNumber) { + case Null: + bnv.Value = BACnetValue{Value: nil} + decodeLen = 0 + // Fixme: fix null type nothing else to do, some Error occurs!!!! + case Boolean: + if lenValue > 0 { + bnv.Value = BACnetValue{Value: true} + } else { + bnv.Value = BACnetValue{Value: false} + } + case UnsignedInt: + decodeLen, bnv.Value.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + case SignedInt: + decodeLen, bnv.Value.Value = encoding.DecodeSigned(buffer, offset+leng, int(lenValue)) + case Real: + decodeLen, bnv.Value.Value = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + case Double: + decodeLen, bnv.Value.Value = encoding.DecodeDoubleSafe(buffer, offset+leng, int(lenValue)) + case OctetString: + decodeLen, bnv.Value.Value = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + case CharacterString: + decodeLen, bnv.Value.Value = encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + case BitString: + bitValue := BACnetBitString{} + decodeLen = bitValue.Decode(buffer, offset+leng, int(lenValue)) + bnv.Value.Value = bitValue + case Enumerated: + decodeLen, bnv.Value.Value = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) + case Date: + decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) + + if leng < apduLen { + leng1, tagNumber, _ := encoding.DecodeTagNumberAndValue(buffer, offset+leng+decodeLen) + if tagNumber == byte(Time) { + leng += leng1 + leng-- + bnv.Value.Value = &DateTime{} + decodeLen = bnv.Value.Value.(*DateTime).Decode(buffer, offset+leng) + } + } else { + bnv.Value.Value = dateValue + } + case Time: + decodeLen, bnv.Value.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+leng, int(lenValue)) + } + + if decodeLen < 0 { + return -1 + } + leng += decodeLen + } + + return leng +} + +type BACnetNameValueCollection struct { + Members []BACnetNameValue +} + +func (bnc *BACnetNameValueCollection) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // Check if it's an opening tag number + if !encoding.IsOpeningTagNumber(buffer, offset+leng, 0) { + return -1 + } + + leng += 1 + bnc.Members = make([]BACnetNameValue, 0) + + for !encoding.IsClosingTagNumber(buffer, offset+leng, 0) { + bValue := BACnetNameValue{} + leng1 := bValue.Decode(buffer, offset+leng, apduLen-leng) + if leng1 < 0 { + return -1 + } + leng += leng1 + bnc.Members = append(bnc.Members, bValue) + } + + leng += 1 + return leng +} + +type BACnetSecurityPolicy int + +type BACnetNetworkSecurityPolicy struct { + PortID int + SecurityLevel BACnetSecurityPolicy +} + +func (bns *BACnetNetworkSecurityPolicy) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // port_id + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != 0 { + return -1 + } + leng += leng1 + leng1, portID := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bns.PortID = int(portID) + + leng = 0 + // security_level + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != 1 { + return -1 + } + leng += leng1 + leng1, uVal := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bns.SecurityLevel = BACnetSecurityPolicy(uVal) + + return leng +} + +type BACnetPortPermission struct { + PortID int + Enabled bool +} + +func (bpp *BACnetPortPermission) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + + // port_id + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != 0 { + return -1 + } + leng += leng1 + leng1, portID := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + bpp.PortID = int(portID) + + leng = 0 + // enabled + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + if tagNumber != 1 { + return -1 + } + leng += leng1 + if lenValue > 0 { + bpp.Enabled = true + } else { + bpp.Enabled = false + } + + return leng +} + +type BACnetPriorityValue struct { + Value interface{} +} + +func (bpv *BACnetPriorityValue) Decode(buffer []byte, offset, apduLen int) int { + // TODO Implement decoding logic for BACnetPriorityValue + return -1 +} + +type BACnetPriorityArray struct { + Value [16]BACnetPriorityValue +} + +func (bpa *BACnetPriorityArray) Decode(buffer []byte, offset, apduLen int) int { + leng := 0 + i := 0 + + for leng < apduLen && i < 16 { + bpa.Value[i] = BACnetPriorityValue{} + leng += bpa.Value[i].Decode(buffer, offset+leng, apduLen-leng) + i++ + } + + return leng +} + +type BACnetProcessIdSelection struct { + Value interface{} // You can specify the type you expect here +} + +func (bps *BACnetProcessIdSelection) Decode(buffer []byte, offset, apduLen int) int { + // TODO Implement decoding logic for BACnetProcessIdSelection + return -1 +} + +type BACnetPropertyAccessResult struct { + ObjectIdentifier ObjectIdentifier + PropertyIdentifier encoding.PropertyIdentifier + PropertyArrayIndex int + DeviceIdentifier ObjectIdentifier + AccessResult interface{} +} + +func (bpar *BACnetPropertyAccessResult) Decode(buffer []byte, offset, apduLen int) int { + // TODO Implement decoding logic for BACnetPropertyAccessResult here + return -1 +} + +type BACnetSetpointReference struct { + Value interface{} +} + +func (bsr *BACnetSetpointReference) Decode(buffer []byte, offset, apduLen int) int { + // TODO Implement decoding logic for BACnetSetpointReference here + return 0 +} + +type BACnetSpecialEvent struct { + Period interface{} + ListOfTimeValues []interface{} + EventPriority int +} + +func (bse *BACnetSpecialEvent) Decode(buffer []byte, offset, apduLen int) int { + // TODO Implement decoding logic for BACnetSpecialEvent here + return 0 +} + +type BACnetTimerStateChangeValue struct { + Value interface{} +} + +func (scv *BACnetTimerStateChangeValue) Decode([]byte, int, int) int { + // TODO implement decoder + return -1 +} + +type BACnetValueSource struct { + Value interface{} +} + +func (scv *BACnetValueSource) Decode([]byte, int, int) int { + // TODO implement decoder + return -1 +} + +type BACnetVMACEntry struct { + VirtualMacAddress interface{} + NativeMacAddress interface{} +} + +func (scv *BACnetVMACEntry) Decode([]byte, int, int) int { + // TODO implement decoder + return -1 +} + +type BACnetAssignedAccessRights struct { + AssignedAccessRights BACnetDeviceObjectReference + Enable bool +} + +func (scv *BACnetAssignedAccessRights) Decode([]byte, int, int) int { + // TODO implement decoder + return -1 +} + +type BACnetAssignedLandingCalls struct { + LandingCalls []landingCall +} + +type landingCall struct { + FloorNumber int + Direction BACnetLiftCarDirection +} + +func (balc *BACnetAssignedLandingCalls) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetAssignedLandingCalls + return 0 +} + +type BACnetLiftCarDirection int + +type BACnetAuthenticationFactor struct { + FormatType BACnetAuthenticationFactorType + FormatClass int + Value []byte +} + +func (baf *BACnetAuthenticationFactor) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetAuthenticationFactor + return 0 +} + +type BACnetAuthenticationFactorType int + +type BACnetAuthenticationFactorFormat struct { + FormatType BACnetAuthenticationFactorType + VendorID int + VendorFormat int +} + +func (baff *BACnetAuthenticationFactorFormat) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetAuthenticationFactorFormat + return 0 +} + +type BACnetAuthenticationPolicy struct { + Policies []policy + OrderEnforced bool + Timeout int +} + +type policy struct { + CredentialDataInput BACnetDeviceObjectReference + Index int +} + +func (bap *BACnetAuthenticationPolicy) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetAuthenticationPolicy + return 0 +} + +type BACnetBDTEntry struct { + // Define BACnetBDTEntry structure here +} + +type BACnetChannelValue struct { + Value interface{} +} + +func (bcv *BACnetChannelValue) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetChannelValue + return 0 +} + +type BACnetCOVSubscription struct { + Recipient BACnetRecipientProcess + MonitoredPropertyReference BACnetObjectPropertyReference + IssueConfirmedNotifications bool + TimeRemaining int + COVIncrement float64 +} + +func (bcs *BACnetCOVSubscription) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetCOVSubscription + return 0 +} + +type BACnetAccessAuthenticationFactorDisable int + +type BACnetCredentialAuthenticationFactor struct { + Disable BACnetAccessAuthenticationFactorDisable + AuthenticationFactor BACnetAuthenticationFactor +} + +func (bcaf *BACnetCredentialAuthenticationFactor) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetCredentialAuthenticationFactor + return 0 +} + +type BACnetDailySchedule struct { + DaySchedule []interface{} +} + +func (bds *BACnetDailySchedule) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetDailySchedule + return 0 +} + +type BACnetRecipientProcess struct { + // Define BACnetRecipientProcess structure here +} + +type BACnetEventNotificationSubscription struct { + Recipient BACnetRecipient + ProcessIdentifier int + IssueConfirmedNotifications bool + TimeRemaining int +} + +func (bens *BACnetEventNotificationSubscription) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetEventNotificationSubscription + return 0 +} + +type BACnetEventParameter struct { + Value interface{} +} + +func (bep *BACnetEventParameter) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetEventParameter + return 0 +} + +type BACnetFaultParameter struct { + Value interface{} +} + +func (bfp *BACnetFaultParameter) Decode(buffer []byte, offset, apduLen int) int { + // Implement decoding logic for BACnetFaultParameter + return 0 +} diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go index b537c83..d2f84ed 100644 --- a/pkg/encoding/encoding.go +++ b/pkg/encoding/encoding.go @@ -139,3 +139,8 @@ func encodeBACnetDouble(value float64) []byte { binary.BigEndian.PutUint64(buf, math.Float64bits(value)) return buf } + +func EncodeApplicationBitString(val interface{}) []byte { + // TODO + return nil +} From 3eb993e901de535e73d86465bd2c12c6cdc00fc1 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Fri, 15 Sep 2023 22:52:11 +0300 Subject: [PATCH 20/25] Fix issue with unnecessary print statements and add BACnet header decoding The commit fixes the issue where unnecessary print statements were present in the code. Additionally, it adds the decoding of the BACnet message in the response of the read property example. Signed-off-by: SammyOina --- example/readProperty/readProperty.go | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go index 73c31d9..fcbf4da 100644 --- a/example/readProperty/readProperty.go +++ b/example/readProperty/readProperty.go @@ -48,11 +48,6 @@ func main() { blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) message := append(blvcBytes, mes...) - fmt.Println(message) - - message = []byte{129, 10, 0, 17, 1, 4, 0, 5, 1, 12, 12, 0, 0, 0, 10, 25, 85} - fmt.Println(message) - // Define the BACnet broadcast address (255.255.255.255:47808) remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") if err != nil { @@ -99,7 +94,23 @@ func main() { // Process the response (you'll need to parse BACnet responses here) response := buffer[:n] - log.Printf("Received response: %X\n", response) - + blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} + headerLength, function, msgLength, err := blvc.Decode(response, 0) + if err != nil { + log.Fatal(err) + } + fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) + fmt.Println("blvc", blvc) + npdu := bacnet.NPDU{Version: 1} + npduLen := npdu.Decode(response, headerLength) + fmt.Println("npdu", npdu) + apdu := bacnet.APDU{} + apduLen := apdu.Decode(response, headerLength+npduLen) + fmt.Println("apdu", apdu) + readPropACK := bacnet.ReadPropertyACK{} + if _, err = readPropACK.Decode(response, headerLength+npduLen+apduLen-2, len(response)); err != nil { + log.Fatal(err) + } + fmt.Println("readprop", readPropACK) } } From 62e09a31686dbf63d5641be14ef0edb6730cabff Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 18 Sep 2023 14:12:18 +0300 Subject: [PATCH 21/25] Fix incorrect comparison in IAM.go The commit fixes an incorrect comparison in the IAM.go file. The tagNumber variable is now correctly compared to the BACnetObjectIdentifier constant. Also adds write property. Signed-off-by: SammyOina --- example/readProperty/readProperty.go | 4 - pkg/bacnet/iam.go | 20 ++-- pkg/bacnet/network.go | 25 +---- pkg/bacnet/{property.go => readProperty.go} | 100 +++++++++--------- pkg/bacnet/writeProperty.go | 110 ++++++++++++++++++++ pkg/encoding/encoding.go | 33 ++++++ 6 files changed, 205 insertions(+), 87 deletions(-) rename pkg/bacnet/{property.go => readProperty.go} (98%) create mode 100644 pkg/bacnet/writeProperty.go diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go index fcbf4da..7720952 100644 --- a/example/readProperty/readProperty.go +++ b/example/readProperty/readProperty.go @@ -13,10 +13,6 @@ import ( func main() { netType := encoding.IPV4 - /*deviceID := bacnet.ObjectIdentifier{ - Type: encoding.AnalogInput, - Instance: 101, - }*/ destination := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809", &netType) npdu := bacnet.NewNPDU(destination, nil, nil, nil) npdu.Control.SetDataExpectingReply(true) diff --git a/pkg/bacnet/iam.go b/pkg/bacnet/iam.go index 551c63b..6036991 100644 --- a/pkg/bacnet/iam.go +++ b/pkg/bacnet/iam.go @@ -21,7 +21,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber != byte(BACnetObjectIdentifier) { + if tagNumber != byte(encoding.BACnetObjectIdentifier) { fmt.Println("tag num object id", tagNumber) return -1, errors.New("Invalid tag number") } @@ -35,7 +35,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { // MAX APDU - unsigned leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber != byte(UnsignedInt) { + if tagNumber != byte(encoding.UnsignedInt) { fmt.Println("tag num max apdu", tagNumber) return -1, errors.New("Invalid tag number") } @@ -47,7 +47,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { // Segmentation - enumerated leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber != byte(Enumerated) { + if tagNumber != byte(encoding.Enumerated) { fmt.Println("tag num segmentation", tagNumber) return -1, errors.New("Invalid tag number") } @@ -60,7 +60,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber != byte(UnsignedInt) { + if tagNumber != byte(encoding.UnsignedInt) { fmt.Println("tag num vendor ID", tagNumber) return -1, errors.New("Invalid tag number") } @@ -79,7 +79,7 @@ func (iam *IAmRequest) Decode(buffer []byte, offset int) (int, error) { func (iam IAmRequest) Encode() []byte { tmp := iam.IamDeviceIdentifier.Encode() propID := iam.SegmentationSupported.(encoding.PropertyIdentifier) - return append(append(append(append(encoding.EncodeTag(encoding.BACnetApplicationTag(BACnetObjectIdentifier), false, len(tmp)), tmp...), encoding.EncodeApplicationUnsigned(iam.MaxAPDULengthAccepted)...), encoding.EncodeApplicationEnumerated(uint32(propID))...), encoding.EncodeApplicationUnsigned(iam.VendorID)...) + return append(append(append(append(encoding.EncodeTag(encoding.BACnetApplicationTag(encoding.BACnetObjectIdentifier), false, len(tmp)), tmp...), encoding.EncodeApplicationUnsigned(iam.MaxAPDULengthAccepted)...), encoding.EncodeApplicationEnumerated(uint32(propID))...), encoding.EncodeApplicationUnsigned(iam.VendorID)...) } type YouAreRequest struct { @@ -95,7 +95,7 @@ func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber == byte(UnsignedInt) { + if tagNumber == byte(encoding.UnsignedInt) { leng1, decodedValue := encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) leng += leng1 youAre.VendorID = decodedValue @@ -105,7 +105,7 @@ func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber == byte(CharacterString) { + if tagNumber == byte(encoding.CharacterString) { leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) leng += leng1 @@ -116,7 +116,7 @@ func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber == byte(CharacterString) { + if tagNumber == byte(encoding.CharacterString) { leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) leng += leng1 youAre.SerialNumber = decodedValue @@ -126,7 +126,7 @@ func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int if leng < apduLen { leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(BACnetObjectIdentifier) { + if tagNumber == byte(encoding.BACnetObjectIdentifier) { leng += leng1 youAre.DeviceIdentifier = ObjectIdentifier{} leng = youAre.DeviceIdentifier.Decode(buffer, offset+leng, int(lenValue)) @@ -135,7 +135,7 @@ func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int if leng < apduLen { leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(OctetString) { + if tagNumber == byte(encoding.OctetString) { leng += leng1 leng1, decodedValue := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) leng += leng1 diff --git a/pkg/bacnet/network.go b/pkg/bacnet/network.go index aeb5c3c..03d6f12 100644 --- a/pkg/bacnet/network.go +++ b/pkg/bacnet/network.go @@ -8,27 +8,6 @@ import ( "github.com/absmach/bacnet/pkg/encoding" ) -type ApplicationTags int - -const ( - Null ApplicationTags = iota - Boolean - UnsignedInt - SignedInt - Real - Double - OctetString - CharacterString - BitString - Enumerated - Date - Time - BACnetObjectIdentifier - Reserve1 - Reserve2 - Reserve3 -) - type BACnetAddress struct { // BACnet Network Number. // NetworkNumber = 0, for local. @@ -88,7 +67,7 @@ func (ba *BACnetAddress) IPAndPort() (string, int) { func (ba *BACnetAddress) Decode(buffer []byte, offset, apduLen int) int { leng := 0 leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(UnsignedInt) { + if tagNumber == byte(encoding.UnsignedInt) { leng += leng1 leng1, ba.NetworkNumber = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) leng += leng1 @@ -97,7 +76,7 @@ func (ba *BACnetAddress) Decode(buffer []byte, offset, apduLen int) int { } leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(OctetString) { + if tagNumber == byte(encoding.OctetString) { leng += leng1 leng1, ba.MacAddress = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) leng += leng1 diff --git a/pkg/bacnet/property.go b/pkg/bacnet/readProperty.go similarity index 98% rename from pkg/bacnet/property.go rename to pkg/bacnet/readProperty.go index 98c3ce6..a0a5b5f 100644 --- a/pkg/bacnet/property.go +++ b/pkg/bacnet/readProperty.go @@ -136,7 +136,7 @@ func (r *ReadPropertyACK) Decode(buffer []byte, offset, apduLen int) (int, error } type BACnetValue struct { - Tag *ApplicationTags + Tag *encoding.BACnetApplicationTag Value interface{} } @@ -147,23 +147,23 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType *encod if !encoding.IsContextSpecific(buffer[offset]) { tagLen, tagNumber, lenValueType := encoding.DecodeTagNumberAndValue(buffer, offset) if tagLen > 0 { - ttag := ApplicationTags(tagNumber) + ttag := encoding.BACnetApplicationTag(tagNumber) bv.Tag = &ttag length += tagLen decodeLen := 0 switch *bv.Tag { - case Null: + case encoding.Null: bv.Value = nil decodeLen = 0 - case Boolean: + case encoding.Boolean: if lenValueType > 0 { bv.Value = true } else { bv.Value = false } - case UnsignedInt: + case encoding.UnsignedInt: if *propID == encoding.RoutingTable { bv.Tag = nil bv.Value = &RouterEntry{} @@ -187,27 +187,27 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType *encod decodeLen, uintVal = encoding.DecodeUnsigned(buffer, offset+length, int(lenValueType)) bv.Value = uintVal } - case SignedInt: + case encoding.SignedInt: var intValue int decodeLen, intValue = encoding.DecodeSigned(buffer, offset+length, int(lenValueType)) bv.Value = intValue - case Real: + case encoding.Real: var floatValue float32 decodeLen, floatValue = encoding.DecodeRealSafe(buffer, offset+length, int(lenValueType)) bv.Value = floatValue - case Double: + case encoding.Double: var doubleValue float64 decodeLen, doubleValue = encoding.DecodeDoubleSafe(buffer, offset+length, int(lenValueType)) bv.Value = doubleValue - case OctetString: + case encoding.OctetString: var octetValue []byte decodeLen, octetValue = encoding.DecodeOctetString(buffer, offset+length, int(lenValueType)) bv.Value = octetValue - case CharacterString: + case encoding.CharacterString: var stringValue string decodeLen, stringValue = encoding.DecodeCharacterString(buffer, offset+length, apduLen, int(lenValueType)) bv.Value = stringValue - case BitString: + case encoding.BitString: switch *propID { case encoding.RecipientList: bv.Tag = nil @@ -244,9 +244,9 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType *encod decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) bv.Value = bitValue } - case Enumerated: + case encoding.Enumerated: decodeLen, bv.Value = encoding.DecodeEnumerated(buffer, offset+length, lenValueType, objType, propID) - case Date: + case encoding.Date: switch *propID { case encoding.EffectivePeriod: bv.Tag = nil @@ -283,9 +283,9 @@ func (bv *BACnetValue) Decode(buffer []byte, offset, apduLen int, objType *encod if (*objType == encoding.DateTimeValue || *objType == encoding.TimePatternValue) && (*propID == encoding.PresentValue || *propID == encoding.RelinquishDefault) { decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) } - case Time: + case encoding.Time: decodeLen, bv.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+length, int(lenValueType)) - case BACnetObjectIdentifier: + case encoding.BACnetObjectIdentifier: if *propID == encoding.LastKeyServer || *propID == encoding.ManualSlaveAddressBinding || *propID == encoding.SlaveAddressBinding || @@ -517,29 +517,29 @@ func (bv *BACnetValue) Encode() []byte { return nil } else { switch *bv.Tag { - case Boolean: + case encoding.Boolean: return encoding.EncodeApplicationBoolean(bv.Value.(bool)) - case UnsignedInt: + case encoding.UnsignedInt: return encoding.EncodeApplicationUnsigned(bv.Value.(uint32)) - case SignedInt: + case encoding.SignedInt: return encoding.EncodeApplicationSigned(bv.Value.(int32)) - case Real: + case encoding.Real: return encoding.EncodeApplicationReal(bv.Value.(float32)) - case Double: + case encoding.Double: return encoding.EncodeApplicationDouble(bv.Value.(float64)) - case OctetString: + case encoding.OctetString: return encoding.EncodeApplicationOctetString(bv.Value.([]byte), 0, len(bv.Value.([]byte))) - case CharacterString: + case encoding.CharacterString: return encoding.EncodeApplicationCharacterString(bv.Value.(string)) - case BitString: + case encoding.BitString: return encoding.EncodeApplicationBitString(bv.Value) - case Enumerated: + case encoding.Enumerated: return encoding.EncodeApplicationEnumerated(bv.Value.(uint32)) - case Date: + case encoding.Date: return encoding.EncodeApplicationDate(bv.Value.(time.Time)) - case Time: + case encoding.Time: return encoding.EncodeApplicationTime(bv.Value.(time.Time)) - case BACnetObjectIdentifier: + case encoding.BACnetObjectIdentifier: return bv.Value.(*ObjectIdentifier).EncodeApp() default: switch bv.Value.(type) { @@ -1095,7 +1095,7 @@ func (dr *BACnetDateRange) Decode(buffer []byte, offset, apduLen int) (int, erro var leng int leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(Date) { + if tagNumber == byte(encoding.Date) { leng += leng1 leng1, startDate := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) dr.StartDate = startDate @@ -1105,7 +1105,7 @@ func (dr *BACnetDateRange) Decode(buffer []byte, offset, apduLen int) (int, erro } leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) - if tagNumber == byte(Date) { + if tagNumber == byte(encoding.Date) { leng += leng1 leng1, endDate := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) @@ -1128,7 +1128,7 @@ func (binding *BACnetAddressBinding) Decode(buffer []byte, offset int, apduLen i length1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+length) // device_identifier - if tagNumber == byte(BACnetObjectIdentifier) { + if tagNumber == byte(encoding.BACnetObjectIdentifier) { length += length1 binding.DeviceIdentifier = ObjectIdentifier{} length += binding.DeviceIdentifier.Decode(buffer, offset+length, int(lenValue)) @@ -1138,7 +1138,7 @@ func (binding *BACnetAddressBinding) Decode(buffer []byte, offset int, apduLen i length1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+length) - if tagNumber == byte(UnsignedInt) { + if tagNumber == byte(encoding.UnsignedInt) { binding.DeviceAddress = BACnetAddress{} length += binding.DeviceAddress.Decode(buffer, offset+length, int(lenValue)) } else { @@ -1193,15 +1193,15 @@ func (b *BACnetHostAddress) Decode(buffer []byte, offset, apduLen int) (int, err leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) switch tagNumber { - case byte(Null): + case byte(encoding.Null): leng += leng1 b.Value = nil - case byte(OctetString): + case byte(encoding.OctetString): leng += leng1 leng1, octetString := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) b.Value = octetString leng += leng1 - case byte(CharacterString): + case byte(encoding.CharacterString): leng += leng1 leng1, characterString := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) b.Value = characterString @@ -2135,7 +2135,7 @@ func (be *BACnetError) Decode(buffer []byte, offset, apduLen int) int { // Decode error_class leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber == byte(Enumerated) { + if tagNumber == byte(encoding.Enumerated) { leng1, eVal := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) leng += leng1 be.ErrorClass = ErrorClassEnum(eVal.(uint32)) @@ -2146,7 +2146,7 @@ func (be *BACnetError) Decode(buffer []byte, offset, apduLen int) int { // Decode error_code leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - if tagNumber == byte(Enumerated) { + if tagNumber == byte(encoding.Enumerated) { leng1, eVal := encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) leng += leng1 be.ErrorCode = ErrorCodeEnum(eVal.(uint32)) @@ -2363,41 +2363,41 @@ func (bnv *BACnetNameValue) Decode(buffer []byte, offset, apduLen int) int { leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) leng += leng1 - switch ApplicationTags(tagNumber) { - case Null: + switch encoding.BACnetApplicationTag(tagNumber) { + case encoding.Null: bnv.Value = BACnetValue{Value: nil} decodeLen = 0 // Fixme: fix null type nothing else to do, some Error occurs!!!! - case Boolean: + case encoding.Boolean: if lenValue > 0 { bnv.Value = BACnetValue{Value: true} } else { bnv.Value = BACnetValue{Value: false} } - case UnsignedInt: + case encoding.UnsignedInt: decodeLen, bnv.Value.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) - case SignedInt: + case encoding.SignedInt: decodeLen, bnv.Value.Value = encoding.DecodeSigned(buffer, offset+leng, int(lenValue)) - case Real: + case encoding.Real: decodeLen, bnv.Value.Value = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) - case Double: + case encoding.Double: decodeLen, bnv.Value.Value = encoding.DecodeDoubleSafe(buffer, offset+leng, int(lenValue)) - case OctetString: + case encoding.OctetString: decodeLen, bnv.Value.Value = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) - case CharacterString: + case encoding.CharacterString: decodeLen, bnv.Value.Value = encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) - case BitString: + case encoding.BitString: bitValue := BACnetBitString{} decodeLen = bitValue.Decode(buffer, offset+leng, int(lenValue)) bnv.Value.Value = bitValue - case Enumerated: + case encoding.Enumerated: decodeLen, bnv.Value.Value = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) - case Date: + case encoding.Date: decodeLen, dateValue := encoding.DecodeDateSafe(buffer, offset+leng, int(lenValue)) if leng < apduLen { leng1, tagNumber, _ := encoding.DecodeTagNumberAndValue(buffer, offset+leng+decodeLen) - if tagNumber == byte(Time) { + if tagNumber == byte(encoding.Time) { leng += leng1 leng-- bnv.Value.Value = &DateTime{} @@ -2406,7 +2406,7 @@ func (bnv *BACnetNameValue) Decode(buffer []byte, offset, apduLen int) int { } else { bnv.Value.Value = dateValue } - case Time: + case encoding.Time: decodeLen, bnv.Value.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+leng, int(lenValue)) } diff --git a/pkg/bacnet/writeProperty.go b/pkg/bacnet/writeProperty.go new file mode 100644 index 0000000..a7fc268 --- /dev/null +++ b/pkg/bacnet/writeProperty.go @@ -0,0 +1,110 @@ +package bacnet + +import ( + "errors" + + "github.com/absmach/bacnet/pkg/encoding" +) + +type WritePropertyRequest struct { + ObjectIdentifier ObjectIdentifier + PropertyIdentifier interface{} + PropertyArrayIndex uint32 + PropertyValue []BACnetValue + Priority uint32 +} + +func (wpr *WritePropertyRequest) Decode(buffer []byte, offset, apduLen int) (int, error) { + var leng int + + // objectidentifier + if encoding.IsContextTag(buffer, offset+leng, 0) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + wpr.ObjectIdentifier = ObjectIdentifier{} + leng1 = wpr.ObjectIdentifier.Decode(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1, errors.New("Decoding objectidentifier failed") + } + + // propertyidentifier + if encoding.IsContextTag(buffer, offset+leng, 1) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + propID := encoding.PropertyList + leng1, wpr.PropertyIdentifier = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, &propID) + leng += leng1 + } else { + return -1, errors.New("Decoding propertyidentifier failed") + } + + // propertyarrayindex optional + if leng < apduLen { + if encoding.IsContextTag(buffer, offset+leng, 2) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, wpr.PropertyArrayIndex = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + // property-value + if encoding.IsOpeningTagNumber(buffer, offset+leng, 3) { + leng++ + wpr.PropertyValue = make([]BACnetValue, 0) + for !encoding.IsClosingTagNumber(buffer, offset+leng, 3) && leng < apduLen { + bValue := BACnetValue{} + propId := wpr.PropertyIdentifier.(encoding.PropertyIdentifier) + leng1, err := bValue.Decode(buffer, offset+leng, apduLen-leng, &wpr.ObjectIdentifier.Type, &propId) + if err != nil { + return -1, err + } + leng += leng1 + wpr.PropertyValue = append(wpr.PropertyValue, bValue) + } + if encoding.IsClosingTagNumber(buffer, offset+leng, 3) { + leng++ + } else { + return -1, errors.New("decoding error for property_value") + } + } else { + return -1, errors.New("decoding error for property_value") + } + + if leng < apduLen { + // priority + if encoding.IsContextTag(buffer, offset+leng, 4) { + leng1, _, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + leng1, wpr.Priority = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + leng += leng1 + } + } + + return leng, nil +} + +func (wpr *WritePropertyRequest) Encode(buffer []byte, objectID ObjectIdentifier, propertyID, arrayIndex, priority uint32, valueList []BACnetValue) []byte { + buf := encoding.EncodeContextObjectId(0, objectID.Type, uint32(objectID.Instance)) + buf = append(buf, encoding.EncodeContextEnumerated(1, propertyID)...) + + // Optional array index; ALL is -1 which is assumed when missing + if arrayIndex != encoding.ArrayAll { + buf = append(buf, encoding.EncodeContextUnsigned(2, arrayIndex)...) + } + + // PropertyValue + buf = append(buf, encoding.EncodeClosingOpeningTag(3, true)...) + for _, value := range valueList { + buf = append(buf, value.Encode()...) + } + buf = append(buf, encoding.EncodeClosingOpeningTag(3, false)...) + + // Optional priority - 0 if not set, 1..16 if set + if priority != encoding.NoPriority { + buf = append(buf, encoding.EncodeContextUnsigned(4, priority)...) + } + + return buf +} diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go index d2f84ed..4efa806 100644 --- a/pkg/encoding/encoding.go +++ b/pkg/encoding/encoding.go @@ -144,3 +144,36 @@ func EncodeApplicationBitString(val interface{}) []byte { // TODO return nil } + +func EncodeContextObjectId(tagNumber BACnetApplicationTag, objectType ObjectType, instance uint32) []byte { + tag := EncodeTag(tagNumber, true, 4) + objectId := encodeBacnetObjectId(objectType, instance) + return append(tag, objectId...) +} + +func encodeBacnetObjectId(objectType ObjectType, instance uint32) []byte { + objectId := ((uint32(objectType) & MaxObject) << InstanceBits) | (instance & MaxInstance) + buf := make([]byte, 4) + binary.BigEndian.PutUint32(buf, objectId) + return buf +} + +func EncodeClosingOpeningTag(tagNum BACnetApplicationTag, opening bool) []byte { + tag := make([]byte, 2) + tag[0] = 0x8 + if tagNum <= 14 { + tag[0] |= (byte(tagNum) << 4) + } else { + tag[0] |= 0xF0 + binary.BigEndian.PutUint16(tag[1:], uint16(tagNum)) + } + if opening { + // Set the type field to opening tag. + tag[0] |= 6 + return tag + } + // Set the type field to closing tag. + tag[0] |= 7 + + return tag +} From f799994a3c45725e6ee079a51b172e7c62505e43 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Mon, 18 Sep 2023 17:15:56 +0300 Subject: [PATCH 22/25] Add new file writeProperty.go for writing a BACnet property value This commit adds a new file writeProperty.go which contains an example main function for writing a BACnet property value. The function sets up the necessary variables and creates the NPDU (Network Protocol Data Unit) for sending the request to the BACnet device. It then encodes the NPDU and creates the APDU (Application Protocol Data Unit) with the necessary information for a WriteProperty service request. The purpose of this commit is to provide the functionality for writing a BACnet property value to a BACnet device. Signed-off-by: SammyOina --- example/writeProperty/writeProperty.go | 110 +++++++++++++++++++++++++ pkg/bacnet/writeProperty.go | 17 ++-- 2 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 example/writeProperty/writeProperty.go diff --git a/example/writeProperty/writeProperty.go b/example/writeProperty/writeProperty.go new file mode 100644 index 0000000..487a1f7 --- /dev/null +++ b/example/writeProperty/writeProperty.go @@ -0,0 +1,110 @@ +package main + +import ( + "fmt" + "log" + "net" + "time" + + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" + "github.com/absmach/bacnet/pkg/transport" +) + +func main() { + netType := encoding.IPV4 + destination := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809", &netType) + npdu := bacnet.NewNPDU(destination, nil, nil, nil) + npdu.Control.SetDataExpectingReply(true) + npdu.Control.SetNetworkPriority(bacnet.NormalMessage) + + npduBytes, err := npdu.Encode() + if err != nil { + log.Fatal(err) + } + + apdu := bacnet.APDU{ + PduType: bacnet.PDUTypeConfirmedServiceRequest, + ServiceChoice: byte(bacnet.WriteProperty), + SegmentedResponseAccepted: false, + MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), + //MaxApduLengthAccepted: bacnet.MaxAPDU1476, + InvokeID: 0, + } + + valTag := encoding.Real + + req := bacnet.WritePropertyRequest{ + PropertyIdentifier: encoding.PresentValue, + ObjectIdentifier: bacnet.ObjectIdentifier{Type: encoding.AnalogInput, Instance: 10}, + PropertyValue: []bacnet.BACnetValue{{Tag: &valTag, Value: float32(22.55)}}, + } + + mes := append(npduBytes, apdu.Encode()...) + mes = append(mes, req.Encode()...) + + blvc := bacnet.NewBVLC(transport.IP) + blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) + message := append(blvcBytes, mes...) + + // Define the BACnet broadcast address (255.255.255.255:47808) + remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") + if err != nil { + fmt.Println("Error resolving remote address:", err) + return + } + + localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") + if err != nil { + fmt.Println("Error: ", err) + return + } + + // Create a UDP connectionBACnetAddress + conn, err := net.DialUDP("udp", localAddr, remoteAddr) + if err != nil { + fmt.Println("Error creating UDP connection:", err) + return + } + defer conn.Close() + + // Send the WhoIsRequest packet + _, err = conn.Write(message) + if err != nil { + log.Fatal("Error sending WhoIsRequest:", err) + } + + // Wait for responses + buffer := make([]byte, 1500) + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Set a timeout for responses + + for { + //conn.ReadFromUDPAddrPort() + n, _, err := conn.ReadFromUDP(buffer) + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + // Timeout reached, no more responses + log.Println("No more responses received.") + break + } + log.Println("Error reading response:", err) + break + } + + // Process the response (you'll need to parse BACnet responses here) + response := buffer[:n] + blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} + headerLength, function, msgLength, err := blvc.Decode(response, 0) + if err != nil { + log.Fatal(err) + } + fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) + fmt.Println("blvc", blvc) + npdu := bacnet.NPDU{Version: 1} + npduLen := npdu.Decode(response, headerLength) + fmt.Println("npdu", npdu) + apdu := bacnet.APDU{} + _ = apdu.Decode(response, headerLength+npduLen) + fmt.Println("apdu", apdu) + } +} diff --git a/pkg/bacnet/writeProperty.go b/pkg/bacnet/writeProperty.go index a7fc268..4127786 100644 --- a/pkg/bacnet/writeProperty.go +++ b/pkg/bacnet/writeProperty.go @@ -85,25 +85,26 @@ func (wpr *WritePropertyRequest) Decode(buffer []byte, offset, apduLen int) (int return leng, nil } -func (wpr *WritePropertyRequest) Encode(buffer []byte, objectID ObjectIdentifier, propertyID, arrayIndex, priority uint32, valueList []BACnetValue) []byte { - buf := encoding.EncodeContextObjectId(0, objectID.Type, uint32(objectID.Instance)) - buf = append(buf, encoding.EncodeContextEnumerated(1, propertyID)...) +func (wpr *WritePropertyRequest) Encode() []byte { + buf := encoding.EncodeContextObjectId(0, wpr.ObjectIdentifier.Type, uint32(wpr.ObjectIdentifier.Instance)) + propId := wpr.PropertyIdentifier.(encoding.PropertyIdentifier) + buf = append(buf, encoding.EncodeContextEnumerated(1, uint32(propId))...) // Optional array index; ALL is -1 which is assumed when missing - if arrayIndex != encoding.ArrayAll { - buf = append(buf, encoding.EncodeContextUnsigned(2, arrayIndex)...) + if wpr.PropertyArrayIndex != encoding.ArrayAll { + buf = append(buf, encoding.EncodeContextUnsigned(2, wpr.PropertyArrayIndex)...) } // PropertyValue buf = append(buf, encoding.EncodeClosingOpeningTag(3, true)...) - for _, value := range valueList { + for _, value := range wpr.PropertyValue { buf = append(buf, value.Encode()...) } buf = append(buf, encoding.EncodeClosingOpeningTag(3, false)...) // Optional priority - 0 if not set, 1..16 if set - if priority != encoding.NoPriority { - buf = append(buf, encoding.EncodeContextUnsigned(4, priority)...) + if wpr.Priority != encoding.NoPriority { + buf = append(buf, encoding.EncodeContextUnsigned(4, wpr.Priority)...) } return buf From 524fc096de326b48b70d503c729de4644ff6e3f5 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 19 Sep 2023 14:32:21 +0300 Subject: [PATCH 23/25] Add BACnet client implementation This commit adds the implementation of the BACnet client interface. The client provides methods for reading and writing BACnet properties. The client uses the provided transport to send and receive BACnet messages. The client interface defines two methods: ReadProperty and WriteProperty. The ReadProperty method takes a context, an address, and a ReadPropertyRequest as arguments and returns a slice of BACnet values and an error. The WriteProperty method takes a context, an address, and a WritePropertyRequest as arguments and returns an error. The client struct has a transport field of type transport.Transport, which is used for sending and receiving BACnet messages. The ReadProperty method implementation sends a ReadPropertyRequest to the specified address using the transport. It returns the response as a slice of BACnet values or an error if no response is received. The WriteProperty method implementation sends a WritePropertyRequest to the specified address using the transport. It returns an error if no response is received. This commit also includes the necessary imports and error definition. Signed-off-by: SammyOina --- client.go | 157 +++++++++++++++++++++++++ example/readProperty/readProperty.go | 11 +- example/whoIs/whois.go | 14 ++- example/writeProperty/writeProperty.go | 11 +- go.mod | 2 + go.sum | 2 + pkg/bacnet/bvlc.go | 2 +- pkg/bacnet/network.go | 29 +++-- pkg/bacnet/npdu.go | 21 ++-- pkg/transport/transport.go | 10 +- pkg/transport/udp/broadcast.go | 11 +- 11 files changed, 235 insertions(+), 35 deletions(-) create mode 100644 client.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..d599420 --- /dev/null +++ b/client.go @@ -0,0 +1,157 @@ +package bacnet + +import ( + "context" + "errors" + + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" + "github.com/absmach/bacnet/pkg/transport" + "golang.org/x/sync/errgroup" +) + +var _ Client = (*client)(nil) + +var errNoResponse = errors.New("no response received") + +type Client interface { + ReadProperty(ctx context.Context, address string, request bacnet.ReadPropertyRequest) ([]bacnet.BACnetValue, error) + WriteProperty(ctx context.Context, address string, request bacnet.WritePropertyRequest) error +} + +type client struct { + transport transport.Transport +} + +// ReadProperty implements Client. +func (c *client) ReadProperty(ctx context.Context, address string, request bacnet.ReadPropertyRequest) ([]bacnet.BACnetValue, error) { + destination, err := bacnet.NewBACnetAddress(0, nil, address) + if err != nil { + return []bacnet.BACnetValue{}, err + } + npdu := bacnet.NewNPDU(destination, nil, nil, nil) + npdu.Control.SetDataExpectingReply(true) + npdu.Control.SetNetworkPriority(bacnet.NormalMessage) + + npduBytes, err := npdu.Encode() + if err != nil { + return []bacnet.BACnetValue{}, err + } + + apdu := bacnet.APDU{ + PduType: bacnet.PDUTypeConfirmedServiceRequest, + ServiceChoice: byte(bacnet.ReadProperty), + SegmentedResponseAccepted: false, + MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), + InvokeID: 0, + } + + mes := append(npduBytes, apdu.Encode()...) + mes = append(mes, request.Encode()...) + + res := make(chan []byte, 1) + var eg errgroup.Group + + eg.Go(func() error { + defer close(res) + return c.transport.Send(ctx, address, mes, int(bacnet.BVLCOriginalBroadcastNPDU), res) + }) + + // Wait for all goroutines to finish using the error group + if err := eg.Wait(); err != nil { + return []bacnet.BACnetValue{}, err + } + + select { + case <-ctx.Done(): + return []bacnet.BACnetValue{}, ctx.Err() + case response, ok := <-res: + if !ok { + return []bacnet.BACnetValue{}, errNoResponse + } else { + blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} + headerLength, _, _, err := blvc.Decode(response, 0) + if err != nil { + return []bacnet.BACnetValue{}, err + } + npduRes := bacnet.NPDU{Version: 1} + npduLen, err := npduRes.Decode(response, headerLength) + if err != nil { + return []bacnet.BACnetValue{}, err + } + apduRes := bacnet.APDU{} + apduLen := apduRes.Decode(response, headerLength+npduLen) + readPropACK := bacnet.ReadPropertyACK{} + if _, err = readPropACK.Decode(response, headerLength+npduLen+apduLen-2, len(response)); err != nil { + return []bacnet.BACnetValue{}, err + } + return readPropACK.PropertyValue, nil + } + default: + return []bacnet.BACnetValue{}, errNoResponse + } +} + +// WriteProperty implements Client. +func (c *client) WriteProperty(ctx context.Context, address string, request bacnet.WritePropertyRequest) error { + destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + if err != nil { + return err + } + npdu := bacnet.NewNPDU(destination, nil, nil, nil) + npdu.Control.SetDataExpectingReply(true) + npdu.Control.SetNetworkPriority(bacnet.NormalMessage) + + npduBytes, err := npdu.Encode() + if err != nil { + return err + } + + apdu := bacnet.APDU{ + PduType: bacnet.PDUTypeConfirmedServiceRequest, + ServiceChoice: byte(bacnet.WriteProperty), + SegmentedResponseAccepted: false, + MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), + InvokeID: 0, + } + + mes := append(npduBytes, apdu.Encode()...) + mes = append(mes, request.Encode()...) + + res := make(chan []byte, 1) + var eg errgroup.Group + + eg.Go(func() error { + defer close(res) + return c.transport.Send(ctx, address, mes, int(bacnet.BVLCOriginalBroadcastNPDU), res) + }) + + // Wait for all goroutines to finish using the error group + if err := eg.Wait(); err != nil { + return err + } + select { + case <-ctx.Done(): + return ctx.Err() + case response, ok := <-res: + if !ok { + return errNoResponse + } else { + blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} + headerLength, _, _, err := blvc.Decode(response, 0) + if err != nil { + return err + } + npduRes := bacnet.NPDU{Version: 1} + npduLen, err := npduRes.Decode(response, headerLength) + if err != nil { + return err + } + apduRes := bacnet.APDU{} + _ = apduRes.Decode(response, headerLength+npduLen) + return nil + } + default: + return errNoResponse + } +} diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go index 7720952..d66dcb1 100644 --- a/example/readProperty/readProperty.go +++ b/example/readProperty/readProperty.go @@ -12,8 +12,10 @@ import ( ) func main() { - netType := encoding.IPV4 - destination := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809", &netType) + destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + if err != nil { + log.Fatal(err) + } npdu := bacnet.NewNPDU(destination, nil, nil, nil) npdu.Control.SetDataExpectingReply(true) npdu.Control.SetNetworkPriority(bacnet.NormalMessage) @@ -98,7 +100,10 @@ func main() { fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) fmt.Println("blvc", blvc) npdu := bacnet.NPDU{Version: 1} - npduLen := npdu.Decode(response, headerLength) + npduLen, err := npdu.Decode(response, headerLength) + if err != nil { + log.Fatal(err) + } fmt.Println("npdu", npdu) apdu := bacnet.APDU{} apduLen := apdu.Decode(response, headerLength+npduLen) diff --git a/example/whoIs/whois.go b/example/whoIs/whois.go index cd52b4d..0c6f0fc 100644 --- a/example/whoIs/whois.go +++ b/example/whoIs/whois.go @@ -7,7 +7,6 @@ import ( "time" "github.com/absmach/bacnet/pkg/bacnet" - "github.com/absmach/bacnet/pkg/encoding" "github.com/absmach/bacnet/pkg/transport" "github.com/absmach/bacnet/pkg/transport/udp" ) @@ -25,10 +24,12 @@ func main() { if err != nil { log.Fatalf("failed to encode npdu with error %v", err) } - netType := encoding.IPV4 - broads = *bacnet.NewBACnetAddress(0xFFFF, nil, "127.0.0.255:47809", &netType) + broads, err = bacnet.NewBACnetAddress(0xFFFF, nil, "127.0.0.255:47809") + if err != nil { + log.Fatal(err) + } - npdu := bacnet.NewNPDU(&broads, nil, nil, nil) + npdu := bacnet.NewNPDU(broads, nil, nil, nil) npdu.Control.SetNetworkPriority(bacnet.NormalMessage) npduBytes, err := npdu.Encode() if err != nil { @@ -105,7 +106,10 @@ func main() { fmt.Println("blvc", blvc) fmt.Println(response[headerLength:]) npdu := bacnet.NPDU{Version: 1} - npduLen := npdu.Decode(response, headerLength) + npduLen, err := npdu.Decode(response, headerLength) + if err != nil { + log.Fatal(err) + } fmt.Println("npdu", npdu) fmt.Println(response[headerLength+npduLen:]) apdu := bacnet.APDU{} diff --git a/example/writeProperty/writeProperty.go b/example/writeProperty/writeProperty.go index 487a1f7..2d78f96 100644 --- a/example/writeProperty/writeProperty.go +++ b/example/writeProperty/writeProperty.go @@ -12,8 +12,10 @@ import ( ) func main() { - netType := encoding.IPV4 - destination := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809", &netType) + destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + if err != nil { + log.Fatal(err) + } npdu := bacnet.NewNPDU(destination, nil, nil, nil) npdu.Control.SetDataExpectingReply(true) npdu.Control.SetNetworkPriority(bacnet.NormalMessage) @@ -101,7 +103,10 @@ func main() { fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) fmt.Println("blvc", blvc) npdu := bacnet.NPDU{Version: 1} - npduLen := npdu.Decode(response, headerLength) + npduLen, err := npdu.Decode(response, headerLength) + if err != nil { + log.Fatal(err) + } fmt.Println("npdu", npdu) apdu := bacnet.APDU{} _ = apdu.Decode(response, headerLength+npduLen) diff --git a/go.mod b/go.mod index c8d470d..80e1779 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module github.com/absmach/bacnet go 1.21.0 + +require golang.org/x/sync v0.3.0 diff --git a/go.sum b/go.sum index e69de29..4c4db29 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= diff --git a/pkg/bacnet/bvlc.go b/pkg/bacnet/bvlc.go index 5591d9f..329efa4 100644 --- a/pkg/bacnet/bvlc.go +++ b/pkg/bacnet/bvlc.go @@ -49,7 +49,7 @@ type BVLC struct { BVLCMaxAPDU MaxAPDU } -func NewBVLC(transprt transport.Transport) *BVLC { +func NewBVLC(transprt transport.TransportMethod) *BVLC { bvlc := &BVLC{ BVLLTypeBACnetIP: 0x81, BVLCHeaderLength: 4, diff --git a/pkg/bacnet/network.go b/pkg/bacnet/network.go index 03d6f12..e58b43f 100644 --- a/pkg/bacnet/network.go +++ b/pkg/bacnet/network.go @@ -3,6 +3,7 @@ package bacnet import ( "encoding/binary" "fmt" + "net" "strings" "github.com/absmach/bacnet/pkg/encoding" @@ -17,7 +18,7 @@ type BACnetAddress struct { MacAddress []byte } -func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface{}, netType *encoding.BACnetNetworkType) *BACnetAddress { +func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface{}) (*BACnetAddress, error) { addr := &BACnetAddress{ NetworkNumber: networkNumber, MacAddress: macAddress, @@ -26,7 +27,21 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface switch addr1 := address.(type) { case string: if address != "" { - switch *netType { + + var netType encoding.BACnetNetworkType + if ip := net.ParseIP(addr1); ip != nil { + if ip.To4() != nil { + netType = encoding.IPV4 + } else if ip.To16() != nil { + netType = encoding.IPV6 + } + } else { + if _, err := net.ParseMAC(addr1); err != nil { + return nil, err + } + netType = encoding.Ethernet + } + switch netType { case encoding.IPV4: tmp1 := strings.Split(addr1, ":") parts := strings.Split(tmp1[0], ".") @@ -49,13 +64,13 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface } } case ObjectIdentifier: - if *netType == encoding.IPV4 { - addr.MacAddress = make([]byte, 8) - binary.LittleEndian.PutUint64(addr.MacAddress, uint64(addr1.Instance)) - } + // Only IPV4 TODO. + addr.MacAddress = make([]byte, 8) + binary.LittleEndian.PutUint64(addr.MacAddress, uint64(addr1.Instance)) + } - return addr + return addr, nil } func (ba *BACnetAddress) IPAndPort() (string, int) { diff --git a/pkg/bacnet/npdu.go b/pkg/bacnet/npdu.go index 7dc9f92..03fa45c 100644 --- a/pkg/bacnet/npdu.go +++ b/pkg/bacnet/npdu.go @@ -3,11 +3,12 @@ package bacnet import ( "encoding/binary" "errors" - "log" "github.com/absmach/bacnet/internal" ) +var errNPDUVersion = errors.New("unexpected NPDU version") + type NPDU struct { Version uint8 // Always one. Control NPDUControlInformation @@ -188,14 +189,14 @@ func (npdu *NPDU) Encode() ([]byte, error) { return buffer, nil } -func (npdu *NPDU) Decode(buffer []byte, offset int) int { +func (npdu *NPDU) Decode(buffer []byte, offset int) (int, error) { length := 0 version := buffer[offset] // always 1!!!! length++ if version != npdu.Version { - log.Println("Received something else!") - return -1 + return -1, errNPDUVersion } + var err error npdu.Control = *NewNPDUControlInformation() length += npdu.Control.Decode(buffer, offset+length) @@ -207,7 +208,10 @@ func (npdu *NPDU) Decode(buffer []byte, offset int) int { length++ npdu.DADR = buffer[offset+length : offset+length+int(npdu.DLEN)] length += int(npdu.DLEN) - npdu.Destination = NewBACnetAddress(uint32(npdu.DNET), npdu.DADR, "", nil) + npdu.Destination, err = NewBACnetAddress(uint32(npdu.DNET), npdu.DADR, "") + if err != nil { + return -1, err + } } if npdu.Control.IsSourceSpecifier() { @@ -217,7 +221,10 @@ func (npdu *NPDU) Decode(buffer []byte, offset int) int { length++ npdu.SADR = buffer[offset+length : offset+length+int(npdu.SLEN)] length += int(npdu.SLEN) - npdu.Source = NewBACnetAddress(uint32(npdu.SNET), npdu.SADR, "", nil) + npdu.Source, err = NewBACnetAddress(uint32(npdu.SNET), npdu.SADR, "") + if err != nil { + return -1, err + } } if npdu.Control.IsDestinationSpecifier() { @@ -234,5 +241,5 @@ func (npdu *NPDU) Decode(buffer []byte, offset int) int { } } - return length + return length, nil } diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 57ba5ac..2c24d50 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -1,7 +1,13 @@ package transport -type Transport int +import "context" + +type TransportMethod int const ( - IP = iota + IP TransportMethod = iota ) + +type Transport interface { + Send(ctx context.Context, address string, payload []byte, BVLCFunction int, res chan []byte) error +} diff --git a/pkg/transport/udp/broadcast.go b/pkg/transport/udp/broadcast.go index f4edff2..f57ca11 100644 --- a/pkg/transport/udp/broadcast.go +++ b/pkg/transport/udp/broadcast.go @@ -5,21 +5,20 @@ import ( "strconv" "github.com/absmach/bacnet/pkg/bacnet" - "github.com/absmach/bacnet/pkg/encoding" ) -func GetBroadcastAddress(localEndpoint string, port int) (bacnet.BACnetAddress, error) { +func GetBroadcastAddress(localEndpoint string, port int) (*bacnet.BACnetAddress, error) { broadcast := "255.255.255.255" interfaces, err := net.Interfaces() if err != nil { - return bacnet.BACnetAddress{}, err + return &bacnet.BACnetAddress{}, err } for _, iface := range interfaces { addrs, err := iface.Addrs() if err != nil { - return bacnet.BACnetAddress{}, err + return &bacnet.BACnetAddress{}, err } for _, addr := range addrs { @@ -33,7 +32,5 @@ func GetBroadcastAddress(localEndpoint string, port int) (bacnet.BACnetAddress, } } } - netType := encoding.IPV4 - return *bacnet.NewBACnetAddress(0xFFFF, nil, broadcast+":"+strconv.Itoa(port), &netType), nil - + return bacnet.NewBACnetAddress(0xFFFF, nil, broadcast+":"+strconv.Itoa(port)) } From b786a58f7c08f30e1b3b3a8537ae5ed7993150ba Mon Sep 17 00:00:00 2001 From: SammyOina Date: Tue, 19 Sep 2023 18:01:05 +0300 Subject: [PATCH 24/25] implement UDP/BACnetIP transport Signed-off-by: SammyOina --- pkg/transport/transport.go | 4 +++ pkg/transport/udp/service.go | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 pkg/transport/udp/service.go diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 2c24d50..91858ea 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -2,12 +2,16 @@ package transport import "context" +// TransportMethod network method to transport messages. type TransportMethod int const ( + // IP BACnet IP transport via UDP. IP TransportMethod = iota ) +// Transport interface to send messages and return responses on the network layer. type Transport interface { + // Send sends a message payload after adding the header and returns the response on a channel. Send(ctx context.Context, address string, payload []byte, BVLCFunction int, res chan []byte) error } diff --git a/pkg/transport/udp/service.go b/pkg/transport/udp/service.go new file mode 100644 index 0000000..ec41288 --- /dev/null +++ b/pkg/transport/udp/service.go @@ -0,0 +1,63 @@ +package udp + +import ( + "context" + "net" + "time" + + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/transport" +) + +var _ transport.Transport = (*client)(nil) + +type client struct { + conn *net.UDPConn +} + +// NewClient creates a new trasnport interface for BACnet/IP via UDP. +func NewClient(ipAddress string) (transport.Transport, error) { + udp, err := net.ResolveUDPAddr("udp", ipAddress) + if err != nil { + return nil, err + } + conn, err := net.ListenUDP("udp", udp) + if err != nil { + return nil, err + } + return &client{conn: conn}, nil +} + +// Send sends a message payload after adding the header and returns the response on a channel. +func (c *client) Send(ctx context.Context, address string, payload []byte, BVLCFunction int, res chan []byte) error { + + blvc := bacnet.NewBVLC(transport.IP) + blvcBytes := blvc.Encode(bacnet.BVLCFunctions(BVLCFunction), uint16(len(payload)+4)) + message := append(blvcBytes, payload...) + + remoteAddr, err := net.ResolveUDPAddr("udp", address) + if err != nil { + return err + } + + if _, err := c.conn.WriteTo(message, remoteAddr); err != nil { + return err + } + + buffer := make([]byte, 1500) + c.conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + + for { + n, _, err := c.conn.ReadFromUDP(buffer) + if err != nil { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + break + } + return err + } + + res <- buffer[:n] + return nil + } + return nil +} From 8cd1d229334231fee4c9d2cd7d42fa30c0f87c69 Mon Sep 17 00:00:00 2001 From: SammyOina Date: Wed, 20 Sep 2023 09:35:25 +0300 Subject: [PATCH 25/25] Fix missing newline at the end of the file Add missing import statement for "fmt" Add comments to ReadProperty and WriteProperty methods Add NewClient function to create a new client Updates examples to new client interface. Signed-off-by: SammyOina --- client.go | 27 +++++-- example/readProperty/readProperty.go | 105 +++---------------------- example/writeProperty/writeProperty.go | 104 +++--------------------- pkg/bacnet/network.go | 3 +- pkg/transport/transport.go | 2 + pkg/transport/udp/service.go | 5 ++ 6 files changed, 49 insertions(+), 197 deletions(-) diff --git a/client.go b/client.go index d599420..a1b58ce 100644 --- a/client.go +++ b/client.go @@ -3,6 +3,7 @@ package bacnet import ( "context" "errors" + "fmt" "github.com/absmach/bacnet/pkg/bacnet" "github.com/absmach/bacnet/pkg/encoding" @@ -15,7 +16,9 @@ var _ Client = (*client)(nil) var errNoResponse = errors.New("no response received") type Client interface { + // ReadProperty provides interface to readproperty and return the value read. ReadProperty(ctx context.Context, address string, request bacnet.ReadPropertyRequest) ([]bacnet.BACnetValue, error) + // WriteProperty provides an interface to write a property value and returns a nil error if successful. WriteProperty(ctx context.Context, address string, request bacnet.WritePropertyRequest) error } @@ -23,10 +26,18 @@ type client struct { transport transport.Transport } -// ReadProperty implements Client. +// NewClient creates a new bacnet client given the transport interface. +func NewClient(transport transport.Transport) Client { + return &client{ + transport: transport, + } +} + +// ReadProperty provides interface to readproperty and return the value read. func (c *client) ReadProperty(ctx context.Context, address string, request bacnet.ReadPropertyRequest) ([]bacnet.BACnetValue, error) { destination, err := bacnet.NewBACnetAddress(0, nil, address) if err != nil { + fmt.Println("dest err") return []bacnet.BACnetValue{}, err } npdu := bacnet.NewNPDU(destination, nil, nil, nil) @@ -35,6 +46,7 @@ func (c *client) ReadProperty(ctx context.Context, address string, request bacne npduBytes, err := npdu.Encode() if err != nil { + fmt.Println("npdu err") return []bacnet.BACnetValue{}, err } @@ -57,8 +69,8 @@ func (c *client) ReadProperty(ctx context.Context, address string, request bacne return c.transport.Send(ctx, address, mes, int(bacnet.BVLCOriginalBroadcastNPDU), res) }) - // Wait for all goroutines to finish using the error group if err := eg.Wait(); err != nil { + fmt.Println("send err") return []bacnet.BACnetValue{}, err } @@ -67,34 +79,39 @@ func (c *client) ReadProperty(ctx context.Context, address string, request bacne return []bacnet.BACnetValue{}, ctx.Err() case response, ok := <-res: if !ok { + fmt.Println("not ok err") return []bacnet.BACnetValue{}, errNoResponse } else { blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} headerLength, _, _, err := blvc.Decode(response, 0) if err != nil { + fmt.Println("blvc err") return []bacnet.BACnetValue{}, err } npduRes := bacnet.NPDU{Version: 1} npduLen, err := npduRes.Decode(response, headerLength) if err != nil { + fmt.Println("npdu res err") return []bacnet.BACnetValue{}, err } apduRes := bacnet.APDU{} apduLen := apduRes.Decode(response, headerLength+npduLen) readPropACK := bacnet.ReadPropertyACK{} if _, err = readPropACK.Decode(response, headerLength+npduLen+apduLen-2, len(response)); err != nil { + fmt.Println("readprop dec err") return []bacnet.BACnetValue{}, err } return readPropACK.PropertyValue, nil } default: + fmt.Println("default err") return []bacnet.BACnetValue{}, errNoResponse } } -// WriteProperty implements Client. +// WriteProperty provides an interface to write a property value and returns a nil error if successful. func (c *client) WriteProperty(ctx context.Context, address string, request bacnet.WritePropertyRequest) error { - destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + destination, err := bacnet.NewBACnetAddress(0, nil, address) if err != nil { return err } @@ -126,10 +143,10 @@ func (c *client) WriteProperty(ctx context.Context, address string, request bacn return c.transport.Send(ctx, address, mes, int(bacnet.BVLCOriginalBroadcastNPDU), res) }) - // Wait for all goroutines to finish using the error group if err := eg.Wait(); err != nil { return err } + select { case <-ctx.Done(): return ctx.Err() diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go index d66dcb1..817a0bd 100644 --- a/example/readProperty/readProperty.go +++ b/example/readProperty/readProperty.go @@ -1,117 +1,30 @@ package main import ( + "context" "fmt" "log" - "net" - "time" + bacClient "github.com/absmach/bacnet" "github.com/absmach/bacnet/pkg/bacnet" "github.com/absmach/bacnet/pkg/encoding" - "github.com/absmach/bacnet/pkg/transport" + "github.com/absmach/bacnet/pkg/transport/udp" ) func main() { - destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + transportClient, err := udp.NewClient("127.0.0.5:47809") if err != nil { log.Fatal(err) } - npdu := bacnet.NewNPDU(destination, nil, nil, nil) - npdu.Control.SetDataExpectingReply(true) - npdu.Control.SetNetworkPriority(bacnet.NormalMessage) - - npduBytes, err := npdu.Encode() - if err != nil { - log.Fatal(err) - } - - apdu := bacnet.APDU{ - PduType: bacnet.PDUTypeConfirmedServiceRequest, - ServiceChoice: byte(bacnet.ReadProperty), - SegmentedResponseAccepted: false, - MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), - //MaxApduLengthAccepted: bacnet.MaxAPDU1476, - InvokeID: 0, - } - + defer transportClient.Close() + client := bacClient.NewClient(transportClient) req := bacnet.ReadPropertyRequest{ PropertyIdentifier: encoding.PresentValue, ObjectIdentifier: &bacnet.ObjectIdentifier{Type: encoding.AnalogInput, Instance: 10}, } - - mes := append(npduBytes, apdu.Encode()...) - mes = append(mes, req.Encode()...) - - blvc := bacnet.NewBVLC(transport.IP) - blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) - message := append(blvcBytes, mes...) - - // Define the BACnet broadcast address (255.255.255.255:47808) - remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") - if err != nil { - fmt.Println("Error resolving remote address:", err) - return - } - - localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") - if err != nil { - fmt.Println("Error: ", err) - return - } - - // Create a UDP connectionBACnetAddress - conn, err := net.DialUDP("udp", localAddr, remoteAddr) - if err != nil { - fmt.Println("Error creating UDP connection:", err) - return - } - defer conn.Close() - - // Send the WhoIsRequest packet - _, err = conn.Write(message) + val, err := client.ReadProperty(context.Background(), "127.0.0.6:47809", req) if err != nil { - log.Fatal("Error sending WhoIsRequest:", err) - } - - // Wait for responses - buffer := make([]byte, 1500) - conn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Set a timeout for responses - - for { - //conn.ReadFromUDPAddrPort() - n, _, err := conn.ReadFromUDP(buffer) - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - // Timeout reached, no more responses - log.Println("No more responses received.") - break - } - log.Println("Error reading response:", err) - break - } - - // Process the response (you'll need to parse BACnet responses here) - response := buffer[:n] - blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} - headerLength, function, msgLength, err := blvc.Decode(response, 0) - if err != nil { - log.Fatal(err) - } - fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) - fmt.Println("blvc", blvc) - npdu := bacnet.NPDU{Version: 1} - npduLen, err := npdu.Decode(response, headerLength) - if err != nil { - log.Fatal(err) - } - fmt.Println("npdu", npdu) - apdu := bacnet.APDU{} - apduLen := apdu.Decode(response, headerLength+npduLen) - fmt.Println("apdu", apdu) - readPropACK := bacnet.ReadPropertyACK{} - if _, err = readPropACK.Decode(response, headerLength+npduLen+apduLen-2, len(response)); err != nil { - log.Fatal(err) - } - fmt.Println("readprop", readPropACK) + log.Fatal(err) } + fmt.Printf("%v\n", val) } diff --git a/example/writeProperty/writeProperty.go b/example/writeProperty/writeProperty.go index 2d78f96..2dad6e4 100644 --- a/example/writeProperty/writeProperty.go +++ b/example/writeProperty/writeProperty.go @@ -1,115 +1,31 @@ package main import ( - "fmt" + "context" "log" - "net" - "time" + bacClient "github.com/absmach/bacnet" "github.com/absmach/bacnet/pkg/bacnet" "github.com/absmach/bacnet/pkg/encoding" - "github.com/absmach/bacnet/pkg/transport" + "github.com/absmach/bacnet/pkg/transport/udp" ) func main() { - destination, err := bacnet.NewBACnetAddress(0, nil, "127.0.0.6:47809") + transportClient, err := udp.NewClient("127.0.0.5:47809") if err != nil { log.Fatal(err) } - npdu := bacnet.NewNPDU(destination, nil, nil, nil) - npdu.Control.SetDataExpectingReply(true) - npdu.Control.SetNetworkPriority(bacnet.NormalMessage) - - npduBytes, err := npdu.Encode() - if err != nil { - log.Fatal(err) - } - - apdu := bacnet.APDU{ - PduType: bacnet.PDUTypeConfirmedServiceRequest, - ServiceChoice: byte(bacnet.WriteProperty), - SegmentedResponseAccepted: false, - MaxSegmentsAccepted: bacnet.BacnetMaxSegments(encoding.NoSegmentation), - //MaxApduLengthAccepted: bacnet.MaxAPDU1476, - InvokeID: 0, - } - + defer transportClient.Close() + client := bacClient.NewClient(transportClient) valTag := encoding.Real - req := bacnet.WritePropertyRequest{ PropertyIdentifier: encoding.PresentValue, ObjectIdentifier: bacnet.ObjectIdentifier{Type: encoding.AnalogInput, Instance: 10}, PropertyValue: []bacnet.BACnetValue{{Tag: &valTag, Value: float32(22.55)}}, } - - mes := append(npduBytes, apdu.Encode()...) - mes = append(mes, req.Encode()...) - - blvc := bacnet.NewBVLC(transport.IP) - blvcBytes := blvc.Encode(bacnet.BVLCOriginalBroadcastNPDU, uint16(len(mes)+4)) - message := append(blvcBytes, mes...) - - // Define the BACnet broadcast address (255.255.255.255:47808) - remoteAddr, err := net.ResolveUDPAddr("udp", "127.0.0.6:47809") - if err != nil { - fmt.Println("Error resolving remote address:", err) - return - } - - localAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") - if err != nil { - fmt.Println("Error: ", err) - return - } - - // Create a UDP connectionBACnetAddress - conn, err := net.DialUDP("udp", localAddr, remoteAddr) - if err != nil { - fmt.Println("Error creating UDP connection:", err) - return - } - defer conn.Close() - - // Send the WhoIsRequest packet - _, err = conn.Write(message) - if err != nil { - log.Fatal("Error sending WhoIsRequest:", err) - } - - // Wait for responses - buffer := make([]byte, 1500) - conn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Set a timeout for responses - - for { - //conn.ReadFromUDPAddrPort() - n, _, err := conn.ReadFromUDP(buffer) - if err != nil { - if netErr, ok := err.(net.Error); ok && netErr.Timeout() { - // Timeout reached, no more responses - log.Println("No more responses received.") - break - } - log.Println("Error reading response:", err) - break - } - - // Process the response (you'll need to parse BACnet responses here) - response := buffer[:n] - blvc := bacnet.BVLC{BVLLTypeBACnetIP: 0x81} - headerLength, function, msgLength, err := blvc.Decode(response, 0) - if err != nil { - log.Fatal(err) - } - fmt.Printf("headerLength %v BVLCfunction %v msgLen %v\n", headerLength, function, msgLength) - fmt.Println("blvc", blvc) - npdu := bacnet.NPDU{Version: 1} - npduLen, err := npdu.Decode(response, headerLength) - if err != nil { - log.Fatal(err) - } - fmt.Println("npdu", npdu) - apdu := bacnet.APDU{} - _ = apdu.Decode(response, headerLength+npduLen) - fmt.Println("apdu", apdu) + if err := client.WriteProperty(context.Background(), "127.0.0.6:47809", req); err == nil { + log.Println("successful write") + } else { + log.Fatal(err) } } diff --git a/pkg/bacnet/network.go b/pkg/bacnet/network.go index e58b43f..07ebe0e 100644 --- a/pkg/bacnet/network.go +++ b/pkg/bacnet/network.go @@ -27,9 +27,8 @@ func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface switch addr1 := address.(type) { case string: if address != "" { - var netType encoding.BACnetNetworkType - if ip := net.ParseIP(addr1); ip != nil { + if ip := net.ParseIP(strings.Split(addr1, ":")[0]); ip != nil { if ip.To4() != nil { netType = encoding.IPV4 } else if ip.To16() != nil { diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index 91858ea..7a83187 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -14,4 +14,6 @@ const ( type Transport interface { // Send sends a message payload after adding the header and returns the response on a channel. Send(ctx context.Context, address string, payload []byte, BVLCFunction int, res chan []byte) error + // Close closes the network connection. + Close() error } diff --git a/pkg/transport/udp/service.go b/pkg/transport/udp/service.go index ec41288..2222a6a 100644 --- a/pkg/transport/udp/service.go +++ b/pkg/transport/udp/service.go @@ -61,3 +61,8 @@ func (c *client) Send(ctx context.Context, address string, payload []byte, BVLCF } return nil } + +// Close closes the udp connection. +func (c *client) Close() error { + return c.conn.Close() +}