hammermaps/php-bacnet

BACnet/IP extension for PHP

Maintainers

Package info

github.com/hammermaps/php-bacnet

Language:C

Type:php-ext

Ext name:ext-bacnet

pkg:composer/hammermaps/php-bacnet

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 1

v0.1.3 2026-08-22 00:12 UTC

This package is auto-updated.

Last update: 2026-08-22 01:16:11 UTC


README

PHP Version bacnet-stack License

PHP-Elefant und BACnet-Netzwerk

php-bacnet ist eine native C-Erweiterung für PHP 8.4 und 8.5, die vollständige BACnet/IP-Kommunikation bereitstellt. Sie kapselt die bacnet-stack-Bibliothek und stellt eine typsichere, objektorientierte API im Namensraum Bacnet\ bereit.

Vollständige API-Dokumentation: docs/api-reference.md
Build & Installation: docs/installation.md PIE: docs/pie.md

Überblick

BACnet (Building Automation and Control Networks) ist der international standardisierte Kommunikationsstandard für Gebäudeautomation (ASHRAE 135 / ISO 16484-5). Diese Erweiterung ermöglicht PHP-Anwendungen, als vollständige BACnet/IP-Knoten zu agieren:

Modus Beschreibung
Client Geräte per Who-Is/I-Am entdecken, Eigenschaften lesen und schreiben
Server Eigene BACnet-Objekte bereitstellen, Callbacks für Read/Write-Anfragen
Mixed Server und Client über einen gemeinsamen UDP-Socket betreiben

Unterstützte Objekttypen

Objekttyp ID Beschreibung
ANALOG_INPUT 0 Analoger Messwert (Sensor)
ANALOG_OUTPUT 1 Analoger Ausgang (Aktor)
ANALOG_VALUE 2 Analoger Softwarewert / Sollwert
BINARY_INPUT 3 Binärer Eingang
BINARY_OUTPUT 4 Binärer Ausgang
BINARY_VALUE 5 Binärer Softwarewert
DEVICE 8 Geräteobjekt
MULTI_STATE_INPUT 13 Mehrwertiger Eingang
MULTI_STATE_OUTPUT 14 Mehrwertiger Ausgang
MULTI_STATE_VALUE 19 Mehrwertiger Softwarewert
SCHEDULE 17 Zeitprogramm (WEEKLY_SCHEDULE lesen)
TREND_LOG 20 Aufzeichnungsobjekt (LOG_BUFFER lesen)

Features

  • Typsichere OOP-APIBacnet\Client, Bacnet\Device, Bacnet\ObjectRef, Bacnet\Server, Bacnet\MixedServer
  • Explizite BACnet-TypenValue::real(), Value::enumerated(), Value::characterString() usw.
  • Komplexe BACnet-DatentypenBitString, Date, Time, ObjectIdentifier
  • GeräteentdeckungwhoIs() mit optionalem Instanzbereich
  • COV-Subscriptions — entfernte Properties beobachten und Änderungen per onCovNotification() im PHP-Event-Loop empfangen
  • Mehrstufiger Client-Cache — POSIX Shared Memory als fester L1, LMDB als persistentes Standard-L2 und austauschbare PHP-Backends
  • Server-Modus — PHP-Callbacks für onReadProperty / onWriteProperty
  • Server-Schutz — ACLs, Token-Buckets, temporäre Quellsperren und Write-Deduplizierung
  • Mixed-Modus — Server und ausgehende Client-Anfragen über einen gemeinsamen UDP-Socket
  • Komfort-APIObjectRef::writePresentValue(), writeActive(), writeInactive()
  • INI-Konfiguration — Port, Timeout, Interface und Server-Sicherheitsgrenzen per php.ini
  • Kleine Laufzeitbasis — bacnet-stack wird statisch eingebettet; LMDB stellt die persistente Cache-Ebene bereit

Schnellstart

<?php
declare(strict_types=1);

// Geräte im Netzwerk entdecken
$client = new Bacnet\Client(interface: 'eth0', timeoutMs: 3000);

$devices = $client->whoIs();
foreach ($devices as $device) {
    printf("Gerät %d @ %s (MaxAPDU=%d)\n",
        $device->getDeviceId(),
        $device->getAddress(),
        $device->getMaxApdu(),
    );
}

// Eigenschaft lesen
[$device] = $client->whoIs(lowLimit: 1234, highLimit: 1234);

$temp = $device->readProperty(
    Bacnet\ObjectType::ANALOG_VALUE,
    1,
    Bacnet\Property::PRESENT_VALUE,
);
printf("Temperatur: %.1f °C\n", $temp);

