Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NOISSUE - Add Read Property and WHOIS support #1

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
765 changes: 765 additions & 0 deletions base.go

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package bacnet
SammyOina marked this conversation as resolved.
Show resolved Hide resolved

type client struct {
// Fields for client configuration
}

type Client interface {
ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error)
WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error
}

func NewClient(address string, port int) (Client, error) {
return &client{}, nil
}

func (c *client) ReadProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier) (interface{}, error) {
return nil, nil
}

func (c *client) WriteProperty(objectID ObjectIdentifier, propertyID PropertyIdentifier, value interface{}) error {
return nil
}
85 changes: 85 additions & 0 deletions encoding/date_time.go
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions encoding/decoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package encoding

func DecodeUnsigned(buffer []byte, offset, len int) (int, uint32) {
value := uint32(0)
for i := 0; i < len; i++ {
value += uint32(buffer[offset+i]) << uint(8*(len-i-1))
}
return len, value
}
78 changes: 78 additions & 0 deletions encoding/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package encoding

import (
"bytes"
"encoding/binary"
)

const (
MaxObject = 0x3FF
SammyOina marked this conversation as resolved.
Show resolved Hide resolved
InstanceBits = 22
MaxInstance = 0x3FFFFF
MaxBitstringBytes = 15
ArrayAll = 0xFFFFFFFF
NoPriority = 0
MinPriority = 1
MaxPriority = 16
)

func EncodeUnsigned(value uint32) []byte {
switch {
case value < 0x100:
SammyOina marked this conversation as resolved.
Show resolved Hide resolved
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)
SammyOina marked this conversation as resolved.
Show resolved Hide resolved
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)...)
}
134 changes: 134 additions & 0 deletions encoding/tags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package encoding

type BACnetApplicationTag int

const (
Null BACnetApplicationTag = iota
Boolean
UnsignedInt
SignedInt
Real
Double
OctetString
CharacterString
BitString
Enumerated
Date
Time
BACnetObjectIdentifier
Reserve1
Reserve2
Reserve3
)

func isExtendedTagNumber(b byte) bool {
return (b & 0xF0) == 0xF0
SammyOina marked this conversation as resolved.
Show resolved Hide resolved
}

func isExtendedValue(b byte) bool {
return (b & 0x07) == 5
}

func isOpeningTag(b byte) bool {
return (b & 0x07) == 6
}

func isClosingTag(b byte) bool {
return (b & 0x07) == 7
}

func isContextSpecific(b byte) bool {
return (b & 0x8) == 0x8
}

func decodeTagNumber(buf []byte, offset int) (len int, tagNum byte) {
len = 1

if isExtendedTagNumber(buf[offset]) {
return len + 1, buf[offset+len]
}
return len, buf[offset] >> 4
}

func DecodeIsCOntextTag(buf []byte, offset int, tagNum byte) bool {
_, myTagNum := decodeTagNumber(buf, offset)
return isContextSpecific(buf[offset]) && myTagNum == tagNum

}

func DecodeIsCOntextTagWithLength(buf []byte, offset int, tagNum byte) (int, bool) {
tagLen, myTagNum := decodeTagNumber(buf, offset)
return tagLen, isContextSpecific(buf[offset]) && myTagNum == tagNum
}

func DecodeTagNumberAndValue(buf []byte, offset int) (len int, tagNum byte, val uint32) {
len, tagNum = decodeTagNumber(buf, offset)

switch {
case isExtendedValue(buf[offset]):
switch buf[offset+len] {
case 255:
len += 1
len1, val1 := DecodeUnsigned(buf, offset+len, 4)
len += len1
val = val1
case 254:
len += 1
len1, val1 := DecodeUnsigned(buf, offset+len, 2)
len += len1
val = val1
default:
val = uint32(buf[offset+len])
len += 1
}
case isOpeningTag(buf[offset]), isClosingTag(buf[offset]):
val = 0
default:
val = uint32(buf[offset] & 0x07)
}
return len, tagNum, val

}

func DecodeTagNumber(buf []byte, offset int) (len int, tagNum byte) {
len = 1
if isExtendedTagNumber(buf[offset]) {
return len + 1, buf[offset+len]
}
return len, buf[offset] >> 4
}

func EncodeTag(tagNum BACnetApplicationTag, ctxSpecific bool, lenVal int) []byte {
tag := []byte{}
value := byte(0)

if ctxSpecific {
value = 0x8
}

if tagNum <= 14 {
value += byte(tagNum) << 4
tag = append(tag, value)
} else {
value += 0xF0
tag = append(tag, value)
tag = append(tag, byte(tagNum))
}

if lenVal <= 4 {
tag[0] += byte(lenVal)
return tag
}
tag[0] += 5
switch {
case lenVal <= 253:
tag = append(tag, byte(lenVal))
return tag
case lenVal <= 65535:
tag = append(tag, 254)
return append(tag, EncodeUnsigned(uint32(lenVal))...)
default:
tag = append(tag, 255)
return append(tag, EncodeUnsigned(uint32(lenVal))...)
}
}
22 changes: 22 additions & 0 deletions general.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package bacnet

import (
"time"

"github.com/absmach/bacnet/encoding"
)

type DateTime struct {
Date time.Time
}

func (dt *DateTime) Decode(buf []byte, offset int) int {
len, date := encoding.DecodeApplicationDate(buf, offset)
len1, ttime := encoding.DecodeApplicationTime(buf, offset+len)
dt.Date = ttime.AddDate(date.Year(), int(date.Month()), date.Day())
return len + len1
}

func (dt DateTime) Encode() []byte {
return append(encoding.EncodeApplicationDate(dt.Date), encoding.EncodeApplicationTime(dt.Date)...)
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/absmach/bacnet

go 1.21.0
Loading