bacnet-rs 0.3.0

BACnet protocol stack implementation in Rust
Documentation
# Communication Examples

BACnet device-to-device communication patterns and client implementations.

## Examples

### `test_client.rs`
**Purpose**: BACnet client for testing communication with remote devices

**Features**:
- Confirmed service request handling
- Error response processing
- Timeout and retry mechanisms
- Service choice implementations
- Response validation and parsing

**Usage**:
```bash
cargo run --example communication/test_client
```

**What it demonstrates**:
- Client-side BACnet communication
- Confirmed service patterns
- Error handling strategies
- Response processing workflows
- Communication reliability patterns

## Communication Patterns

### Request-Response Pattern
```
Client                          Server
  │                              │
  ├─ Confirmed Request ─────────→│
  │                              ├─ Process Request
  │                              ├─ Generate Response
  │←─────── Complex Ack ─────────┤
  │                              │
  └─ Process Response            │
```

### Error Handling
```
Client                          Server
  │                              │
  ├─ Confirmed Request ─────────→│
  │                              ├─ Detect Error
  │←────── Error Response ───────┤
  │                              │
  ├─ Handle Error                │
  ├─ Retry Logic                 │
  └─ Alternative Action          │
```

## Service Categories

### Read Services
- **ReadProperty**: Single property read
- **ReadPropertyMultiple**: Batch property read
- **ReadRange**: Historical data reading

### Write Services
- **WriteProperty**: Single property write
- **WritePropertyMultiple**: Batch property write

### Object Services
- **CreateObject**: Dynamic object creation
- **DeleteObject**: Object removal

### File Services
- **AtomicReadFile**: File data reading
- **AtomicWriteFile**: File data writing

### Alarm & Event Services
- **AcknowledgeAlarm**: Alarm acknowledgment
- **GetAlarmSummary**: Active alarm retrieval
- **GetEventInformation**: Event status reading

## Client Implementation Patterns

### Basic Client Structure
```rust
use bacnet_rs::{
    client::BacnetClient,
    object::{ObjectIdentifier, PropertyIdentifier},
    service::ConfirmedServiceChoice,
};

struct MyBacnetClient {
    client: BacnetClient,
    target_device: SocketAddr,
    timeout: Duration,
}

impl MyBacnetClient {
    pub fn read_property(&self, 
        object_id: ObjectIdentifier, 
        property: PropertyIdentifier
    ) -> Result<PropertyValue> {
        // Implementation
    }
    
    pub fn write_property(&self,
        object_id: ObjectIdentifier,
        property: PropertyIdentifier, 
        value: PropertyValue
    ) -> Result<()> {
        // Implementation
    }
}
```

### Batch Operations
```rust
// Read multiple properties efficiently
let properties = vec![
    (ai1_id, PropertyIdentifier::PresentValue),
    (ai2_id, PropertyIdentifier::PresentValue),
    (av1_id, PropertyIdentifier::PresentValue),
];

let values = client.read_properties_batch(&properties)?;
```

### Error Recovery
```rust
fn robust_read_property(
    client: &BacnetClient,
    object_id: ObjectIdentifier,
    property: PropertyIdentifier,
) -> Result<PropertyValue> {
    let mut attempts = 0;
    let max_attempts = 3;
    
    loop {
        match client.read_property(object_id, property) {
            Ok(value) => return Ok(value),
            Err(e) if attempts < max_attempts => {
                attempts += 1;
                thread::sleep(Duration::from_millis(100 * attempts));
                continue;
            }
            Err(e) => return Err(e),
        }
    }
}
```

## Service Request Examples

### ReadProperty Request
```rust
use bacnet_rs::service::{ReadPropertyRequest, ConfirmedServiceChoice};

let request = ReadPropertyRequest::new(
    ObjectIdentifier::new(ObjectType::AnalogInput, 1),
    PropertyIdentifier::PresentValue,
    None, // No array index
);

let response = client.send_confirmed_request(
    target_addr,
    ConfirmedServiceChoice::ReadProperty,
    &request.encode()?
)?;
```

### WriteProperty Request
```rust
use bacnet_rs::service::WritePropertyRequest;

let request = WritePropertyRequest::new(
    ObjectIdentifier::new(ObjectType::AnalogValue, 1),
    PropertyIdentifier::PresentValue,
    None, // No array index
    PropertyValue::Real(23.5),
    Some(8), // Priority 8
);

client.send_confirmed_request(
    target_addr,
    ConfirmedServiceChoice::WriteProperty,
    &request.encode()?
)?;
```

