ITBees.ModbusServer 8.0.3

ITBees.ModbusServer

Serwer TCP dla liczników energii (i innych urządzeń Modbus RTU) podpiętych przez bramki RS485→Ethernet typu USR DR134 pracujące w trybie transparentnym „TCP Client".

Bramka łączy się z serwerem, przedstawia się pakietem rejestracyjnym, a od tego momentu biblioteka pełni rolę mastera Modbus RTU: cyklicznie odpytuje mapę rejestrów licznika przez transparentny tunel, dekoduje wartości i zapisuje odczyty do bazy przez repozytoria FAS.

  • konfigurowany port nasłuchu (ModbusServer_port),
  • opisowa nazwa licznika (EnergyMeter.Name + Description),
  • rejestracja modeli bazodanowych w konwencji FAS (ModbusServerSetup.RegisterDbModels),
  • mapa rejestrów per licznik (funkcja 03/04, typy: UInt16/Int16/UInt32/Int32/Float32 ±swap, skala),
  • nieznane urządzenia trafiają do tabeli UnknownModbusDevice (łatwe „sparowanie" nowej bramki z UI),
  • gotowa mapa rejestrów dla Eastron SDM120 (DefaultRegisterMaps.EastronSdm120()).

Integracja z aplikacją FAS

appsettings.json:

{
  "ModbusServer_port": "8899",
  "ModbusServer_responseTimeoutMs": "2000",
  "ModbusServer_registrationPacketTimeoutMs": "3000",
  "ModbusServer_resyncDelayMs": "5000"
}

(trzy ostatnie klucze są opcjonalne — wartości powyżej to domyślne).

Rejestracja zależności (obok pozostałych modułów FAS):

new ModbusServerSetup().Register(services, configurationRoot);

Rejestracja modeli w DbContext.OnModelCreating:

ModbusServerSetup.RegisterDbModels(modelBuilder);

Po tym wystarczy wygenerować migrację (dotnet ef migrations add AddModbusServer). Serwer startuje sam jako IHostedService razem z aplikacją.

Host musi mieć zarejestrowane generyczne repozytoria FAS (IReadOnlyRepository<> / IWriteOnlyRepository<>) — standard w aplikacjach FAS.

Dodanie licznika

energyMeterService.Create(new NewEnergyMeterIm
{
    Name = "Rozdzielnia — hala A",             // nazwa opisowa
    Description = "Licznik za falownikiem",
    DeviceIdentifier = "METER-001",             // pakiet rejestracyjny bramki
    IdentificationMode = MeterIdentificationMode.RegistrationPacket,
    ModbusUnitId = 1,                           // adres slave na RS485
    PollingIntervalSeconds = 60,
    Registers = DefaultRegisterMaps.EastronSdm120(),
});

Identyfikacja połączenia: najpierw po pakiecie rejestracyjnym, w razie jego braku po IP (MeterIdentificationMode.RemoteIp, wtedy DeviceIdentifier = adres IP bramki). Połączenia niedopasowane do żadnego licznika lądują w IEnergyMeterService.GetUnknownDevices() — razem z odebranym pakietem (hex + ASCII), więc nową bramkę widać od razu.

Konfiguracja bramki USR DR134 (firmware V43xx)

  1. Network parameters — nadaj urządzeniu stałe IP albo zostaw DHCP (przy identyfikacji pakietem rejestracyjnym adres nie ma znaczenia).
  2. Port Parameter (parametry szeregowe — muszą zgadzać się z licznikiem):
    • Baud rate: wg licznika (Eastron SDM120 fabrycznie 2400), Data 8, Parity None, Stop 1,
    • Work Mode / Socket: TCP Client,
    • Remote Server Addr: adres (IP/domena) serwera z tą biblioteką,
    • Remote Port: wartość z ModbusServer_port (np. 8899),
    • Modbus TCP / protocol conversion: wyłączone (None) — rozmawiamy czystym RTU przez tunel,
    • „Modbus Poll" (tryb master w bramce): wyłączone — masterem jest serwer,
    • Registration packet: typ CUSTOM, treść np. METER-001 (ASCII), wysyłka przy połączeniu (location: connect; nie ustawiaj „z każdą ramką").
  3. MQTT Gateway / EDGE Gateway — zostaw wyłączone.
  4. RS485: zaciski A/A(+) i B/B(−) do licznika, przy dłuższych liniach terminator 120 Ω.

Na liczniku ustaw unikalny adres Modbus (1–247) zgodny z ModbusUnitId. Kilka liczników na jednej szynie RS485 = jedna bramka, jedno połączenie — obecnie jeden EnergyMeter na połączenie; przy wielu licznikach za jedną bramką daj każdemu osobną bramkę albo zgłoś potrzebę rozszerzenia.

Spójność ramek RTU (od 8.0.3)

Odpowiedź Modbus RTU nie niesie ani identyfikatora transakcji, ani adresu rejestru — z zapytaniem łączy ją wyłącznie kolejność na łączu. Spóźniona odpowiedź (opóźnienie łącza dłuższe niż ModbusServer_responseTimeoutMs, na łączach komórkowych zdarza się kilkanaście razy na dobę) uchodziłaby za odpowiedź na kolejne zapytanie i od tej chwili każdy rejestr dostawałby wartość poprzedniego (energia = częstotliwość, napięcie = stan licznika), aż do zgubienia jakiejś ramki. Serwer pilnuje trzech reguł:

  • bajty czekające w gnieździe przed wysłaniem zapytania są odrzucane (ostrzeżenie w logu z ich hex),
  • po timeoucie albo obcej ramce (zły CRC, inny adres slave, inna funkcja, inna liczba rejestrów) cykl jest przerywany, a kolejny zaczyna się nie wcześniej niż po ModbusServer_resyncDelayMs (domyślnie 5000 ms; przy dłuższym interwale odpytywania obowiązuje interwał),
  • odpowiedź musi nieść dokładnie tyle rejestrów, ile zapytano.

Diagnostyka: poziom Debug dla kategorii ITBees.ModbusServer loguje każdą ramkę TX/RX (hex) oraz pakiet rejestracyjny:

{ "Logging": { "LogLevel": { "ITBees.ModbusServer": "Debug" } } }

Emulator do testów

Bez fizycznego urządzenia można sprawdzić cały tor:

dotnet run --project ITBees.ModbusServer.DeviceEmulator -- 127.0.0.1 8899 METER-001 1

Emulator zachowuje się jak DR134 + SDM120: łączy się, wysyła pakiet rejestracyjny i odpowiada na zapytania FC04 symulowanymi wartościami (napięcie ~230 V, narastająca energia).

Testy

dotnet test

29 testów: CRC-16/MODBUS (wektor katalogowy), budowa/parsowanie ramek RTU, dekodowanie typów danych, rozpoznawanie obcych ramek oraz testy integracyjne end-to-end po pętli zwrotnej TCP (odczyt i resynchronizacja po spóźnionej odpowiedzi).

No packages depend on ITBees.ModbusServer.

Version Downloads Last updated
8.0.3 4 09/10/2026
8.0.2 2 09/10/2026
8.0.1 25 09/03/2026