Подпишитесь на единственный пункт с определенными "повторениями" и "перерывом"

I have an IObservable which is a hot observable, that allows different subscriptions to analyze incoming packets.

Я хочу написать метод, который посылает некоторые данные с и ID, и ждет ответа с тем же самым ID. Псевдокод:

void SendData(byte[] data, int retries, int timeout, Action success, Action fail) 
{
    var sequenceId = GetSequenceId();
    _port.SendData(data, sequenceId);
    _packetStream.Where(p => p.SequenceId == sequenceId)
                 .Take(1)
                 .WaitForTimeout(timeout)
                 .WaitForRetry(retries)
                 .Subscribe(success) //Need to unsubscribe after packet is received
    //If we didn't receive an answer packet, then call fail() action 
}

Действительно не знайте, как этот материал обычно делается с Реактивными Расширениями. Было бы действительно радо получить некоторые предложения. Спасибо.

1
nl ja de

2 ответы

Кодекс в вашем вопросе смотрит близко к праву. Два "ждут" методов, существуют в структуре Rx (Перерыв и Повторить). I would recommend you change your method to return an IObservable и drop the success и fail parameter. Doing so "keeps you in the monad" и lets you chain further operators onto the observable if needed. The success и fail parameters are instead used when you subscribe to the resulting observable (as OnNext и OnError respectively).

Я предполагаю, что данные должны быть, негодуют на перерыве (иначе, вы действительно не повторяете). Чтобы сделать это, которое можно использовать Заметный. Создайте , чтобы послать данные по подписке.

IObservable SendData(byte[] data, int retries, TimeSpan timeout)
{
    //only get the sequence id once per call to SendData, regardless of retries
    var sequenceId = GetSequenceId();
    return Observable.Create(obs =>
        {   //this code runs every time you subscribe
            _port.SendData(data, sequenceId);
            return _packetStream.Where(p => p.SequenceId == sequenceId)
                                .Take(1)
                                .Timeout(timeout)
                                .Subscribe(obs)
        })
        .Retry(retries); 
}

Помещая Повторную попытку оператор в причинах конца, Создавание заметного, чтобы быть повторенным, если это времена. Как в стороне, есть перегрузки Перерыва, которые позволяют вам проходить в другой заметной последовательности, чтобы использовать в случае перерыва. Можно использовать эту перегрузку наряду с Заметным . Бросок , чтобы обеспечить ваше собственное исключение в случае перерыва при желании, например, предоставить дополнительное сообщение об ошибке.

Обратите внимание, что этот код не делает , посылают данные, пока вы не подписываетесь, и не делает блока, пока результат не возвращен, или перерыв достигнут, но действительно позволяет вам отменить дальнейшие повторения, Располагая подписку. Этот код также не препятствует тому, чтобы вы послали многократные пакеты в то же время. Если вы должны блок, можно сделать что-то вроде этого:

var response = SendData(/* arguments */);
response.Do(success, fail).StartWith(null).ToTask().Wait();

При использовании C# 5 и называете это в рамках async метода, можно ждать заметного.

