Как создать наблюдаемую для устаревшей системы камер?

#c# #.net #reactive-programming #system.reactive

Вопрос:

У меня есть устаревший клиент камеры для связи с камерой. То, как это работает, немного неудобно для того времени. Будучи сам поклонником «реактивных» вещей, я хотел бы общаться с камерой в реактивной манере.

Вот как это работает

Всякий раз, когда вы хотите начать захват кадров, вы вызываете Start, и он начинает захватывать кадр каждые 0,5 секунды, и вы получаете уведомления о каждом кадре, подписывающемся на событие «при захвате». Событие продолжает расти до тех пор, пока вы не вызовете Остановку.

Таким образом, камера имеет:

  • способ начать захват кадров
  • еще один способ остановить захват
  • событие, возникающее при захвате нового кадра

Это то, чего я хочу

Я хотел бы инкапсулировать камеру в более удобную абстракцию, в которой методы запуска и остановки заменяются на an IObservable<bool> , и результирующее наблюдаемое должно выдавать пакеты снимков ( Frames ).

Более подробная информация

Это устаревший класс камер:

 class Camera 
{
    void StartRecording();
    void StopRecording();
    public event CaptureEventHandler OnCapture;
    public delegate void CaptureEventHandler(Camera sender, Frame capture);
}
 

Что я хочу, так это создать обернуть камеру в класс, подобный этому (я создал только скелет).:

 public class ObservableCamera 
{
      public ObservableCamera(Camera camera, IObservable<bool> enableCapture)
      { 
          // TODO: Define the 'Captures' observable using the parameters above
      }

      IObservable<Frame[]> Captures { get; }
}
 

Камера должна начинать захват кадров, когда наблюдаемый объект enableCapture испускает a true , и должна прекращать захват, когда он нажимает false .

Чтобы немного прояснить это, вот мраморная диаграмма, показывающая взаимодействие:

  • Первая последовательность такова enableCapture
  • Вторая должна быть вспомогательной последовательностью, созданной на основе OnCapture события.

введите описание изображения здесь

В дополнение к этому, StopRecording должен вызываться всякий раз, когда была запущена операция запуска, а также в случае завершения enableCapture или сбоя наблюдаемого.

Итак, о чем я прошу?

Я получил комментарии, в которых говорилось, что моя цель неясна. Чтобы прояснить это, вот мой вопрос:

Как я должен реализовать наблюдаемое для работы с устаревшей камерой? Я никогда не знаю, с чего начать.

Комментарии:

1. Итак, что же на самом деле вас сдерживает? Я не понимаю вопроса.

2. Вопрос подразумевается, так как я описываю то, что я хочу. Что меня сдерживает, так это невежество.

3. @SuperJMN Это может быть хорошим вопросом для обмена программным обеспечением, если вы спрашиваете о подходе к его разработке. Или вы подозреваете, что есть какая-то встроенная вещь, которую вам нужно использовать, но о которой вы не знаете? (Не разработчик C#, так что не уверен.)

4. Хотя я проголосовал за повторное открытие вопроса, я думаю, что необходимы дополнительные разъяснения относительно поведения результата IObservable<Frame[]> Captures в случае, если привод IObservable<bool> enableCapture выдает повторяющиеся последовательные значения true или false, и в случае, если он завершается успешно или с ошибкой. Например, должен StopRecording() ли он вызываться при enableCapture завершении?

5. SuperJMN не могли бы вы отредактировать вопрос и добавить это требование? Также, пожалуйста, проголосуйте повторно, чтобы я мог опубликовать свой ответ здесь, если это возможно.

Ответ №1:

Вот один из подходов, который использует Window оператор для определения окон (периодов), когда камера включена. Camera.OnCapture Событие подписывается в начале каждого окна и отменяется в конце окна.

 Camera camera;
IObservable<bool> enableCapture;

IObservable<Frame[]> observableCamera = enableCapture
    .DistinctUntilChanged()
    .Publish(published => published
        .Window(published.Where(enabled => enabled), _ => published.Where(enabled => !enabled))
        .Select(w =>
            Observable
                .FromEvent<Camera.CaptureEventHandler, Frame>(
                    h => (sender, capture) => h(capture),
                    h =>
                    {
                        camera.OnCapture  = h;
                        camera.StartRecording();
                    },
                    h =>
                    {
                        camera.StopRecording();
                        camera.OnCapture -= h;
                    })
                .TakeUntil(w.LastOrDefaultAsync())
                .ToArray()
        )
        .Concat()
    );
 

DistinctUntilChanged Оператор используется для того, чтобы игнорировать последовательные значения true или false. Publish Оператор используется для того, чтобы избежать многократных подписок на enableCapture последовательность. Скорее всего , эта последовательность будет такой Subject<bool> , и без того горячей, что сделает ее по Publish существу избыточной, но в принципе хорошо избегать нескольких подписок.

Не было предпринято никаких мер в случае, если либо StartRecording метод, либо StopRecording метод не сработают. В этом случае подписка на OnCapture мероприятие будет утечена.

Комментарии:

1. Ладно, я не знаю, что это за колдовство, но я совершенно поражен. Я думаю, что мне нужно пойти к Publish операторам Window и внимательно прочитать. Этот звонок LastOrDefaultAsync заставил меня заблудиться! Есть ли какой-нибудь хороший способ увидеть, что происходит? Есть ли хорошие тренировки/ката для Rx? Я действительно хочу освоить эту тему.

2. @SuperJMN ха-ха-ха! Нет, я не знаю, как лучше всего овладеть Rx. И я сам не считаю себя мастером. Каждый раз, когда я хочу решить проблему с Rx, я часами перебираю все доступные встроенные операторы в поисках того, который может решить конкретную проблему. Не очень продуктивный способ решения проблем, но, тем не менее, веселый. 🙂

3. @SuperJMN хорошим учебным материалом является введение в онлайн-книгу Rx , но я уверен, что вы знаете об этом.

4. ДА. К сожалению, таких сложных образцов, как этот, не существует. Примеры использования в реальной жизни иногда требуют глубокого понимания того, что происходит. Сложные наблюдаемые объекты (из-за вложенной композиции) — это чистое мастерство. Я бы хотел, чтобы там были ката, с которыми можно было бы попрактиковаться.

5. Внимание, Concat оператор ведет себя странно в текущей версии библиотеки Rx (5.0.0). Мой совет-вместо этого использовать эквивалентный Merge(1) оператор, пока проблема с Concat ним не будет устранена. 1 Это значение maxConcurrent параметра. Установка этого параметра в 1 значение означает отсутствие параллелизма.