我有一个场景,我必须控制多个PTZ摄像机才能拍摄多个角度的照片。因此,例如:
Camera A
的角度为A1
,A2
,A3
Camera B
的角度为B1
,B2
,B3
,B4
移动相机以指向正确的角度,捕获图像并上载图像是返回承诺的异步功能。
moveCamera( angle )
captureImage()
uploadImage()
摄像头必须并行运行,但是每个摄像头所拍摄的角度必须顺序进行。
我某种程度上感觉可以通过RxJS轻松解决,但是我正在努力将它们组合在一起。我能提供的最好的解决方案就是类似的解决方案,通过这种解决方案,相机可以按顺序相互处理。请注意,我使用redux-observable
,下面的代码是我在 plain RxJS中能做到的最好的代码。请原谅我的RxJS。
const angles = {
'Camera A': [ 'A1', 'A2', 'A3' ],
'Camera B': [ 'B1', 'B2', 'B3', 'B4' ],
}
const cameras = of( [ 'Camera A', 'Camera B' ] );
const cameraRun = cameras.pipe(
mergeMap( camera => {
// in redux-observable, I could return an array here
return of( angles[ camera ] );
} )
);
cameraRun.pipe(
concatMap( angle => {
return moveCamera( angle )
.then( () => captureImage() )
.then( () => uploadImage() )
.then( () => console.log( 'Image success' ) );
} )
)
对于认识redux-observable
的人,我有3个史诗:
RUN_CAMERA_SET_ROUTINE
-运行mergeMap
内的所有摄像机
RUN_CAMERA_ROUTINE
-在mergeMap
内为每个摄像机运行所有角度
CAPTURE_IMAGE
-在一个concatMap
我最初的想法是CAPTURE_IMAGE
由于mergeMap
生成流而将被“分组”,但是我错了。对于所有摄像机,CAPTURE_IMAGE
似乎仍然在每个角度排队。
任何指针都将非常有帮助。
答案 0 :(得分:1)
您的问题归结为按顺序并行执行其他一些可观察对象,并从Promises中创建可观察对象。
然后,您必须构造要依次执行的Observable数组(单个摄像机的摄像机角度动作)和要并行执行的Observable数组(每个摄像机的摄像机动作列表) )。
这可能是纯RxJS中的代码
import { concat, forkJoin, merge, defer } from 'rxjs';
const cameras = ['Camera A', 'Camera B'];
const cameraAngles = { 'Camera A': ['A1', 'A2', 'A3'], 'Camera B': ['B1', 'B2', 'B3', 'B4'] }
/**
* Performs a camera action consisting of multiple parts. Returns a Promise.
* @param camera a camera, I added this parameter because it seemed logic but you don't seem to need it.
* @param angle a camera angle
*/
function doCameraAction(camera, angle) {
return moveCamera(angle)
.then(() => captureImage())
.then(() => uploadImage())
.then(() => console.log('Image success'));
}
/**
* Returns an array of Observables. Each Observable will perform a camera action when subscribed to.
* @param camera a camera
* @param angles an array of camera angles
*/
function getCameraActions(camera, angles) {
// 'defer' is used to defer the creation and thus execution of a camera action until someone subscribes
return angles.map(angle => defer(() => doCameraAction(camera, angle)));
}
/** Returns an Observable that will execute multiple camera actions in sequence for the given camera */
const cameraActions = (camera) => concat(...getCameraActions(camera, cameraAngles[camera]));
// An Observable that will execute some camera actions for each camera in parallel
const multiCameraActions$ = forkJoin(cameras.map(camera => cameraActions(camera)));
//---------------------- or merge(...cameras.map(camera => cameraActions(camera)));
答案 1 :(得分:0)
我会为此开枪。我在StackBlitz中提出了一个解决方案,以展示我的想法。在控制台上单击之前,单击按钮以开始新的运行。
有关此解决方案的一些要点:
start$
来启动新的运行,这对解决方案并不重要。camera
的变量,但这只是使console.log()可以清楚地显示相机在做什么。redux-observable
不做任何事情,而是将其保留为rxjs
的原样。concat()
将照片的拍摄转换为可观察的序列,而不是像一连串的承诺那样将其留给您-没必要,只是一种不同的处理方式。cameraA$
和cameraB$
),但是也可以使用一系列相机来完成。请随意进行分叉并将其更改为更接近您要查找的内容。
这是StackBlitz中的内容:
import { mergeMap, concatMap, tap } from 'rxjs/operators';
import { fromEvent, from, concat, merge, defer } from 'rxjs';
const moveCamera = (camera, angle) => new Promise(
(resolve, reject) => {
setTimeout(() => {
console.log(`moved: ${camera} angle: ${angle}`);
resolve();
}, 1000) }
);
const captureImage = (camera) => new Promise(
(resolve, reject) => {
setTimeout(() => {
console.log(`${camera} captured image.`);
resolve();
}, 100) }
);
const uploadImage = (camera) => new Promise(
(resolve, reject) => {
setTimeout(() => {
console.log(`${camera} uploaded image.`);
resolve();
}, 2000) }
);
const start$ = fromEvent(document.getElementById('start'), 'click');
const takeAPhoto$ = (camera, angle) => concat(
defer(() => moveCamera(camera, angle)),
defer(() => captureImage(camera)),
defer(() => uploadImage(camera))
);
const cameraA$ = from(['A1', 'A2', 'A3']).pipe(
concatMap(angle => takeAPhoto$('Camera A', angle))
);
const cameraB$ = from(['B1', 'B2', 'B3', 'B4']).pipe(
concatMap(angle => takeAPhoto$('Camera B', angle))
);
start$.pipe(
tap(() => console.log('\n\nstart new run')),
mergeMap(() => merge(cameraA$, cameraB$)),
).subscribe();
我希望这会有所帮助。