1
добавлено
Это выглядит правильным, но я все еще don' t понимают, где это откажется от подписки после того, как операция закончена. У меня будет много требований к этому методу, таким образом, сможет причинить боль, если там будет подвешивать оставленные подписки. Кроме того, почему мне нужен StartWith (пустой указатель)?
добавлено автор ionoy, источник
@ionoy StartWith (пустой указатель) необходим из-за того, как ToTask ведет себя, если заметное заканчивает без пункта или ошибки. ToTask и ждут, будет обращаться с неподпиской. Если вы подписываетесь вручную, вы ответственны за то, что отказались от подписки. Как отмечено в другом комментарии, операторах в структуре все отказываются от подписки сами на ошибке, или завершение, таким образом отказываясь от подписки только строго необходимо для раннего завершения.
добавлено автор Gideon Engelberth, источник
@LeeCampbell, Если вы don' t отправляют данные, вы могли бы также просто увеличить перерыв и забыть "повторения" в целом. Распространено отправить пакеты в случае, если они не делают его первым разом, специально для вещей как последовательные порты, которые не "гарантируют" доставку. Id последовательности или подобный механизм могут тогда использоваться, чтобы гарантировать, что пакет не обрабатывается дважды.
добавлено автор Gideon Engelberth, источник
Я хотел бы указать здесь, что код выше взглядов в значительной степени исправляет, кроме я не думаю, что вы хотите звонок SendData, сделанный на каждую повторную попытку? Если это верно, тогда это должно быть перемещено за пределами Создавания, или Повторная попытка должна быть перемещена между.Timeout (перерыв) и.Subscribe (obs).
добавлено автор Lee Campbell, источник
Спасибо за чистку этого @Gideon. Это кажется разумной причиной держать посылание, где это.
добавлено автор Lee Campbell, источник

Что я получил, до сих пор это:

private void WaitForAnswer(byte sequenceId, int timeout, Action success, Action fail, int retriesLeft)
{
    _packetStream.Where(p => p.GetSequence() == sequenceId)
                 .Take(1)
                 .Timeout(TimeSpan.FromMilliseconds(timeout))
                 .Subscribe(success, 
                            _ => {
                                if (retriesLeft > 0) WaitForAnswer(sequenceId, timeout, success, fail, --retriesLeft);
                                else fail();
                            }, 
                           () => { });
}

Я не совсем уверен, хотя, если это решение правильно избавляется от подписки.

0
добавлено
Я didn' t определяют что я don' t действительно должен на самом деле заблокировать, ожидая. Я могу использовать, Ждут метод, поскольку вы уже предложили в своем ответе сделать это. Обычно, хотя, I' ll просто поставляют что-то, чтобы воздействовать на успех/подводить. Мое конечное решение сегодня использовало отдельный Предмет, возвращаясь Заметный с ним и используя OnNext/OnComplete в оригинальной Подписке. Но код в вашем ответе кажется уборщиком.
добавлено автор ionoy, источник
Кроме того, мой Заметный оригинал не закончится до самого конца, так, чтобы, вероятно, означал, что у меня будут тысячи подписок после получаса, которое определенно повредит работу.
добавлено автор ionoy, источник
Но технически я все еще был бы в состоянии ждать многочисленных ответов с подобным кодом?
добавлено автор ionoy, источник
Ваш код doesn' t избавляются от подписки вообще. Как деталь внедрения , однако, встроенные операторы вы использовали здесь, все имеют, "автоотделяют" поведение, означая, что они располагают себя, когда заметное заканчивается. Что еще более важно, этот код не ждет ответа прежде, чем возвратиться, поскольку имя подразумевает, что делает.
добавлено автор Gideon Engelberth, источник
Оба Берут (1) и , Перерыв заставит подписанный на заметный заканчиваться (на первом пакете соответствия или после перерыва соответственно) независимо от того, если _packetStream сделает. Запрещение пакетов, посланных, в то время как другой происходит, у вас должна только быть единственная подписка за один раз.
добавлено автор Gideon Engelberth, источник
Да. У вас будет одна подписка за пакет, к которому вы прислушиваетесь.
добавлено автор Gideon Engelberth, источник
Microsoft Stack Jobs
Microsoft Stack Jobs
1 788 участник(ов)

Work & freelance only Microsoft Stack. Feed https://t.me/Microsoftstackjobsfeed Чат про F#: @Fsharp_chat Чат про C#: @CSharpChat Чат про Xamarin: @xamarin_russia Чат общения:@dotnettalks

Microsoft Developer Community Chat
Microsoft Developer Community Chat
584 участник(ов)

Чат для разработчиков и системных администраторов Microsoft Developer Community. __________ Новостной канал: @msdevru __________ Баним за: оскорбления, мат, рекламу, флуд, флейм, спам, NSFW контент, а также большое количество оффтоп тем. @banofbot