Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 19 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,26 @@ jobs:
- uses: actions/download-artifact@v4
with:
path: artifacts
# Every platform artifact carries the same docs (README.md, LICENSE, ...).
# Uploading two assets with one name makes the release action fail part-way
# and leaves a draft release behind (a real tagged release hit this). Flatten the
# artifacts into one folder with a single copy of each file name, and stop
# if two files share a name but differ.
- name: Collect release assets (one copy of each file name)
shell: bash
run: |
mkdir -p release
find artifacts -type f | sort | while read -r f; do
name=$(basename "$f")
if [ -e "release/$name" ]; then
cmp -s "$f" "release/$name" || { echo "FAIL: two different files are both named $name ($f)"; exit 1; }
else
cp "$f" "release/$name"
fi
done
ls -l release
- uses: softprops/action-gh-release@v2
with:
name: ${{ github.ref_name }}
generate_release_notes: true
files: artifacts/**/*
files: release/*
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ up/down nudge Analog Input 1.

## Conventions

- Device is named "Rainbow"; objects use the series' colour names; vendor id
- Device is named "Chipkin Example B-SS"; objects use the series' colour names; vendor id
389.
- Implement **only** the services and objects the B-SS profile requires -
but expose **every required property** of each object.
Expand Down
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.2] - unreleased

### Changed

- **Device renamed from the series' colour placeholder "Rainbow" to "Chipkin
Example B-SS"** so devices from different examples in the series are
distinguishable from each other on the same BACnet network - every example
previously announced the identical Object_Name "Rainbow", which made two
examples on one subnet indistinguishable by name. Sub-object names are
unchanged. `docs/colour-table.md` (series root) updated to match.
APP_VERSION bumped 1.0.1 -> 1.0.2.

## [1.0.1] - unreleased

### Fixed

- **`Application_Software_Version` (12) and `Firmware_Revision` (44) were
hardcoded to `"1.0.0"` and never updated as the example's real version
advanced** - the same issue found and fixed in
[BACnetProfileExample-B-SCHUB-CPP](https://github.com/chipkin/BACnetProfileExample-B-SCHUB-CPP)
v1.1.13 via a real device read with CAS BACnet Explorer.
`Application_Software_Version` now returns `APP_VERSION` directly (one
source of truth, can't drift from `--version`'s own banner again).
`Firmware_Revision` is now built once at start-up, right after
`CASExampleHelper.PrintVersion()` makes the first successful native stack
call, from the CAS BACnet Stack's own
`BACnetStack_GetAPIMajorVersion()`/`GetAPIMinorVersion()`/
`GetAPIPatchVersion()`/`GetAPIBuildVersion()` (the same 4 calls
`PrintVersion()` already uses for the start-up banner) - it names the
underlying platform, not this app. Verified with a real ReadProperty
(`bacpypes3`) against the running device:
`Application_Software_Version = "1.0.1"`,
`Firmware_Revision = "6.0.21.0"`.

## [1.0.0] - unreleased

### Added
Expand Down
40 changes: 30 additions & 10 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
// object that every BACnet device must have. Each object is given a colour name
// (a convention shared across this example series):
//
// Device 389001 "Rainbow" (instance configurable with --deviceID)
// Device 389001 "Chipkin Example B-SS" (instance configurable with --deviceID)
// Analog Input 1 "Bronze" (REAL, degrees Celsius; starts at 21.5)
// Binary Input 1 "Emerald" (active / inactive)
// Multi-State Input 1 "Hot Pink" (an enumerated state, 1..3)
Expand Down Expand Up @@ -76,7 +76,7 @@ internal static unsafe class Program
// 1. Example + device configuration
// -------------------------------------------------------------------
private const string APP_NAME = "BACnet B-SS (Smart Sensor) Example - C#";
private const string APP_VERSION = "1.0.0";
private const string APP_VERSION = "1.0.2";

// The device instance. BACnet requires this to be configurable, so it
// defaults to 389001 and can be overridden on the command line with
Expand Down Expand Up @@ -105,11 +105,11 @@ internal static unsafe class Program
// the whole BACnet internetwork, and here it is a COMPILE-TIME constant.
// The device instance is runtime-configurable via --deviceID, so it is
// easy to ship two units, configure their instances correctly, and still
// have BOTH announce Object_Name "Rainbow" - a spec violation, and a hard
// have BOTH announce Object_Name "Chipkin Example B-SS" - a spec violation, and a hard
// BTL failure. In a real product Object_Name must be per-unit
// configurable too: derive it from a serial number, DIP switches, a
// config file, or add a --deviceName argument.
private const string DEVICE_NAME = "Rainbow";
private const string DEVICE_NAME = "Chipkin Example B-SS";

// The Device object's Description. Change it to what YOUR device
// actually is; this string describes this tutorial.
Expand All @@ -121,13 +121,22 @@ internal static unsafe class Program
// VENDOR_NAME - your company name; it must match VENDOR_IDENTIFIER above.
// MODEL_NAME - your model designation. This is what a building operator
// reads to identify your device in a discovery tool.
// FIRMWARE_REVISION / APPLICATION_SOFTWARE_VERSION - your real
// versions. Wire them to your build rather than
// hard-coding a number that will go stale.
// APPLICATION_SOFTWARE_VERSION - this example's own real version;
// sourced directly from APP_VERSION above, so it can't
// drift from what --version reports.
// g_firmwareRevision - the underlying CAS BACnet Stack's REAL
// version (it names the platform underneath this app,
// not the app itself). Built once at start-up, right
// after the native library is confirmed loaded - see
// Main(). Not a compile-time constant because it is
// read from the stack at runtime via the same
// BACnetStack_GetAPI*Version() calls
// CASExampleHelper.PrintVersion() uses for the
// start-up banner.
private const string VENDOR_NAME = "Chipkin Automation Systems";
private const string MODEL_NAME = "CAS BACnet Stack Example - B-SS";
private const string FIRMWARE_REVISION = "1.0.0";
private const string APPLICATION_SOFTWARE_VERSION = "1.0.0";
private const string APPLICATION_SOFTWARE_VERSION = APP_VERSION;
private static string g_firmwareRevision = "0.0.0.0";

// The sensor objects (all instance 1) and their colour names.
private const uint ANALOG_INPUT_INSTANCE = 1; // "Bronze"
Expand Down Expand Up @@ -489,7 +498,7 @@ private static bool GetPropertyCharacterString(
}
if (propertyIdentifier == CASBACnetStackAdapter.PROPERTY_IDENTIFIER_FIRMWAREREVISION)
{
return ReturnCharacterString(FIRMWARE_REVISION, value, valueElementCount, maxElementCount, encodingType);
return ReturnCharacterString(g_firmwareRevision, value, valueElementCount, maxElementCount, encodingType);
}
if (propertyIdentifier == CASBACnetStackAdapter.PROPERTY_IDENTIFIER_APPLICATIONSOFTWAREVERSION)
{
Expand Down Expand Up @@ -552,6 +561,17 @@ private static int Main(string[] args)
return 1;
}

// The native library is confirmed loaded at this point (PrintVersion()
// above made the first successful stack call). Build the Device
// object's Firmware_Revision from the stack's own real version - it
// reports the underlying CAS BACnet Stack, not this app - using the
// same 4 getter calls PrintVersion() already used for the banner.
g_firmwareRevision =
CASBACnetStackAdapter.BACnetStack_GetAPIMajorVersion() + "." +
CASBACnetStackAdapter.BACnetStack_GetAPIMinorVersion() + "." +
CASBACnetStackAdapter.BACnetStack_GetAPIPatchVersion() + "." +
CASBACnetStackAdapter.BACnetStack_GetAPIBuildVersion();

// --- Bind the BACnet/IP socket -------------------------------------
try
{
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ and who answers it.
## The device this example creates

```
Device 389001 "Rainbow" (Vendor 389 - Chipkin Automation Systems)
Device 389001 "Chipkin Example B-SS" (Vendor 389 - Chipkin Automation Systems)
│
├── Analog Input 1 "Bronze" Present_Value 21.5 (REAL, degrees Celsius; read-only)
├── Binary Input 1 "Emerald" Present_Value inactive (read-only)
Expand Down Expand Up @@ -82,7 +82,7 @@ which is the point of a profile example.

| Object type | Instance | Name |
|-------------|:--------:|------|
| Device | 389001 | Rainbow |
| Device | 389001 | Chipkin Example B-SS |
| Analog Input | 1 | Bronze |
| Binary Input | 1 | Emerald |
| Multi-state Input | 1 | Hot Pink |
Expand Down Expand Up @@ -223,7 +223,7 @@ Expected output:
BACnet B-SS (Smart Sensor) Example - C# v1.0.0
CAS BACnet Stack v6.0.21.0
FYI: listening on <your-ip>:47808 (broadcast <your-subnet-broadcast>)
FYI: Device 389001 ("Rainbow") ready. Vendor ID 389. Press 'h' for help.
FYI: Device 389001 ("Chipkin Example B-SS") ready. Vendor ID 389. Press 'h' for help.
```

The device listens on UDP **47808** (BACnet/IP) by default. Allow that port
Expand Down Expand Up @@ -264,10 +264,10 @@ Use a BACnet client such as the
instance **389001** (vendor **389**). It also broadcasts an I-Am at
start-up.
2. **Browse the object model** - the device shows five objects: the Device
(`Rainbow`), the three sensors, and the Network Port (`Vermilion`). Reading
(`Chipkin Example B-SS`), the three sensors, and the Network Port (`Vermilion`). Reading
the Device's `Object_List` returns all five.
3. **Read the Device** - ReadProperty `389001` -> `Object_Name` returns
`"Rainbow"`; `Protocol_Revision` returns `24`; `Description` returns the
`"Chipkin Example B-SS"`; `Protocol_Revision` returns `24`; `Description` returns the
profile description string.
4. **Read a sensor** - ReadProperty Analog Input `1` -> `Present_Value`
returns `21.5`; `Units` returns `degrees-Celsius`; `Out_Of_Service` returns
Expand Down
4 changes: 2 additions & 2 deletions docs/PICS.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ is writable.

| Object type | Instance | Object_Name | Optional properties supported |
|---|:---:|---|---|
| Device | 389001 | Rainbow | Description |
| Device | 389001 | Chipkin Example B-SS | Description |
| Analog Input | 1 | Bronze | - |
| Binary Input | 1 | Emerald | - |
| Multi-State Input | 1 | Hot Pink | State_Text |
Expand Down Expand Up @@ -119,7 +119,7 @@ handle data in all character sets.
<!-- OBJECTS-PROPERTIES:BEGIN (generated by tools/gen-objects-properties.py from docs/objects.json - do not edit here) -->
Every object this example creates, and every REQUIRED property of each (per ANSI/ASHRAE 135-2024 clause 12 and the stack's `docs/property-profile-reference.md`), plus the optional properties the example turns on. **Served by** says who answers a ReadProperty: the **stack** generates it, or the **app** serves it from a `GetProperty*` callback in `Program.cs`. A ⚠ row is a required property the app does not serve and the stack would fill with a default - that is a defect, not a feature.

### Device 389001 "Rainbow" - the device itself; the instance is configurable with --deviceID. The stack rows are device-wide facts only the stack knows - the protocol version and revision it implements, the services and object types it was configured with, the live object list and address-binding table. The accepted rows are the stack's configured defaults for APDU limits, segmentation, system status and database revision; an application that answered them from its own constants could contradict the stack, so this example does not
### Device 389001 "Chipkin Example B-SS" - the device itself; the instance is configurable with --deviceID. The stack rows are device-wide facts only the stack knows - the protocol version and revision it implements, the services and object types it was configured with, the live object list and address-binding table. The accepted rows are the stack's configured defaults for APDU limits, segmentation, system status and database revision; an application that answered them from its own constants could contradict the stack, so this example does not

| Property | Datatype | Served by | Writable |
|---|---|---|:---:|
Expand Down
2 changes: 1 addition & 1 deletion docs/objects.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"type": "Device",
"instance": 389001,
"name": "Rainbow",
"name": "Chipkin Example B-SS",
"app": [
"Object_Name",
"Description",
Expand Down
Loading