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)
- Network parameters — nadaj urządzeniu stałe IP albo zostaw DHCP (przy identyfikacji pakietem rejestracyjnym adres nie ma znaczenia).
- 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ą").
- MQTT Gateway / EDGE Gateway — zostaw wyłączone.
- 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.
.NET 8.0
- ITBees.Fas.Setup (>= 8.0.15)
- ITBees.Interfaces (>= 8.0.72)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Microsoft.Extensions.Hosting.Abstractions (>= 8.0.1)