# az-device-contract-codegen

Generate deterministic Modbus contract artifacts from typed service definitions.

- Crate: `az-device-contract-codegen`
- Local path: `creates/az-device-contract-codegen`
- Install: `cargo add az-device-contract-codegen`

The crate plans a Modbus address layout, then emits a stable artifact bundle for:

- C headers and sources for RTU/TCP handlers
- Markdown protocol documents
- Rust client code built around `tokio-modbus`

## Minimal usage

```rust
use az_device_contract_codegen::{
    generate_bundle, ApiKind, ContractMethod, ContractRenderRequest, ContractService,
    ContractTypedItem, ReadReturnKind, TransportKind, ValueType,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let request = ContractRenderRequest {
        transports: vec![TransportKind::Rtu, TransportKind::Tcp],
        services: vec![ContractService {
            api_kind: ApiKind::RuntimeRead,
            interface_name: "DeviceReadApi".into(),
            interface_summary: Some("Runtime reads".into()),
            methods: vec![ContractMethod {
                summary: Some("Read signal lights".into()),
                method_name: "getSignalLights".into(),
                read_return_kind: Some(ReadReturnKind::Dto),
                read_return_type_name: Some("SignalLights".into()),
                read_area: None,
                write_area: None,
                read_fields: vec![
                    ContractTypedItem {
                        name: "ch1".into(),
                        summary: None,
                        value_type: ValueType::Boolean,
                    },
                    ContractTypedItem {
                        name: "ch2".into(),
                        summary: None,
                        value_type: ValueType::Boolean,
                    },
                ],
                parameters: vec![],
            }],
        }],
    };

    let bundle = generate_bundle(&request)?;

    for artifact in &bundle.artifacts {
        println!("{}", artifact.relative_path.display());
    }
    Ok(())
}
```

Typical output paths:

- `Core/Inc/generated/modbus/*.h`
- `Core/Src/generated/modbus/**/*.c`
- `Docs/generated/modbus/protocols/*.md`
- `Docs/generated/modbus/rust/*.rs`

## Default mapping rules

- `STATIC_READ` defaults to input registers (`0x04`)
- `RUNTIME_READ`
  - all-boolean DTO/scalar reads default to discrete inputs (`0x02`)
  - everything else defaults to input registers (`0x04`)
- `WRITE`
  - all-boolean parameters default to coils (`0x05` / `0x0F`)
  - everything else defaults to holding registers (`0x06` / `0x10`)

`readArea` and `writeArea` can override those defaults explicitly.

## Notes

- The crate itself does not write files; it returns `GeneratedArtifact` values.
- Generated Rust clients assume the consumer compiles them with `tokio-modbus` and `serialport`.
- Generated filenames and helper symbols currently use the existing `okm_*` naming line used by this repository.
