Я хочу использовать Rx для работы с данными последовательного порта, структура пакета которых выглядит следующим образом.

+-----------+--------+---------+
| Signature | Length | Payload |
+-----------+--------+---------+
| 2 byte    | 1 byte | ...     |
+-----------+--------+---------+

Но было бы много фрагмента полученных данных. Такие как (подпись 0xFC 0xFA)
Данные 1: 0xFC 0xFA 0x02 0x01 0x01 0xFC 0xFA 0x03 0x01 // Содержат один пакет и фрагментный пакет
Данные 2: 0x02 0x03 0xFC 0xFA 0x02 0x01 0x03 // Содержат продолжение фрагмента предыдущего и нового пакета

Как передать операторов для вывода как
Пакет 1: 0xFC 0xFA 0x02 0x01 0x01
Пакет 2: 0xFC 0xFA 0x03 0x01 0x02 0x03
...

1
ShineZero 20 Дек 2019 в 19:28

2 ответа

Лучший ответ

Вы разделяете поток байтов по определенной схеме. Я не уверен, как вы получаете ваши байты и как вы моделируете свою наблюдаемую, Observable<byte> или Observable<byte[]>!?

Во всяком случае, здесь то, что я догадался, переведено в виде строк, но идея все та же. Я выбрал x, а затем y в качестве шаблона (0xFC 0xFA в вашем случае).

Вы найдете мои комментарии в коде:

final ImmutableList<String> PATTERN = ImmutableList.of("x", "y");

Observable<String> source = Observable
        .fromArray("x", "y", "1", "2", "3", "x", "y", "4", "5", "x", "y", "1", "x", "y", "x", "4", "6", "x")
        .share();//publishing to hot observable (we are splitting this source by some of its elements)

//find the next pattern
Observable<List<String>> nextBoundary = source
        .buffer(2, 1)
        .filter(pairs -> CollectionUtils.isEqualCollection(PATTERN, pairs));

//https://raw.githubusercontent.com/wiki/ReactiveX/RxJava/images/rx-operators/buffer2.png
//start a buffer for each x found
//buffers (packets) may overlap
source.buffer(source.filter(e -> e.equals("x")),
        x -> source
                .take(1)//next emission after the x
                .switchMap(y -> y.equals("y") ?
                        nextBoundary // if 'y' then find the next patter
                        : Observable.empty() //otherwise stop buffering
                )
)
        .filter(packet -> packet.size() > 2)//do not take the wrong buffers like ["x", "4"] (x not followed by y) but it is not lost
        .map(packet -> {
            //each packet is like the following :
            //[x, y, 1, 2, 3, x, y]
            //[x, y, 4, 5, x, y]
            //[x, y, 1, x, y]
            //[x, y, x, 4, 6, x]
            //because of the closing boundary, the event comes too late
            //then we have to handle the packet (it overlaps on the next one)
            List<String> ending = packet.subList(packet.size() - 2, packet.size());
            return CollectionUtils.isEqualCollection(PATTERN, ending) ? packet.subList(0, packet.size() - 2) : packet;
        })
        .blockingSubscribe(e -> System.out.println(e));

Результат:

[x, y, 1, 2, 3]
[x, y, 4, 5]
[x, y, 1]
[х, у, х, 4, 6, х]

1
bubbles 21 Дек 2019 в 00:48

Вам нужен наблюдатель с состоянием. Было бы эти состояния:

  1. прослушивание начала пакета
  2. получил первое прослушивание байта для второго байта
  3. получил второе прослушивание байтов для длины
  4. получил заголовок-прослушивание для тела

В RxJava вы должны создать класс Packetizer, который будет иметь два интересных метода:

public void nextByte(Char next);
public Observable<Packet> packetSource();

Внутренне он будет поддерживать состояние, включая длину оставшейся части тела и т. Д. Он также будет иметь PublishSubject<Packet>, который будет излучать каждый пакет при его создании.

1
Bob Dalgleish 20 Дек 2019 в 18:51