// Eigenschaft schreiben
$device->writeProperty(
    Bacnet\ObjectType::ANALOG_VALUE,
    1,
    Bacnet\Property::PRESENT_VALUE,
    Bacnet\Value::real(22.0),
    priority: 16,
);

// Komfort-API via ObjectRef
$sensor = new Bacnet\ObjectRef($device, Bacnet\ObjectType::ANALOG_VALUE, 1);
$sensor->writePresentValue(23.5);  // Typ wird automatisch erkannt

$relay = new Bacnet\ObjectRef($device, Bacnet\ObjectType::BINARY_OUTPUT, 1);
$relay->writeActive();    // ENUMERATED(1)
$relay->writeInactive();  // ENUMERATED(0)

Client-Cache

Metadaten, Objektlisten und Discovery-Ergebnisse werden standardmäßig zuerst aus einem zwischen Prozessen geteilten C-Cache gelesen und zusätzlich in LMDB persistiert. /var/cache/php-bacnet muss für den PHP-Prozess existieren und beschreibbar sein. Dynamische Zustände und negative Ergebnisse bleiben zunächst ungecached.

$client->setCacheOptions(['state_enabled' => true, 'state_ttl' => 1.0]);
$freshDevices = $client->whoIs(refresh: true);
$stats = $client->getCacheStats();

Ein beliebiges Bacnet\CacheBackendInterface kann LMDB pro Instanz ersetzen, beispielsweise für Redis oder Memcached. Siehe Client-Cache.

Mixed-Modus

Ein MixedServer stellt lokale Objekte bereit und kann über denselben Socket andere Geräte entdecken, lesen und schreiben:

$mixed = new Bacnet\MixedServer(5, 'net3', 47808);
$mixed->addLocalObject(new Bacnet\ObjectIdentifier(
    Bacnet\ObjectType::ANALOG_VALUE,
    1,
));

[$device] = $mixed->whoIs(200, 200, 1000);
$value = $device->readProperty(
    Bacnet\ObjectType::ANALOG_VALUE,
    10110,
    Bacnet\Property::PRESENT_VALUE,
);

while (true) {
    $mixed->poll(100);
}

Während synchroner Client-Aufrufe werden eingehende Server-PDUs gepuffert und anschließend von poll() verarbeitet. Details und Event-Loop-Muster stehen in docs/mixed-mode.md.

Ausführbare Beispiele:

php examples/mixed_daemon.php
php examples/mixed_read.php 200 analog_value 10110 present_value
php examples/security_server.php

Server-Sicherheit

Server und MixedServer schützen eingehende Pakete standardmäßig durch globale und quellbezogene Token-Buckets, eigene Who-Is-/WriteProperty-Limits, temporäre Quellsperren, IPv4-CIDR-ACLs und die Deduplizierung erfolgreicher Writes. Beim MixedServer findet die Prüfung bereits vor dessen 32-PDU-Queue statt.

$server = new Bacnet\Server(9001, 'eth0', 47808);
$server->setSecurityOptions([
    'allowed_networks' => ['192.168.202.0/24'],
    'denied_networks' => ['192.168.202.250/32'],
    'per_source_rate' => 50,
    'write_rate' => 5,
]);

$effective = $server->getSecurityOptions();
$stats = $server->getSecurityStats(includeSources: false, reset: false);

Deny hat Vorrang; eine leere Allowlist erlaubt alle IPv4-Quellen. Änderungen per setSecurityOptions() wirken sofort und setzen laufende Token-/Blockzustände zurück, behalten aber kumulierte Statistiken. Ungültige Schlüssel oder Werte erzeugen ValueError.

Die Mechanismen ersetzen keine Netzsegmentierung: Klassisches BACnet/IP bleibt unverschlüsselt und unauthentifiziert. VLAN und Firewall sind weiterhin die primäre Sicherheitsgrenze. Alle Optionen, Statistiken und Betriebsbeispiele stehen in docs/server-security.md.

Installation

Vollständige Anleitung: docs/installation.md

Für den Build ist LMDB verpflichtend; unter Debian/Ubuntu muss vor dem Konfigurieren liblmdb-dev installiert sein. Die PHPT-Suite benötigt zudem die PHP-Erweiterung sockets (bei den PHP-CLI-Paketen normalerweise enthalten).

sudo apt-get install -y liblmdb-dev php8.5-cli php8.5-dev

# Submodul initialisieren und bacnet-stack bauen
git submodule update --init --recursive
./scripts/build-deps.sh

# Extension bauen (PHP 8.5)
phpize8.5
./configure --with-bacnet --with-php-config=php-config8.5
make -j$(nproc)

