diff --git a/example/readProperty/readProperty.go b/example/readProperty/readProperty.go new file mode 100644 index 0000000..7720952 --- /dev/null +++ b/example/readProperty/readProperty.go @@ -0,0 +1,112 @@ +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.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...) + + // 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{} + 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) + } +} diff --git a/example/whoIs/whois.go b/example/whoIs/whois.go new file mode 100644 index 0000000..cd52b4d --- /dev/null +++ b/example/whoIs/whois.go @@ -0,0 +1,123 @@ +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" + "github.com/absmach/bacnet/pkg/transport/udp" +) + +func main() { + + var highLimit, lowLimit uint32 = 4000000, 0 + req := bacnet.WhoIs{ + HighLimit: &highLimit, + LowLimit: &lowLimit, + } + 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 := encoding.IPV4 + broads = *bacnet.NewBACnetAddress(0xFFFF, nil, "127.0.0.255:47809", &netType) + + npdu := bacnet.NewNPDU(&broads, 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...) + + 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 { + 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) + 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/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/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 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 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< 0xFFFF { + return -1, errors.New("Value exceeds 0xFFFF") + } + iam.VendorID = decodedValue + + return leng, nil +} + +func (iam IAmRequest) Encode() []byte { + tmp := iam.IamDeviceIdentifier.Encode() + propID := iam.SegmentationSupported.(encoding.PropertyIdentifier) + 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 { + VendorID uint32 + ModelName string + SerialNumber string + DeviceIdentifier ObjectIdentifier + DeviceMACAddress []byte +} + +func (youAre *YouAreRequest) Decode(buffer []byte, offset int, apduLen int) (int, error) { + leng := 0 + + leng1, tagNumber, lenValue := encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(encoding.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(encoding.CharacterString) { + leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + + leng += leng1 + youAre.ModelName = decodedValue + } else { + return -1, errors.New("Invalid tag number") + } + + leng1, tagNumber, lenValue = encoding.DecodeTagNumberAndValue(buffer, offset+leng) + leng += leng1 + if tagNumber == byte(encoding.CharacterString) { + leng1, decodedValue := encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + leng += leng1 + 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(encoding.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(encoding.OctetString) { + leng += leng1 + leng1, decodedValue := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + leng += leng1 + youAre.DeviceMACAddress = decodedValue + } + } + + return leng, nil +} + +func (youAre YouAreRequest) Encode() []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/pkg/bacnet/network.go b/pkg/bacnet/network.go new file mode 100644 index 0000000..03d6f12 --- /dev/null +++ b/pkg/bacnet/network.go @@ -0,0 +1,88 @@ +package bacnet + +import ( + "encoding/binary" + "fmt" + "strings" + + "github.com/absmach/bacnet/pkg/encoding" +) + +type BACnetAddress struct { + // BACnet Network Number. + // NetworkNumber = 0, for local. + NetworkNumber uint32 + // MacAddress represnets the ip address with 4 bytes and 2 bytes for port. + // If len == 0, then this a broadcast address. + MacAddress []byte +} + +func NewBACnetAddress(networkNumber uint32, macAddress []byte, address interface{}, netType *encoding.BACnetNetworkType) *BACnetAddress { + addr := &BACnetAddress{ + NetworkNumber: networkNumber, + MacAddress: macAddress, + } + + switch addr1 := address.(type) { + case string: + if address != "" { + switch *netType { + case encoding.IPV4: + tmp1 := strings.Split(addr1, ":") + parts := strings.Split(tmp1[0], ".") + var ipAddr [4]byte + for i, part := range parts { + val := byte(0) + fmt.Sscanf(part, "%d", &val) + ipAddr[i] = val + } + var port uint16 + fmt.Sscanf(tmp1[1], "%d", &port) + addr.MacAddress = append(ipAddr[:], byte(port>>8), byte(port)) + case encoding.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 == encoding.IPV4 { + addr.MacAddress = make([]byte, 8) + 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(encoding.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(encoding.OctetString) { + leng += leng1 + leng1, ba.MacAddress = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + leng += leng1 + } else { + return -1 + } + + return leng +} diff --git a/pkg/bacnet/npdu.go b/pkg/bacnet/npdu.go new file mode 100644 index 0000000..7dc9f92 --- /dev/null +++ b/pkg/bacnet/npdu.go @@ -0,0 +1,238 @@ +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 *uint8, vendorID *uint16) *NPDU { + npdu := &NPDU{ + Version: 1, + Control: *NewNPDUControlInformation(), + Destination: destination, + Source: source, + } + switch hopCount { + case nil: + npdu.HopCount = 255 + default: + npdu.HopCount = *hopCount + } + if vendorID != nil { + npdu.VendorID = *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 +} diff --git a/pkg/bacnet/object.go b/pkg/bacnet/object.go new file mode 100644 index 0000000..a68a104 --- /dev/null +++ b/pkg/bacnet/object.go @@ -0,0 +1,49 @@ +package bacnet + +import ( + "encoding/binary" + + "github.com/absmach/bacnet/pkg/encoding" +) + +type ObjectInstance uint32 + +type ObjectIdentifier struct { + 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 = 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.IsContextTag(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< 0 { + ttag := encoding.BACnetApplicationTag(tagNumber) + bv.Tag = &ttag + length += tagLen + + decodeLen := 0 + + switch *bv.Tag { + case encoding.Null: + bv.Value = nil + decodeLen = 0 + case encoding.Boolean: + if lenValueType > 0 { + bv.Value = true + } else { + bv.Value = false + } + case encoding.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, err + } + } 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 encoding.SignedInt: + var intValue int + decodeLen, intValue = encoding.DecodeSigned(buffer, offset+length, int(lenValueType)) + bv.Value = intValue + case encoding.Real: + var floatValue float32 + decodeLen, floatValue = encoding.DecodeRealSafe(buffer, offset+length, int(lenValueType)) + bv.Value = floatValue + case encoding.Double: + var doubleValue float64 + decodeLen, doubleValue = encoding.DecodeDoubleSafe(buffer, offset+length, int(lenValueType)) + bv.Value = doubleValue + case encoding.OctetString: + var octetValue []byte + decodeLen, octetValue = encoding.DecodeOctetString(buffer, offset+length, int(lenValueType)) + bv.Value = octetValue + case encoding.CharacterString: + var stringValue string + decodeLen, stringValue = encoding.DecodeCharacterString(buffer, offset+length, apduLen, int(lenValueType)) + bv.Value = stringValue + case encoding.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, int(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, int(lenValueType)) + bv.Value = bitValue + case encoding.ProtocolObjectTypesSupported: + bv.Tag = nil + bitValue := &BACnetObjectTypesSupported{} + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) + bv.Value = bitValue + case encoding.ProtocolServicesSupported: + bv.Tag = nil + bitValue := &BACnetServicesSupported{} + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) + bv.Value = bitValue + default: + bitValue := &BACnetBitString{} + decodeLen = bitValue.Decode(buffer, offset, int(lenValueType)) + bv.Value = bitValue + } + case encoding.Enumerated: + decodeLen, bv.Value = encoding.DecodeEnumerated(buffer, offset+length, lenValueType, objType, propID) + case encoding.Date: + switch *propID { + case encoding.EffectivePeriod: + bv.Tag = nil + bv.Value = &BACnetDateRange{} + length-- + decodeLen, err = bv.Value.(*BACnetDateRange).Decode(buffer, offset+length, apduLen) + if err != nil { + return -1, err + } + 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, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) + } + if (*objType == encoding.DateTimeValue || *objType == encoding.TimePatternValue) && (*propID == encoding.PresentValue || *propID == encoding.RelinquishDefault) { + decodeLen, bv.Value = encoding.DecodeDateSafe(buffer, offset+length, int(lenValueType)) + } + case encoding.Time: + decodeLen, bv.Value = encoding.DecodeBACnetTimeSafe(buffer, offset+length, int(lenValueType)) + case encoding.BACnetObjectIdentifier: + if *propID == encoding.LastKeyServer || + *propID == encoding.ManualSlaveAddressBinding || + *propID == encoding.SlaveAddressBinding || + *propID == encoding.DeviceAddressBinding { + bv.Tag = nil + bv.Value = &BACnetAddressBinding{} + length-- + decodeLen = bv.Value.(*BACnetAddressBinding).Decode(buffer, offset+length, apduLen) + } else { + 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) + length = apduLen + } + + if decodeLen < 0 { + return -1, fmt.Errorf("no tags decoded") + } + length += decodeLen + } + } else { + switch *propID { + case encoding.BacnetIpGlobalAddress, encoding.FdBbmdAddress: + 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 = &BACnetRecipient{} + length += bv.Value.(*BACnetRecipient).Decode(buffer, offset+length, apduLen-length) + case encoding.KeySets: + 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, + encoding.LastRestoreTime, + encoding.TimeOfDeviceRestart, + encoding.AccessEventTime, + encoding.UpdateTime: + 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 = &ReadAccessSpecification{} + length, err = bv.Value.(*ReadAccessSpecification).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } + case encoding.ListOfObjectPropertyReferences: + bv.Value = &BACnetDeviceObjectPropertyReference{} + length, err = bv.Value.(*BACnetDeviceObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + if err != nil { + return -1, err + } + 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 = &BACnetDeviceObjectReference{} + length += bv.Value.(*BACnetDeviceObjectReference).Decode(buffer, offset+length, apduLen-length) + case encoding.EventAlgorithmInhibitRef, + encoding.InputReference, + encoding.ManipulatedVariableReference, + encoding.ControlledVariableReference: + bv.Value = &BACnetObjectPropertyReference{} + length += bv.Value.(*BACnetObjectPropertyReference).Decode(buffer, offset+length, apduLen-length) + case encoding.LoggingRecord: + 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 = &BACnetActionList{} + length += bv.Value.(*BACnetActionList).Decode(buffer, offset+length, apduLen-length) + case encoding.Scale: + bv.Value = &BACnetScale{} + length += bv.Value.(*BACnetScale).Decode(buffer, offset+length, apduLen-length) + case encoding.LightingCommand: + bv.Value = &BACnetLightingCommand{} + length += bv.Value.(*BACnetLightingCommand).Decode(buffer, offset+length, apduLen-length) + case encoding.Prescale: + bv.Value = &BACnetPrescale{} + length += bv.Value.(*BACnetPrescale).Decode(buffer, offset+length, apduLen-length) + case encoding.RequestedShedLevel, + encoding.ExpectedShedLevel, + encoding.ActualShedLevel: + bv.Value = &BACnetShedLevel{} + length += bv.Value.(*BACnetShedLevel).Decode(buffer, offset+length, apduLen-length) + case encoding.LogBuffer: + 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 = &BACnetCalendarEntry{} + length += bv.Value.(*BACnetCalendarEntry).Decode(buffer, offset+length, apduLen-length) + case encoding.PresentValue: + switch *objType { + case encoding.Group: + bv.Value = &ReadAccessResult{} + length += bv.Value.(*ReadAccessResult).Decode(buffer, offset+length, apduLen-length) + 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 = &BACnetAccessRule{} + length += bv.Value.(*BACnetAccessRule).Decode(buffer, offset+length, apduLen-length) + case encoding.Tags: + bv.Value = &BACnetNameValue{} + length += bv.Value.(*BACnetNameValue).Decode(buffer, offset+length, apduLen-length) + case encoding.SubordinateTags: + bv.Value = &BACnetNameValueCollection{} + length += bv.Value.(*BACnetNameValueCollection).Decode(buffer, offset+length, apduLen-length) + case encoding.NetworkAccessSecurityPolicies: + bv.Value = &BACnetNetworkSecurityPolicy{} + length += bv.Value.(*BACnetNetworkSecurityPolicy).Decode(buffer, offset+length, apduLen-length) + case encoding.PortFilter: + bv.Value = &BACnetPortPermission{} + length += bv.Value.(*BACnetPortPermission).Decode(buffer, offset+length, apduLen-length) + case encoding.PriorityArray: + bv.Value = &BACnetPriorityArray{} + length += bv.Value.(*BACnetPriorityArray).Decode(buffer, offset+length, apduLen-length) + case encoding.ProcessIdentifierFilter: + bv.Value = &BACnetProcessIdSelection{} + length += bv.Value.(*BACnetProcessIdSelection).Decode(buffer, offset+length, apduLen-length) + case encoding.SetpointReference: + bv.Value = &BACnetSetpointReference{} + length += bv.Value.(*BACnetSetpointReference).Decode(buffer, offset+length, apduLen-length) + case encoding.ExceptionSchedule: + bv.Value = &BACnetSpecialEvent{} + length += bv.Value.(*BACnetSpecialEvent).Decode(buffer, offset+length, apduLen-length) + case encoding.StateChangeValues: + bv.Value = &BACnetTimerStateChangeValue{} + length += bv.Value.(*BACnetTimerStateChangeValue).Decode(buffer, offset+length, apduLen-length) + case encoding.ValueSource, encoding.ValueSourceArray: + bv.Value = &BACnetValueSource{} + length += bv.Value.(*BACnetValueSource).Decode(buffer, offset+length, apduLen-length) + case encoding.VirtualMacAddressTable: + bv.Value = &BACnetVMACEntry{} + length += bv.Value.(*BACnetVMACEntry).Decode(buffer, offset+length, apduLen-length) + case encoding.AssignedAccessRights: + bv.Value = &BACnetAssignedAccessRights{} + length += bv.Value.(*BACnetAssignedAccessRights).Decode(buffer, offset+length, apduLen-length) + case encoding.AssignedLandingCalls: + bv.Value = &BACnetAssignedLandingCalls{} + length += bv.Value.(*BACnetAssignedLandingCalls).Decode(buffer, offset+length, apduLen-length) + case encoding.AccessEventAuthenticationFactor: + bv.Value = &BACnetAuthenticationFactor{} + length += bv.Value.(*BACnetAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) + case encoding.SupportedFormats: + bv.Value = &BACnetAuthenticationFactorFormat{} + length += bv.Value.(*BACnetAuthenticationFactorFormat).Decode(buffer, offset+length, apduLen-length) + case encoding.AuthenticationPolicyList: + bv.Value = &BACnetAuthenticationPolicy{} + length += bv.Value.(*BACnetAuthenticationPolicy).Decode(buffer, offset+length, apduLen-length) + case encoding.ActiveCovSubscriptions: + bv.Value = &BACnetCOVSubscription{} + length += bv.Value.(*BACnetCOVSubscription).Decode(buffer, offset+length, apduLen-length) + case encoding.AuthenticationFactors: + bv.Value = &BACnetCredentialAuthenticationFactor{} + length += bv.Value.(*BACnetCredentialAuthenticationFactor).Decode(buffer, offset+length, apduLen-length) + case encoding.WeeklySchedule: + bv.Value = &BACnetDailySchedule{} + length += bv.Value.(*BACnetDailySchedule).Decode(buffer, offset+length, apduLen-length) + case encoding.SubscribedRecipients: + bv.Value = &BACnetEventNotificationSubscription{} + length += bv.Value.(*BACnetEventNotificationSubscription).Decode(buffer, offset+length, apduLen-length) + case encoding.EventParameters: + bv.Value = &BACnetEventParameter{} + length += bv.Value.(*BACnetEventParameter).Decode(buffer, offset+length, apduLen-length) + case encoding.FaultParameters: + bv.Value = &BACnetFaultParameter{} + length += bv.Value.(*BACnetFaultParameter).Decode(buffer, offset+length, apduLen-length) + default: + bv.Value = nil + } + } + return length, nil +} + +func (bv *BACnetValue) Encode() []byte { + if bv.Tag == nil { + return nil + } else { + switch *bv.Tag { + case encoding.Boolean: + return encoding.EncodeApplicationBoolean(bv.Value.(bool)) + case encoding.UnsignedInt: + return encoding.EncodeApplicationUnsigned(bv.Value.(uint32)) + case encoding.SignedInt: + return encoding.EncodeApplicationSigned(bv.Value.(int32)) + case encoding.Real: + return encoding.EncodeApplicationReal(bv.Value.(float32)) + case encoding.Double: + return encoding.EncodeApplicationDouble(bv.Value.(float64)) + case encoding.OctetString: + return encoding.EncodeApplicationOctetString(bv.Value.([]byte), 0, len(bv.Value.([]byte))) + case encoding.CharacterString: + return encoding.EncodeApplicationCharacterString(bv.Value.(string)) + case encoding.BitString: + return encoding.EncodeApplicationBitString(bv.Value) + case encoding.Enumerated: + return encoding.EncodeApplicationEnumerated(bv.Value.(uint32)) + case encoding.Date: + return encoding.EncodeApplicationDate(bv.Value.(time.Time)) + case encoding.Time: + return encoding.EncodeApplicationTime(bv.Value.(time.Time)) + case encoding.BACnetObjectIdentifier: + 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) + 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) 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) +} + +// 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, + } +} + +// 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) +} + +// 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(encoding.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(encoding.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 +} + +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(encoding.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(encoding.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(encoding.Null): + leng += leng1 + b.Value = nil + case byte(encoding.OctetString): + leng += leng1 + leng1, octetString := encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + b.Value = octetString + leng += leng1 + case byte(encoding.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...) +} + +// 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 +} + +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(encoding.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(encoding.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 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 encoding.Boolean: + if lenValue > 0 { + bnv.Value = BACnetValue{Value: true} + } else { + bnv.Value = BACnetValue{Value: false} + } + case encoding.UnsignedInt: + decodeLen, bnv.Value.Value = encoding.DecodeUnsigned(buffer, offset+leng, int(lenValue)) + case encoding.SignedInt: + decodeLen, bnv.Value.Value = encoding.DecodeSigned(buffer, offset+leng, int(lenValue)) + case encoding.Real: + decodeLen, bnv.Value.Value = encoding.DecodeRealSafe(buffer, offset+leng, int(lenValue)) + case encoding.Double: + decodeLen, bnv.Value.Value = encoding.DecodeDoubleSafe(buffer, offset+leng, int(lenValue)) + case encoding.OctetString: + decodeLen, bnv.Value.Value = encoding.DecodeOctetString(buffer, offset+leng, int(lenValue)) + case encoding.CharacterString: + decodeLen, bnv.Value.Value = encoding.DecodeCharacterString(buffer, offset+leng, apduLen-leng, int(lenValue)) + case encoding.BitString: + bitValue := BACnetBitString{} + decodeLen = bitValue.Decode(buffer, offset+leng, int(lenValue)) + bnv.Value.Value = bitValue + case encoding.Enumerated: + decodeLen, bnv.Value.Value = encoding.DecodeEnumerated(buffer, offset+leng, lenValue, nil, nil) + 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(encoding.Time) { + leng += leng1 + leng-- + bnv.Value.Value = &DateTime{} + decodeLen = bnv.Value.Value.(*DateTime).Decode(buffer, offset+leng) + } + } else { + bnv.Value.Value = dateValue + } + case encoding.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/bacnet/whois.go b/pkg/bacnet/whois.go new file mode 100644 index 0000000..e3a0826 --- /dev/null +++ b/pkg/bacnet/whois.go @@ -0,0 +1,55 @@ +package bacnet + +import "github.com/absmach/bacnet/pkg/encoding" + +type WhoIs struct { + HighLimit, LowLimit *uint32 +} + +func (w *WhoIs) Decode(buf []byte, offset, apduLen int) int { + if apduLen <= 0 { + return 0 + } + length, tagNum, lenVal := encoding.DecodeTagNumberAndValue(buf, offset) + if tagNum != 0 { + return -1 + } + if apduLen > 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{} +} diff --git a/pkg/bacnet/writeProperty.go b/pkg/bacnet/writeProperty.go new file mode 100644 index 0000000..4127786 --- /dev/null +++ b/pkg/bacnet/writeProperty.go @@ -0,0 +1,111 @@ +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() []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 wpr.PropertyArrayIndex != encoding.ArrayAll { + buf = append(buf, encoding.EncodeContextUnsigned(2, wpr.PropertyArrayIndex)...) + } + + // PropertyValue + buf = append(buf, encoding.EncodeClosingOpeningTag(3, true)...) + 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 wpr.Priority != encoding.NoPriority { + buf = append(buf, encoding.EncodeContextUnsigned(4, wpr.Priority)...) + } + + return buf +} diff --git a/pkg/encoding/date_time.go b/pkg/encoding/date_time.go new file mode 100644 index 0000000..a47f305 --- /dev/null +++ b/pkg/encoding/date_time.go @@ -0,0 +1,85 @@ +package encoding + +import "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 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/pkg/encoding/decoding.go b/pkg/encoding/decoding.go new file mode 100644 index 0000000..0cf82de --- /dev/null +++ b/pkg/encoding/decoding.go @@ -0,0 +1,166 @@ +package encoding + +import ( + "encoding/binary" + "math" + "time" +) + +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++ { + value += uint32(buffer[offset+i]) << uint(8*(len-i-1)) + } + 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 +} + +// 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 +} + +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) +} + +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) +} diff --git a/pkg/encoding/encoding.go b/pkg/encoding/encoding.go new file mode 100644 index 0000000..4efa806 --- /dev/null +++ b/pkg/encoding/encoding.go @@ -0,0 +1,179 @@ +package encoding + +import ( + "bytes" + "encoding/binary" + "math" +) + +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() + } +} + +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)...) +} + +// 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...) +} + +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 +} + +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 +} diff --git a/pkg/encoding/property.go b/pkg/encoding/property.go new file mode 100644 index 0000000..2ed48a0 --- /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 ( + SegmentedBoth BACnetSegmentation = iota + SegmentedTransmit + SegmentedReceive + NoSegmentation +) + +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 new file mode 100644 index 0000000..8bd9239 --- /dev/null +++ b/pkg/encoding/tags.go @@ -0,0 +1,136 @@ +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 IsContextTag(buf []byte, offset int, tagNum byte) bool { + _, myTagNum := decodeTagNumber(buf, offset) + return IsContextSpecific(buf[offset]) && myTagNum == tagNum + +} + +func IsContextTagWithLength(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))...) + } +} + +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 +} diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go new file mode 100644 index 0000000..57ba5ac --- /dev/null +++ b/pkg/transport/transport.go @@ -0,0 +1,7 @@ +package transport + +type Transport int + +const ( + IP = iota +) diff --git a/pkg/transport/udp/broadcast.go b/pkg/transport/udp/broadcast.go new file mode 100644 index 0000000..f4edff2 --- /dev/null +++ b/pkg/transport/udp/broadcast.go @@ -0,0 +1,39 @@ +package udp + +import ( + "net" + "strconv" + + "github.com/absmach/bacnet/pkg/bacnet" + "github.com/absmach/bacnet/pkg/encoding" +) + +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 := encoding.IPV4 + return *bacnet.NewBACnetAddress(0xFFFF, nil, broadcast+":"+strconv.Itoa(port), &netType), nil + +}