{"id":49891361,"url":"https://github.com/anviod/bacnet","last_synced_at":"2026-05-15T21:17:03.570Z","repository":{"id":357722254,"uuid":"1236395532","full_name":"anviod/bacnet","owner":"anviod","description":"BACnet 协议栈是一款基于 Go 语言实现的 BACnet/IP 通信组件，面向楼宇自动化（BA）与工业控制系统。支持设备发现（Who-Is/I-Am）、属性读写（Read/WriteProperty）、点位建模与扩展，具备跨平台能力（ARM/x86）与高并发性能，适用于边缘网关与数据采集场景。模块化架构便于协议扩展与系统集成，可无缝对接多种南向设备与北向平台。The BACnet protocol stack is implemented in Go for building automation, supporting discovery, property operations, flexible modeling, and scalable integration.","archived":false,"fork":false,"pushed_at":"2026-05-14T01:13:52.000Z","size":123,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-14T03:19:28.010Z","etag":null,"topics":["bacnet","bacnet-client","bacnet-ip","bacnet-library","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anviod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-12T07:55:49.000Z","updated_at":"2026-05-14T02:41:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/anviod/bacnet","commit_stats":null,"previous_names":["anviod/bacnet"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/anviod/bacnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anviod%2Fbacnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anviod%2Fbacnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anviod%2Fbacnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anviod%2Fbacnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anviod","download_url":"https://codeload.github.com/anviod/bacnet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anviod%2Fbacnet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33080777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T20:25:35.270Z","status":"ssl_error","status_checked_at":"2026-05-15T20:25:34.732Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bacnet","bacnet-client","bacnet-ip","bacnet-library","golang"],"created_at":"2026-05-15T21:17:02.859Z","updated_at":"2026-05-15T21:17:03.559Z","avatar_url":"https://github.com/anviod.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Documentation\n\n- [English Documentation](README.md) (This file)\n- [中文文档](README_CN.md)\n  \n# BACnet Protocol Stack\n\nA Go implementation of the BACnet/IP protocol stack for building automation and control systems.\n\n## Features\n\n- **BACnet/IP Protocol**: Full support for BACnet/IP communication\n- **Device Discovery**: Who-Is and I-Am services for network device discovery\n- **Object Access**: ReadProperty, ReadMultipleProperty, WriteProperty, WriteMultipleProperty\n- **Network Management**: What-Is-Network-Number, Who-Is-Router-To-Network\n- **Transaction Management**: TSM (Transaction State Machine) for confirmed services\n- **Concurrency**: Thread-safe design with connection pooling\n\n## Installation\n\n```bash\ngo get github.com/anviod/bacnet\n```\n\n## Quick Start\n\n### Basic Device Discovery\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"log\"\n    \n    \"github.com/anviod/bacnet\"\n    \"github.com/anviod/bacnet/btypes\"\n)\n\nfunc main() {\n    // Create a BACnet client\n    client, err := bacnet.NewClient(\u0026bacnet.ClientBuilder{\n        Ip:         \"192.168.1.100\",\n        SubnetCIDR: 24,\n        Port:       47808, // Default BACnet port (0xBAC0)\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer client.Close()\n\n    // Start the client message loop\n    go client.ClientRun()\n\n    // Discover all devices on the network\n    devices, err := client.WhoIs(\u0026bacnet.WhoIsOpts{\n        Low:  0,\n        High: 4194304, // Max BACnet device ID\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Printf(\"Discovered %d devices\\n\", len(devices))\n    for _, dev := range devices {\n        fmt.Printf(\"Device ID: %d, IP: %s:%d\\n\", dev.DeviceID, dev.Ip, dev.Port)\n    }\n}\n```\n\n### Read Point Value (Analog Input)\n\n```go\n// Read the present value of an analog input point (AI-1)\nfunc readAnalogInput(client bacnet.Client, device btypes.Device) {\n    result, err := client.ReadProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.AnalogInput, // Object type: Analog Input\n                Instance: 1,                  // Point number: AI-1\n            },\n            Properties: []btypes.Property{\n                {\n                    Type:       btypes.PropPresentValue, // Read present value\n                    ArrayIndex: btypes.ArrayAll,\n                },\n            },\n        },\n    })\n    if err != nil {\n        log.Printf(\"Failed to read AI-1: %v\", err)\n        return\n    }\n\n    // Get the value\n    if len(result.Object.Properties) \u003e 0 {\n        fmt.Printf(\"AI-1 Present Value: %v\\n\", result.Object.Properties[0].Data)\n    }\n}\n```\n\n### Read Binary Input Point\n\n```go\n// Read the present value of a binary input point (BI-1)\nfunc readBinaryInput(client bacnet.Client, device btypes.Device) {\n    result, err := client.ReadProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.BinaryInput, // Object type: Binary Input\n                Instance: 1,                  // Point number: BI-1\n            },\n            Properties: []btypes.Property{\n                {\n                    Type:       btypes.PropPresentValue,\n                    ArrayIndex: btypes.ArrayAll,\n                },\n            },\n        },\n    })\n    if err != nil {\n        log.Printf(\"Failed to read BI-1: %v\", err)\n        return\n    }\n\n    if len(result.Object.Properties) \u003e 0 {\n        value := result.Object.Properties[0].Data\n        state := \"OFF\"\n        if value == true || value == uint8(1) {\n            state = \"ON\"\n        }\n        fmt.Printf(\"BI-1 Present Value: %s (%v)\\n\", state, value)\n    }\n}\n```\n\n### Write Point Value (Analog Output)\n\n```go\n// Write a value to an analog output point (AO-1)\nfunc writeAnalogOutput(client bacnet.Client, device btypes.Device, value float64) error {\n    err := client.WriteProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.AnalogOutput, // Object type: Analog Output\n                Instance: 1,                   // Point number: AO-1\n            },\n            Properties: []btypes.Property{\n                {\n                    Type:       btypes.PropPresentValue,\n                    ArrayIndex: btypes.ArrayAll,\n                    Data:       value,             // Value to write (e.g., 25.5)\n                    Priority:   btypes.Normal,    // Priority level\n                },\n            },\n        },\n    })\n    if err != nil {\n        log.Printf(\"Failed to write AO-1: %v\", err)\n        return err\n    }\n\n    fmt.Printf(\"Successfully wrote %.2f to AO-1\\n\", value)\n    return nil\n}\n```\n\n### Write Binary Output Point\n\n```go\n// Write a value to a binary output point (BO-1)\nfunc writeBinaryOutput(client bacnet.Client, device btypes.Device, value bool) error {\n    err := client.WriteProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.BinaryOutput, // Object type: Binary Output\n                Instance: 1,                   // Point number: BO-1\n            },\n            Properties: []btypes.Property{\n                {\n                    Type:       btypes.PropPresentValue,\n                    ArrayIndex: btypes.ArrayAll,\n                    Data:       value,           // true = ON, false = OFF\n                    Priority:   btypes.Normal,   // Priority level\n                },\n            },\n        },\n    })\n    if err != nil {\n        log.Printf(\"Failed to write BO-1: %v\", err)\n        return err\n    }\n\n    fmt.Printf(\"Successfully wrote %v to BO-1\\n\", value)\n    return nil\n}\n```\n\n### Read Multiple Properties at Once\n\n```go\n// Read multiple properties from multiple objects in one request\nfunc readMultiplePoints(client bacnet.Client, device btypes.Device) {\n    result, err := client.ReadMultiProperty(device, btypes.MultiplePropertyData{\n        Objects: []btypes.Object{\n            // Read AI-1 present value and units\n            {\n                ID: btypes.ObjectID{Type: btypes.AnalogInput, Instance: 1},\n                Properties: []btypes.Property{\n                    {Type: btypes.PropPresentValue},\n                    {Type: btypes.PropUnits},\n                },\n            },\n            // Read AI-2 present value\n            {\n                ID: btypes.ObjectID{Type: btypes.AnalogInput, Instance: 2},\n                Properties: []btypes.Property{\n                    {Type: btypes.PropPresentValue},\n                },\n            },\n            // Read BI-1 present value\n            {\n                ID: btypes.ObjectID{Type: btypes.BinaryInput, Instance: 1},\n                Properties: []btypes.Property{\n                    {Type: btypes.PropPresentValue},\n                },\n            },\n        },\n    })\n    if err != nil {\n        log.Printf(\"Failed to read multiple properties: %v\", err)\n        return\n    }\n\n    // Process results\n    for _, obj := range result.Objects {\n        fmt.Printf(\"Object: %s-%d\\n\", obj.ID.Type, obj.ID.Instance)\n        for _, prop := range obj.Properties {\n            fmt.Printf(\"  %s: %v\\n\", prop.Type, prop.Data)\n        }\n    }\n}\n```\n\n### Scan All Objects in Device\n\n```go\n// Scan all objects in a device\nfunc scanDeviceObjects(client bacnet.Client, device btypes.Device) error {\n    // Get all objects from the device\n    scannedDevice, err := client.Objects(device)\n    if err != nil {\n        return fmt.Errorf(\"failed to scan objects: %v\", err)\n    }\n\n    fmt.Printf(\"Found %d objects in device %d\\n\", scannedDevice.Objects.Len(), device.DeviceID)\n\n    // Iterate through all object types\n    objectTypes := []btypes.ObjectType{\n        btypes.AnalogInput,\n        btypes.AnalogOutput,\n        btypes.AnalogValue,\n        btypes.BinaryInput,\n        btypes.BinaryOutput,\n        btypes.BinaryValue,\n    }\n\n    for _, objType := range objectTypes {\n        objects := scannedDevice.Objects[objType]\n        if len(objects) == 0 {\n            continue\n        }\n\n        fmt.Printf(\"\\n%s objects:\\n\", objType)\n        for instance, obj := range objects {\n            fmt.Printf(\"  Instance %d: Name=%q\\n\", instance, obj.Name)\n        }\n    }\n\n    return nil\n}\n```\n\n### Complete Device Integration Flow\n\n```go\n// Complete flow: Discover device -\u003e Scan objects -\u003e Read value -\u003e Write value\nfunc completeIntegration(client bacnet.Client) error {\n    // Step 1: Discover devices\n    devices, err := client.WhoIs(\u0026bacnet.WhoIsOpts{\n        Low:  2228316,\n        High: 2228316,\n    })\n    if err != nil {\n        return fmt.Errorf(\"whois failed: %v\", err)\n    }\n    if len(devices) == 0 {\n        return fmt.Errorf(\"no devices found\")\n    }\n\n    device := devices[0]\n    fmt.Printf(\"Found device: ID=%d, IP=%s:%d\\n\", device.DeviceID, device.Ip, device.Port)\n\n    // Step 2: Scan objects\n    scannedDevice, err := client.Objects(device)\n    if err != nil {\n        return fmt.Errorf(\"object scan failed: %v\", err)\n    }\n\n    // Step 3: Find target point\n    aiObjects := scannedDevice.Objects[btypes.AnalogInput]\n    targetPoint, ok := aiObjects[0] // AnalogInput:0\n    if !ok {\n        return fmt.Errorf(\"target point AnalogInput:0 not found\")\n    }\n    fmt.Printf(\"Found target point: %s\\n\", targetPoint.Name)\n\n    // Step 4: Read present value\n    result, err := client.ReadProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.AnalogInput,\n                Instance: 0,\n            },\n            Properties: []btypes.Property{\n                {Type: btypes.PropPresentValue},\n            },\n        },\n    })\n    if err != nil {\n        return fmt.Errorf(\"read property failed: %v\", err)\n    }\n    fmt.Printf(\"Present Value: %v\\n\", result.Object.Properties[0].Data)\n\n    // Step 5: Write to AnalogValue\n    writeErr := client.WriteProperty(device, btypes.PropertyData{\n        Object: btypes.Object{\n            ID: btypes.ObjectID{\n                Type:     btypes.AnalogValue,\n                Instance: 1,\n            },\n            Properties: []btypes.Property{\n                {\n                    Type:       btypes.PropPresentValue,\n                    ArrayIndex: btypes.ArrayAll,\n                    Data:       float64(25.5),\n                    Priority:   btypes.Normal,\n                },\n            },\n        },\n    })\n    if writeErr != nil {\n        return fmt.Errorf(\"write property failed: %v\", writeErr)\n    }\n    fmt.Println(\"Write successful\")\n\n    return nil\n}\n```\n\n## API Reference\n\n### Client Interface\n\n```go\ntype Client interface {\n    io.Closer\n    IsRunning() bool\n    ClientRun()\n    \n    // Device Discovery\n    WhoIs(wh *WhoIsOpts) ([]btypes.Device, error)\n    IAm(dest btypes.Address, iam btypes.IAm) error\n    \n    // Network Management\n    WhatIsNetworkNumber() []*btypes.Address\n    WhoIsRouterToNetwork() (resp *[]btypes.Address)\n    \n    // Object Access\n    Objects(dev btypes.Device) (btypes.Device, error)\n    ReadProperty(dest btypes.Device, rp btypes.PropertyData) (btypes.PropertyData, error)\n    ReadMultiProperty(dev btypes.Device, rp btypes.MultiplePropertyData) (btypes.MultiplePropertyData, error)\n    WriteProperty(dest btypes.Device, wp btypes.PropertyData) error\n    WriteMultiProperty(dev btypes.Device, wp btypes.MultiplePropertyData) error\n    \n    // Timeout variants\n    ReadPropertyWithTimeout(dest btypes.Device, rp btypes.PropertyData, timeout time.Duration) (btypes.PropertyData, error)\n    ReadMultiPropertyWithTimeout(dev btypes.Device, rp btypes.MultiplePropertyData, timeout time.Duration) (btypes.MultiplePropertyData, error)\n}\n```\n\n### WhoIs Options\n\n```go\ntype WhoIsOpts struct {\n    Low             int             // Device ID lower bound (0 to 4194304)\n    High            int             // Device ID upper bound\n    GlobalBroadcast bool            // Use global broadcast (0xFFFF)\n    NetworkNumber   uint16          // Target network number\n    Destination     *btypes.Address // Specific destination (optional)\n}\n```\n\n### Property Reading\n\n```go\n// Read a single property from a device\nresult, err := client.ReadProperty(device, btypes.PropertyData{\n    Object: btypes.Object{\n        ID: btypes.ObjectID{\n            Type:     btypes.AnalogInput,\n            Instance: 1,\n        },\n        Properties: []btypes.Property{\n            {\n                Type:       btypes.PropPresentValue,\n                ArrayIndex: btypes.ArrayAll,\n            },\n        },\n    },\n})\n\n// Read multiple properties from multiple objects\nresult, err := client.ReadMultiProperty(device, btypes.MultiplePropertyData{\n    Objects: []btypes.Object{\n        {\n            ID: btypes.ObjectID{Type: btypes.AnalogInput, Instance: 1},\n            Properties: []btypes.Property{\n                {Type: btypes.PropPresentValue},\n                {Type: btypes.PropUnits},\n            },\n        },\n        {\n            ID: btypes.ObjectID{Type: btypes.AnalogInput, Instance: 2},\n            Properties: []btypes.Property{\n                {Type: btypes.PropPresentValue},\n            },\n        },\n    },\n})\n```\n\n### Property Writing\n\n```go\n// Write a property to a device\nerr := client.WriteProperty(device, btypes.PropertyData{\n    Object: btypes.Object{\n        ID: btypes.ObjectID{\n            Type:     btypes.AnalogOutput,\n            Instance: 1,\n        },\n        Properties: []btypes.Property{\n            {\n                Type:       btypes.PropPresentValue,\n                ArrayIndex: btypes.ArrayAll,\n                Data:       float64(25.5),\n                Priority:   btypes.Normal,\n            },\n        },\n    },\n})\n```\n\n## Internal Architecture \u0026 Call Flow\n\n### 1. Client Initialization Flow\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     NewClient()                                 │\n├─────────────────────────────────────────────────────────────────┤\n│  1. Validate IP Address                                         │\n│     └─ validation.ValidIP(ip)                                   │\n│                                                                 │\n│  2. Validate Port (default: 47808)                              │\n│     └─ validation.ValidPort(port)                               │\n│                                                                 │\n│  3. Create DataLink Layer                                       │\n│     ├─ NewUDPDataLink(iface, port)         // by interface      │\n│     └─ NewUDPDataLinkFromIP(ip, subnet, port) // by IP          │\n│                                                                 │\n│  4. Initialize TSM (Transaction State Machine)                  │\n│     └─ tsm.New(defaultStateSize)                                │\n│                                                                 │\n│  5. Initialize UTSM (Unconfirmed TSM)                           │\n│     └─ utsm.NewManager(...)                                     │\n│                                                                 │\n│  6. Create Buffer Pool                                          │\n│     └─ sync.Pool for receive buffers                            │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 2. WhoIs Device Discovery Flow\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     WhoIs()                                     │\n├─────────────────────────────────────────────────────────────────┤\n│  1. Determine Broadcast Destination                             │\n│     ├─ GetBroadcastAddress()                                    │\n│     └─ Override with custom destination if provided             │\n│                                                                 │\n│  2. Encode NPDU (Network Protocol Data Unit)                    │\n│     ├─ Version: 1                                               │\n│     ├─ Destination: Broadcast address                           │\n│     ├─ Source: Local address                                    │\n│     └─ ExpectingReply: false (broadcast)                        │\n│                                                                 │\n│  3. Encode WhoIs Service Data                                   │\n│     ├─ Low device ID                                            │\n│     └─ High device ID                                           │\n│                                                                 │\n│  4. Subscribe to UTSM for IAm responses                         │\n│     └─ utsm.Subscribe(start, end)                               │\n│                                                                 │\n│  5. Send Broadcast Request (async)                              │\n│     └─ go c.Send(dest, npdu, data, nil)                         │\n│                                                                 │\n│  6. Collect and Deduplicate Responses                           │\n│     ├─ Filter IAm responses                                     │\n│     ├─ Deduplicate by device instance ID                        │\n│     └─ Build device list                                        │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 3. ReadProperty Flow (Confirmed Service)\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     ReadProperty()                              │\n├─────────────────────────────────────────────────────────────────┤\n│  1. Get Transaction ID from TSM                                 │\n│     └─ tsm.ID(ctx)                                              │\n│                                                                 │\n│  2. Build NPDU                                                  │\n│     ├─ Destination: Device address                              │\n│     ├─ Source: Local address                                    │\n│     └─ ExpectingReply: true                                     │\n│                                                                 │\n│  3. Encode APDU (Confirmed Service Request)                     │\n│     ├─ DataType: ConfirmedServiceRequest                        │\n│     ├─ Service: ServiceConfirmedReadProperty                    │\n│     ├─ InvokeId: Transaction ID                                 │\n│     └─ Service Data: Object ID + Property ID                    │\n│                                                                 │\n│  4. Send Request with Retry                                     │\n│     ├─ c.Send(dest, npdu, data, nil)                            │\n│     ├─ tsm.Receive(id, timeout)                                 │\n│     └─ Retry up to retryCount times                             │\n│                                                                 │\n│  5. Decode Response                                             │\n│     ├─ Decode APDU header                                       │\n│     ├─ Check for errors                                         │\n│     └─ Decode property value                                    │\n│                                                                 │\n│  6. Release Transaction ID                                      │\n│     └─ tsm.Put(id)                                              │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 4. WriteProperty Flow (Confirmed Service)\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     WriteProperty()                             │\n├─────────────────────────────────────────────────────────────────┤\n│  1. Get Transaction ID from TSM                                 │\n│     └─ tsm.ID(ctx)                                              │\n│                                                                 │\n│  2. Build NPDU                                                  │\n│     ├─ Destination: Device address                              │\n│     ├─ Source: Local address                                    │\n│     └─ ExpectingReply: true                                     │\n│                                                                 │\n│  3. Encode APDU (Confirmed Service Request)                     │\n│     ├─ DataType: ConfirmedServiceRequest                        │\n│     ├─ Service: ServiceConfirmedWriteProperty                   │\n│     ├─ InvokeId: Transaction ID                                 │\n│     └─ Service Data: Object ID + Property ID + Value            │\n│                                                                 │\n│  4. Send Request with Retry                                     │\n│     ├─ c.Send(dest, npdu, data, nil)                            │\n│     ├─ tsm.Receive(id, timeout)                                 │\n│     └─ Retry up to 2 times                                      │\n│                                                                 │\n│  5. Decode Response                                             │\n│     ├─ Decode APDU header                                       │\n│     ├─ Check for SimpleAck (success)                            │\n│     └─ Check for Error PDU                                      │\n│                                                                 │\n│  6. Release Transaction ID                                      │\n│     └─ tsm.Put(id)                                              │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 5. Message Reception Flow\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     ClientRun()                                 │\n├─────────────────────────────────────────────────────────────────┤\n│  Loop:                                                          │\n│  1. Get buffer from pool                                        │\n│     └─ readBufferPool.Get()                                     │\n│                                                                 │\n│  2. Receive data from DataLink                                  │\n│     └─ dataLink.Receive(buffer)                                 │\n│                                                                 │\n│  3. Handle message concurrently                                 │\n│     └─ go handleMsg(addr, data)                                 │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     handleMsg()                                 │\n├─────────────────────────────────────────────────────────────────┤\n│  1. Decode BVLC (BACnet Virtual Link Control)                   │\n│     ├─ Type: BVLCTypeBacnetIP                                   │\n│     ├─ Function: Broadcast/Unicast/ForwardedNPDU                │\n│     └─ Length: Packet length                                    │\n│                                                                 │\n│  2. Decode NPDU                                                 │\n│     ├─ Version                                                  │\n│     ├─ Source/Destination addresses                             │\n│     └─ Network layer message handling                           │\n│                                                                 │\n│  3. Decode APDU and Route to Handler                            │\n│     ├─ UnconfirmedServiceRequest                                │\n│     │   ├─ IAm → utsm.Publish()                                 │\n│     │   └─ WhoIs → (ignore or respond)                          │\n│     ├─ SimpleAck → tsm.Send()                                   │\n│     ├─ ComplexAck → tsm.Send()                                  │\n│     ├─ ConfirmedServiceRequest → tsm.Send()                     │\n│     └─ Error → tsm.Send(error)                                  │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 6. Transaction State Machine (TSM)\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                     TSM Architecture                            │\n├─────────────────────────────────────────────────────────────────┤\n│                                                                 │\n│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐      │\n│   │   Caller    │────▶│  TSM.ID()   │────▶│   State     │      │\n│   │  (Request)  │     │  (Get ID)   │     │  (Active)   │      │\n│   └─────────────┘     └─────────────┘     └──────┬──────┘      │\n│                                                  │              │\n│                                                  ▼              │\n│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐      │\n│   │   Caller    │◀────│ TSM.Put()   │◀────│   State     │      │\n│   │  (Cleanup)  │     │ (Release)   │     │ (Complete)  │      │\n│   └─────────────┘     └─────────────┘     └─────────────┘      │\n│                                                  ▲              │\n│                                                  │              │\n│   ┌─────────────┐     ┌─────────────┐     ┌─────────────┐      │\n│   │   handleMsg │────▶│ TSM.Send()  │────▶│   Data      │      │\n│   │  (Response) │     │ (Deliver)   │     │  Channel    │      │\n│   └─────────────┘     └─────────────┘     └─────────────┘      │\n│                                                                 │\n│  Key Components:                                                │\n│  - states: map[int]*state (active transactions)                 │\n│  - free.id: channel (available invoke IDs 1-254)                │\n│  - free.space: channel (concurrent transaction limit)           │\n│  - pool: sync.Pool (state object reuse)                         │\n│                                                                 │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### 7. Protocol Stack Layering\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                    Application Layer                            │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │  Services: WhoIs, IAm, ReadProperty, WriteProperty      │   │\n│  │  Objects: Device, AnalogInput, BinaryOutput, etc.       │   │\n│  └─────────────────────────────────────────────────────────┘   │\n├─────────────────────────────────────────────────────────────────┤\n│                   Presentation Layer                            │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │  Encoder: APDU, NPDU, BVLC encoding                     │   │\n│  │  Decoder: APDU, NPDU, BVLC decoding                     │   │\n│  │  Types: ObjectID, Property, Address                     │   │\n│  └─────────────────────────────────────────────────────────┘   │\n├─────────────────────────────────────────────────────────────────┤\n│                     Network Layer                               │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │  NPDU: Network Protocol Data Unit                       │   │\n│  │  - Source/Destination addressing                        │   │\n│  │  - Hop count, priority, network numbers                 │   │\n│  └─────────────────────────────────────────────────────────┘   │\n├─────────────────────────────────────────────────────────────────┤\n│                   Data Link Layer                               │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │  BVLC: BACnet Virtual Link Control                      │   │\n│  │  UDP:  UDP socket communication                         │   │\n│  │  MS/TP: Master-Slave/Token-Pass (optional)              │   │\n│  └─────────────────────────────────────────────────────────┘   │\n├─────────────────────────────────────────────────────────────────┤\n│                    Physical Layer                               │\n│  ┌─────────────────────────────────────────────────────────┐   │\n│  │  Ethernet/IP network                                      │   │\n│  └─────────────────────────────────────────────────────────┘   │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Supported BACnet Services\n\n### Confirmed Services\n- ReadProperty (12)\n- ReadPropertyMultiple (14)\n- WriteProperty (15)\n- WritePropertyMultiple (16)\n\n### Unconfirmed Services\n- IAm (0)\n- WhoIs (8)\n\n## Object Types\n\n| Type | Code | Description |\n|------|------|-------------|\n| AnalogInput | 0 | Analog input point |\n| AnalogOutput | 1 | Analog output point |\n| AnalogValue | 2 | Analog value |\n| BinaryInput | 3 | Binary input point |\n| BinaryOutput | 4 | Binary output point |\n| BinaryValue | 5 | Binary value |\n| Device | 8 | BACnet device |\n| MultiStateInput | 13 | Multi-state input |\n| MultiStateOutput | 14 | Multi-state output |\n| TrendLog | 20 | Trend log |\n\n## Property Types (Common)\n\n| Property | Code | Description |\n|----------|------|-------------|\n| PresentValue | 85 | Current value of the object |\n| Units | 117 | Engineering units |\n| Description | 28 | Object description |\n| ObjectName | 77 | Object name |\n| ObjectType | 79 | Object type |\n| ObjectIdentifier | 75 | Object identifier |\n| ObjectList | 76 | List of objects in device |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────┐\n│                     Application Layer                       │\n│  WhoIs | ReadProperty | WriteProperty | Objects            │\n└──────────────────────────┬──────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────┐\n│                     Presentation Layer                      │\n│           Encoder / Decoder (APDU/NPDU/BVLC)               │\n└──────────────────────────┬──────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────┐\n│                      Network Layer                          │\n│              Addressing, Routing, Priority                  │\n└──────────────────────────┬──────────────────────────────────┘\n                           │\n                           ▼\n┌─────────────────────────────────────────────────────────────┐\n│                   Data Link Layer                          │\n│                    UDP / MS/TP                             │\n└─────────────────────────────────────────────────────────────┘\n```\n\n## Transaction Management\n\n### TSM (Transaction State Machine)\nHandles confirmed services that require acknowledgment. Uses channel-based communication for request/response matching.\n\n### UTSM (Unconfirmed Transaction State Machine)\nHandles unconfirmed services like WhoIs/IAm. Uses publish/subscribe pattern for broadcast responses.\n\n## Configuration\n\n### ClientBuilder Options\n\n```go\ntype ClientBuilder struct {\n    DataLink   datalink.DataLink // Custom data link (optional)\n    Interface  string            // Network interface name (e.g., \"eth0\")\n    Ip         string            // IP address\n    Port       int               // BACnet port (default: 47808)\n    SubnetCIDR int               // Subnet CIDR (e.g., 24 for /24)\n    MaxPDU     uint16            // Maximum PDU size (default: 1476)\n}\n```\n\n## Constants\n\n```go\n// Protocol\nconst DefaultPort = 0xBAC0 // 47808\nconst MaxAPDU = 1476\n\n// Network\nconst GlobalBroadcast = 0xFFFF\nconst DefaultHopCount = 255\n\n// Priorities\nconst (\n    LifeSafety        = 3\n    CriticalEquipment = 2\n    Urgent            = 1\n    Normal            = 0\n)\n```\n\n## Usage Notes\n\n### Network Considerations\n\n1. **Port Binding**: \n   - Default BACnet port is 47808 (0xBAC0)\n   - When testing with a simulator on the same machine, use different ports for discovery and confirmed services to avoid port conflicts\n   - Example: Use port 47808 for WhoIs discovery, then switch to 47809 for ReadProperty/WriteProperty operations\n\n2. **IP Address Binding**:\n   - Bind to `0.0.0.0` to listen on all interfaces\n   - Avoid binding to the target device's IP address\n   - For multi-subnet environments, ensure proper subnet CIDR configuration\n\n3. **Broadcast Behavior**:\n   - WhoIs uses broadcast by default\n   - Use `WhoIsOpts.Destination` for unicast WhoIs requests\n   - Broadcast may not work across VLANs or subnets\n\n### Error Handling\n\n1. **Timeout Handling**:\n   - Use `ReadPropertyWithTimeout` for explicit timeout control\n   - Confirmed services include retry logic with exponential backoff\n   - Unconfirmed services have no retry mechanism\n\n2. **Common Errors**:\n   - `timeout`: Device did not respond within the timeout period\n   - `invalid argument`: Invalid object type or property ID\n   - `no such object`: Requested object does not exist on the device\n   - `access denied`: Insufficient permissions for write operations\n\n3. **Retry Strategy**:\n   - Confirmed services are retried up to 2 times by default\n   - Consider implementing application-level retry for critical operations\n\n### Performance Considerations\n\n1. **Batch Operations**:\n   - Use `ReadMultiProperty` for reading multiple properties at once\n   - This reduces network round-trips and improves performance\n   - Limit batch size based on device's MaxAPDU setting\n\n2. **Concurrency**:\n   - Client is thread-safe for concurrent operations\n   - TSM limits concurrent confirmed transactions (default: 10)\n   - Consider rate limiting for high-frequency operations\n\n3. **Memory Management**:\n   - Use buffer pool for efficient memory usage\n   - Release resources promptly with `client.Close()`\n\n### Best Practices\n\n1. **Client Lifecycle**:\n   - Always use `defer client.Close()` to release resources\n   - Start `client.ClientRun()` in a goroutine before making requests\n   - Verify `client.IsRunning()` before sending requests\n\n2. **Device Communication**:\n   - Cache device addresses after discovery\n   - Validate device responses for expected data types\n   - Handle device reboots and network interruptions gracefully\n\n3. **Testing**:\n   - Use the provided integration test (`TestRealDeviceAcceptanceFlow`) for validation\n   - Test with both real devices and simulators\n   - Monitor response times and error rates in production\n\n## Testing\n\n```bash\n# Run all tests\ngo test ./...\n\n# Run specific test\ngo test -v ./network/...\n\n# Run acceptance tests\ngo test -v -run Acceptance\n\n# Run real device integration test\ngo test . -run TestRealDeviceAcceptanceFlow -count=1 -v\n```\n\n## License\n\nMIT License\n\n## References\n\n- [ANSI/ASHRAE Standard 135-2020](https://www.ashrae.org/standards-research/standards/ashrae-standard-135)\n- [BACnet Protocol Specification](http://www.bacnet.org/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanviod%2Fbacnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanviod%2Fbacnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanviod%2Fbacnet/lists"}