# Extension dauerhaft aktivieren
sudo cp modules/bacnet.so $(php-config8.5 --extension-dir)/
echo "extension=bacnet.so" | sudo tee /etc/php/8.5/cli/conf.d/30-bacnet.ini
php8.5 -m | grep bacnet

Entwicklung

Die C- und Header-Dateien außerhalb von deps/ werden mit der versionierten .clang-format-Konfiguration formatiert. config.h wird von diesen Befehlen ausgenommen, weil sie durch configure erzeugt wird.

# Formatieren und anschließend die Standards prüfen
clang-format -i bacnet.c php_bacnet.h src/*.[ch] tests/c_client_test.c
./scripts/check-coding-standards.sh

# Abhängigkeit, Erweiterung und PHPT-Suite bauen bzw. ausführen
./scripts/build-deps.sh
phpize8.5
./configure --with-bacnet --with-php-config=php-config8.5
make EXTRA_CFLAGS="-Wall -Wextra -Wno-unused-parameter" -j"$(nproc)"
mkdir -p /tmp/php-bacnet-lmdb-test
php8.5 run-tests.php -d extension=modules/bacnet.so \
    -d bacnet.cache_lmdb_path=/tmp/php-bacnet-lmdb-test tests/

Extension-Lebenszyklus

bacnet ist eine PHP-Extension (kein Zend-Extension-Modul). Der dynamische Build exportiert daher get_module; PHP prüft beim Laden API-Nummer und Build-ID. Die einmalige Klassen- und INI-Registrierung erfolgt in MINIT, anfragebezogene Client-Zustände werden in RINIT zurückgesetzt, und die INI-Registrierung wird in MSHUTDOWN aufgehoben.

Konfiguration (php.ini)

Direktive Standard Beschreibung
bacnet.default_port 47808 UDP-Port (Standard-BACnet-Port = 0xBAC0)
bacnet.default_timeout_ms 3000 Request-Timeout in Millisekunden
bacnet.default_interface 0.0.0.0 Interface-Name ("eth0") oder Auto-Erkennung
bacnet.server_security_enabled 1 Gemeinsame Schutzschicht aktivieren
bacnet.server_per_source_rate / bacnet.server_per_source_burst 50 / 100 Paketlimit je Quell-IP
bacnet.server_global_rate / bacnet.server_global_burst 500 / 1000 Globales Paketlimit
bacnet.server_who_is_rate / bacnet.server_who_is_burst 2 / 5 Who-Is-Limit je Quelle
bacnet.server_write_rate / bacnet.server_write_burst 5 / 10 WriteProperty-Limit je Quelle
bacnet.server_flood_violations / bacnet.server_flood_window_seconds 20 / 10 Verletzungen und Zählfenster bis zur Sperre
bacnet.server_block_duration_seconds 60 Dauer einer Quellsperre
bacnet.server_duplicate_window_seconds 5 Deduplizierungsfenster erfolgreicher Writes
bacnet.server_allowed_networks / bacnet.server_denied_networks leer / leer Kommagetrennte IPv4-CIDRs
bacnet.server_max_sources / bacnet.server_source_ttl_seconds 1024 / 300 Größe und Ablauf der Quelltabelle
bacnet.server_log_interval_seconds 60 Mindestabstand aggregierter Warnungen
extension=bacnet.so
bacnet.default_port       = 47808
bacnet.default_timeout_ms = 3000
bacnet.default_interface  = eth0
bacnet.server_allowed_networks = 192.168.202.0/24
bacnet.server_denied_networks = 192.168.202.250/32

Die vollständige Zuordnung zwischen INI-Direktiven und PHP-Optionsschlüsseln steht im Sicherheitsleitfaden.

Dokumentation

Datei Beschreibung
docs/api-reference.md Vollständige PHP API-Referenz (php.net-Stil)
docs/installation.md Build- und Installationsanleitung
docs/faq.md Häufige Fragen zu Installation, Discovery und Fehlersuche
docs/mixed-mode.md Architektur, Queue und Daemon-Betrieb des Mixed-Modus
docs/server-security.md ACLs, Rate-Limits, Sperren, Deduplizierung und Statistiken
examples/README.md Ausführbare Client-/Server-Demos
stubs/bacnet.stub.php IDE/PHPStan Stubs
CHANGELOG.md Versionshistorie

Anforderungen

  • PHP 8.4 oder 8.5 — NTS-Build (Non-Thread-Safe), mit Dev-Headers (php8.5-dev)
  • Linux (GCC, Autotools)
  • build-essential, cmake ≥ 3.16
  • Netzwerkzugang auf UDP-Port 47808 (BACnet/IP)

Lizenz

BSD-3-Clause — siehe LICENSE. Kompatibel mit der bacnet-stack BSD-3-Lizenz.

Danksagungen