Implement KnxTpUartClient and transport classes with connection handling and message encoding
Signed-off-by: Tony <tonylu@tony-cloud.com>
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:knx_stack/knx_stack.dart';
|
||||
import 'package:knx_stack_tpuart/knx_stack_tpuart.dart';
|
||||
|
||||
void main() {
|
||||
group('KnxTpUartClient', () {
|
||||
test('completes the reset and state handshake', () async {
|
||||
final transport = _FakeKnxTransport();
|
||||
final client = KnxTpUartClient(transport: transport);
|
||||
|
||||
final connectFuture = client.connect();
|
||||
await _flushMicrotasks();
|
||||
|
||||
expect(transport.sentFrames, hasLength(1));
|
||||
expect(transport.sentFrames.single, [0x01]);
|
||||
|
||||
transport.emit(const [0x03]);
|
||||
await _flushMicrotasks();
|
||||
|
||||
expect(transport.sentFrames, hasLength(2));
|
||||
expect(transport.sentFrames.last, [0x02]);
|
||||
|
||||
transport.emit(const [0x07]);
|
||||
await connectFuture;
|
||||
|
||||
expect(client.isConnected, isTrue);
|
||||
});
|
||||
|
||||
test('encodes cEMI writes into TP-UART service bytes', () async {
|
||||
final transport = _FakeKnxTransport();
|
||||
final client = KnxTpUartClient(transport: transport);
|
||||
await _connectClient(client, transport);
|
||||
|
||||
final sendFuture = client.send(_sampleCemiWrite().toBytes());
|
||||
await _flushMicrotasks();
|
||||
|
||||
expect(transport.sentFrames.last, [
|
||||
0x80,
|
||||
0xbc,
|
||||
0x81,
|
||||
0x11,
|
||||
0x82,
|
||||
0x01,
|
||||
0x83,
|
||||
0x00,
|
||||
0x84,
|
||||
0x01,
|
||||
0x85,
|
||||
0xe2,
|
||||
0x86,
|
||||
0x00,
|
||||
0x87,
|
||||
0x80,
|
||||
0x88,
|
||||
0x64,
|
||||
0x49,
|
||||
0x54,
|
||||
]);
|
||||
|
||||
transport.emit(const [0x8b]);
|
||||
await sendFuture;
|
||||
});
|
||||
|
||||
test('decodes inbound TP telegrams into cEMI indications', () async {
|
||||
final transport = _FakeKnxTransport();
|
||||
final client = KnxTpUartClient(transport: transport);
|
||||
await _connectClient(client, transport);
|
||||
|
||||
final frameFuture = client.inboundFrames.first;
|
||||
final messageFuture = client.inboundMessages.first;
|
||||
|
||||
transport.emit(const [0xbc, 0x11, 0x01, 0x00, 0x01, 0xe2, 0x00, 0x80, 0x64, 0x54]);
|
||||
|
||||
expect(await frameFuture, [
|
||||
0x29,
|
||||
0x00,
|
||||
0xbc,
|
||||
0xe0,
|
||||
0x11,
|
||||
0x01,
|
||||
0x00,
|
||||
0x01,
|
||||
0x02,
|
||||
0x00,
|
||||
0x80,
|
||||
0x64,
|
||||
]);
|
||||
expect((await messageFuture).destinationAddress.canonical, '0/0/1');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _connectClient(KnxTpUartClient client, _FakeKnxTransport transport) async {
|
||||
final connectFuture = client.connect();
|
||||
await _flushMicrotasks();
|
||||
transport.emit(const [0x03]);
|
||||
await _flushMicrotasks();
|
||||
transport.emit(const [0x07]);
|
||||
await connectFuture;
|
||||
}
|
||||
|
||||
Future<void> _flushMicrotasks() async {
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
}
|
||||
|
||||
CemiLDataReq _sampleCemiWrite() {
|
||||
final controlField2 = ExtendedControlField()
|
||||
..addressType = AddressType.group
|
||||
..hopCount = 6
|
||||
..eff = ExtendedFrameFormat.pointToPointOrStandardGroupAddressedLDataExtendedFrame;
|
||||
|
||||
return CemiLDataReq(
|
||||
controlField1: ControlField(0xbc),
|
||||
controlField2: controlField2,
|
||||
sourceAddress: KnxIndividualAddress.parse('1.1.1'),
|
||||
destinationAddress: KnxGroupAddress.parse('0/0/1'),
|
||||
tpdu: TPDU(data: Uint8List.fromList([0x64])),
|
||||
);
|
||||
}
|
||||
|
||||
final class _FakeKnxTransport implements KnxTransport {
|
||||
final StreamController<Uint8List> _frames = StreamController<Uint8List>.broadcast();
|
||||
final List<Uint8List> sentFrames = <Uint8List>[];
|
||||
|
||||
bool _isConnected = false;
|
||||
|
||||
@override
|
||||
String get transportId => 'fake_tpuart';
|
||||
|
||||
@override
|
||||
KnxFeature get feature => KnxFeature.tpuartTransport;
|
||||
|
||||
@override
|
||||
KnxFeatureSupport get support => const KnxFeatureSupport.supported(
|
||||
feature: KnxFeature.tpuartTransport,
|
||||
platform: KnxPlatform.macos,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get isConnected => _isConnected;
|
||||
|
||||
@override
|
||||
Stream<Uint8List> get inboundFrames => _frames.stream;
|
||||
|
||||
@override
|
||||
Future<void> connect() async {
|
||||
_isConnected = true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect() async {
|
||||
_isConnected = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> send(Uint8List frame) async {
|
||||
sentFrames.add(Uint8List.fromList(frame));
|
||||
}
|
||||
|
||||
void emit(List<int> frame) {
|
||||
_frames.add(Uint8List.fromList(frame));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user