## Testing Scenarios

### Local Testing
```bash
# Start a responder device
cargo run --example basic/responder_device 12345 &

# Test client communication
cargo run --example communication/test_client

# Cleanup
kill %1
```

### Remote Device Testing
```bash
# Test with real BACnet device
cargo run --example communication/test_client

# Configure target in source code:
let target_addr = "192.168.1.100:47808".parse()?;
```

### Error Simulation
```bash
# Test timeout handling
cargo run --example communication/test_client

# Test with unreachable device:
let target_addr = "192.168.999.999:47808".parse()?;
```

## Performance Optimization

### Connection Pooling
```rust
use std::collections::HashMap;

struct ConnectionPool {
    clients: HashMap<SocketAddr, BacnetClient>,
    max_connections: usize,
}

impl ConnectionPool {
    fn get_client(&mut self, addr: SocketAddr) -> &mut BacnetClient {
        self.clients.entry(addr).or_insert_with(|| {
            BacnetClient::new(addr)
        })
    }
}
```

### Request Batching
```rust
// Batch multiple requests for efficiency
struct BatchRequest {
    requests: Vec<(ObjectIdentifier, PropertyIdentifier)>,
    target: SocketAddr,
}

impl BatchRequest {
    fn execute(&self, client: &BacnetClient) -> Vec<Result<PropertyValue>> {
        // Use ReadPropertyMultiple for efficiency
        let rpm_request = ReadPropertyMultipleRequest::from_requests(&self.requests);
        client.send_rpm_request(self.target, rpm_request)
    }
}
```

### Async Communication (Future)
```rust
// Future async implementation
use tokio::time::timeout;

async fn async_read_property(
    client: &AsyncBacnetClient,
    object_id: ObjectIdentifier,
    property: PropertyIdentifier,
) -> Result<PropertyValue> {
    timeout(
        Duration::from_secs(5),
        client.read_property_async(object_id, property)
    ).await?
}
```

## Integration with Other Examples

### With Object Database
```rust
// Sync remote device with local database
fn sync_device_properties(
    client: &BacnetClient,
    database: &ObjectDatabase,
    remote_addr: SocketAddr,
) -> Result<()> {
    let objects = database.get_all_objects();
    
    for obj_id in objects {
        if let Ok(remote_value) = client.read_property(
            remote_addr, 
            obj_id, 
            PropertyIdentifier::PresentValue
        ) {
            database.set_property(obj_id, PropertyIdentifier::PresentValue, remote_value)?;
        }
    }
    
    Ok(())
}
```

### With Networking Examples
```rust
// Discover and communicate with all devices
let discovered_devices = whois_scanner.discover_devices()?;

for device in discovered_devices {
    let client = BacnetClient::new();
    let properties = client.read_device_properties(device.address)?;
    println!("Device {}: {:?}", device.device_id, properties);
}
```

## Error Codes and Handling

### Common BACnet Error Classes
- **Device**: Device-related errors (offline, busy)
- **Object**: Object not found or invalid
- **Property**: Property access errors
- **Resources**: Resource exhaustion
- **Security**: Access denied, authentication
- **Services**: Service not supported

### Error Response Processing
```rust
match error_response {
    BacnetError::Device(DeviceError::DeviceBusy) => {
        // Retry after delay
        thread::sleep(Duration::from_secs(1));
        retry_request()?;
    }
    BacnetError::Object(ObjectError::UnknownObject) => {
        // Object doesn't exist, skip
        continue;
    }
    BacnetError::Property(PropertyError::WriteAccessDenied) => {
        // Property is read-only
        return Err("Cannot write to read-only property".into());
    }
    _ => return Err(error_response.into()),
}
```

## Security Considerations

### Access Control
- Validate device credentials
- Implement authentication where required
- Use secure communication channels when available
- Log all communication attempts

### Rate Limiting
```rust
use std::time::{Duration, Instant};

struct RateLimiter {
    last_request: Instant,
    min_interval: Duration,
}

impl RateLimiter {
    fn check_rate_limit(&mut self) -> Result<()> {
        let elapsed = self.last_request.elapsed();
        if elapsed < self.min_interval {
            thread::sleep(self.min_interval - elapsed);
        }
        self.last_request = Instant::now();
        Ok(())
    }
}
```

This communication framework provides robust, efficient, and reliable BACnet client functionality for production use.