#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
значение означает отсутствие параллелизма.