wrplayer 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,462 @@
1
+ /**
2
+ * PTZ Controller - PTZ云台控制核心模块
3
+ * 支持方向控制、变焦、聚焦、预置点管理等功能
4
+ */
5
+
6
+ class PTZController {
7
+ /**
8
+ * 创建PTZ控制器实例
9
+ * @param {Object} config - PTZ配置
10
+ * @param {string} config.apiUrl - SIP服务器地址
11
+ * @param {string} config.apiKey - API认证密钥
12
+ * @param {string} config.deviceId - 设备ID
13
+ * @param {string} config.channelId - 通道ID
14
+ * @param {boolean} [config.debug=false] - 是否启用调试
15
+ * @param {boolean} [config.useProxy=true] - 是否使用代理(避免CORS)
16
+ */
17
+ constructor(config) {
18
+ this.config = {
19
+ debug: false,
20
+ useProxy: true,
21
+ ...config
22
+ };
23
+
24
+ if (!this.config.apiUrl) {
25
+ throw new Error('PTZController: apiUrl is required');
26
+ }
27
+
28
+ this.eventHandlers = {};
29
+ this.isMoving = false;
30
+ this.currentSpeed = 50; // 默认速度50%
31
+
32
+ // 预置点管理
33
+ this.preset = new PTZPresetManager(this);
34
+ // 巡航管理
35
+ this.cruise = new PTZCruiseManager(this);
36
+ // 扫描管理
37
+ this.scan = new PTZScanManager(this);
38
+
39
+ this._log('PTZ Controller initialized', this.config);
40
+ }
41
+
42
+ /**
43
+ * 发送PTZ API请求
44
+ * @private
45
+ * @param {string} endpoint - API端点
46
+ * @param {Object} params - 请求参数
47
+ * @returns {Promise} - API响应
48
+ */
49
+ async _request(endpoint, params = {}) {
50
+ try {
51
+ let url;
52
+
53
+ if (this.config.useProxy && typeof window !== 'undefined') {
54
+ // 使用Next.js代理
55
+ url = `/api/ptz/${endpoint}`;
56
+ } else {
57
+ // 直接请求
58
+ url = `${this.config.apiUrl}/api/front-end/${endpoint}`;
59
+ }
60
+
61
+ // 添加查询参数
62
+ const searchParams = new URLSearchParams(params);
63
+ const fullUrl = `${url}?${searchParams}`;
64
+
65
+ const headers = {
66
+ 'Content-Type': 'application/json',
67
+ };
68
+
69
+ // 添加API Key认证 - 只使用SIP服务器需要的格式
70
+ if (this.config.apiKey) {
71
+ headers['api-key'] = this.config.apiKey;
72
+ }
73
+
74
+ this._log('PTZ API Request:', fullUrl);
75
+ this._log('PTZ API Headers:', JSON.stringify(headers, null, 2));
76
+
77
+ const response = await fetch(fullUrl, {
78
+ method: 'GET',
79
+ headers
80
+ });
81
+
82
+ if (!response.ok) {
83
+ throw new Error(`PTZ API Error: ${response.status} ${response.statusText}`);
84
+ }
85
+
86
+ const data = await response.json();
87
+ this._log('PTZ API Response:', data);
88
+
89
+ // 触发成功事件
90
+ this._emit('request:success', { endpoint, params, data });
91
+
92
+ return data;
93
+ } catch (error) {
94
+ this._log('PTZ API Error:', error);
95
+ this._emit('request:error', { endpoint, params, error });
96
+ throw error;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * PTZ方向控制
102
+ * @param {string} direction - 方向 ('up', 'down', 'left', 'right', 'stop')
103
+ * @param {number} [speed] - 速度 (1-100)
104
+ * @returns {Promise}
105
+ */
106
+ async move(direction, speed = this.currentSpeed) {
107
+ const command = direction === 'stop' ? 'stop' : direction;
108
+ const horizonSpeed = direction === 'left' || direction === 'right' ?
109
+ Math.floor(speed * 255 / 100) : 0;
110
+ const verticalSpeed = direction === 'up' || direction === 'down' ?
111
+ Math.floor(speed * 255 / 100) : 0;
112
+ const zoomSpeed = 0;
113
+
114
+ this.isMoving = command !== 'stop';
115
+
116
+ await this._request(`ptz/${this.config.deviceId}/${this.config.channelId}`, {
117
+ command,
118
+ horizonSpeed,
119
+ verticalSpeed,
120
+ zoomSpeed
121
+ });
122
+
123
+ this._emit('move', { direction, speed });
124
+ return this;
125
+ }
126
+
127
+ /**
128
+ * 停止所有PTZ动作
129
+ * @returns {Promise}
130
+ */
131
+ async stop() {
132
+ return this.move('stop', 0);
133
+ }
134
+
135
+ /**
136
+ * 变焦控制
137
+ * @param {string} direction - 'in' 或 'out'
138
+ * @param {number} [speed] - 速度 (1-100)
139
+ * @returns {Promise}
140
+ */
141
+ async zoom(direction, speed = this.currentSpeed) {
142
+ const command = direction === 'in' ? 'zoomin' : 'zoomout';
143
+ const zoomSpeed = Math.floor(speed * 16 / 100);
144
+
145
+ await this._request(`ptz/${this.config.deviceId}/${this.config.channelId}`, {
146
+ command,
147
+ horizonSpeed: 0,
148
+ verticalSpeed: 0,
149
+ zoomSpeed
150
+ });
151
+
152
+ this._emit('zoom', { direction, speed });
153
+ return this;
154
+ }
155
+
156
+ /**
157
+ * 聚焦控制
158
+ * @param {string} direction - 'near' 或 'far'
159
+ * @param {number} [speed] - 速度 (1-100)
160
+ * @returns {Promise}
161
+ */
162
+ async focus(direction, speed = this.currentSpeed) {
163
+ const focusSpeed = Math.floor(speed * 255 / 100);
164
+
165
+ await this._request(`fi/focus/${this.config.deviceId}/${this.config.channelId}`, {
166
+ command: direction,
167
+ speed: focusSpeed
168
+ });
169
+
170
+ this._emit('focus', { direction, speed });
171
+ return this;
172
+ }
173
+
174
+ /**
175
+ * 光圈控制
176
+ * @param {string} direction - 'in' 或 'out'
177
+ * @param {number} [speed] - 速度 (1-100)
178
+ * @returns {Promise}
179
+ */
180
+ async iris(direction, speed = this.currentSpeed) {
181
+ const irisSpeed = Math.floor(speed * 255 / 100);
182
+
183
+ await this._request(`fi/iris/${this.config.deviceId}/${this.config.channelId}`, {
184
+ command: direction,
185
+ speed: irisSpeed
186
+ });
187
+
188
+ this._emit('iris', { direction, speed });
189
+ return this;
190
+ }
191
+
192
+ /**
193
+ * 设置控制速度
194
+ * @param {number} speed - 速度 (1-100)
195
+ * @returns {PTZController}
196
+ */
197
+ setSpeed(speed) {
198
+ this.currentSpeed = Math.max(1, Math.min(100, speed));
199
+ this._emit('speed:change', { speed: this.currentSpeed });
200
+ return this;
201
+ }
202
+
203
+ /**
204
+ * 获取当前速度
205
+ * @returns {number}
206
+ */
207
+ getSpeed() {
208
+ return this.currentSpeed;
209
+ }
210
+
211
+ /**
212
+ * 事件监听
213
+ * @param {string} event - 事件名
214
+ * @param {Function} handler - 事件处理函数
215
+ * @returns {PTZController}
216
+ */
217
+ on(event, handler) {
218
+ if (!this.eventHandlers[event]) {
219
+ this.eventHandlers[event] = [];
220
+ }
221
+ this.eventHandlers[event].push(handler);
222
+ return this;
223
+ }
224
+
225
+ /**
226
+ * 移除事件监听
227
+ * @param {string} event - 事件名
228
+ * @param {Function} [handler] - 事件处理函数
229
+ * @returns {PTZController}
230
+ */
231
+ off(event, handler) {
232
+ if (!this.eventHandlers[event]) return this;
233
+
234
+ if (handler) {
235
+ const index = this.eventHandlers[event].indexOf(handler);
236
+ if (index > -1) {
237
+ this.eventHandlers[event].splice(index, 1);
238
+ }
239
+ } else {
240
+ delete this.eventHandlers[event];
241
+ }
242
+ return this;
243
+ }
244
+
245
+ /**
246
+ * 触发事件
247
+ * @private
248
+ * @param {string} event - 事件名
249
+ * @param {*} data - 事件数据
250
+ */
251
+ _emit(event, data) {
252
+ if (this.eventHandlers[event]) {
253
+ this.eventHandlers[event].forEach(handler => {
254
+ try {
255
+ handler(data);
256
+ } catch (error) {
257
+ this._log('Event handler error:', error);
258
+ }
259
+ });
260
+ }
261
+ }
262
+
263
+ /**
264
+ * 调试日志
265
+ * @private
266
+ */
267
+ _log(...args) {
268
+ if (this.config.debug) {
269
+ console.log('[PTZController]', ...args);
270
+ }
271
+ }
272
+
273
+ /**
274
+ * 销毁控制器
275
+ */
276
+ destroy() {
277
+ this.eventHandlers = {};
278
+ this._log('PTZ Controller destroyed');
279
+ }
280
+ }
281
+
282
+ /**
283
+ * PTZ预置点管理器
284
+ */
285
+ class PTZPresetManager {
286
+ constructor(controller) {
287
+ this.controller = controller;
288
+ }
289
+
290
+ /**
291
+ * 查询预置点列表
292
+ * @returns {Promise<Array>}
293
+ */
294
+ async list() {
295
+ const response = await this.controller._request(
296
+ `preset/query/${this.controller.config.deviceId}/${this.controller.config.channelId}`
297
+ );
298
+ this.controller._emit('preset:list', response);
299
+ return response;
300
+ }
301
+
302
+ /**
303
+ * 保存当前位置为预置点
304
+ * @param {number} presetId - 预置点ID (1-255)
305
+ * @returns {Promise}
306
+ */
307
+ async save(presetId) {
308
+ await this.controller._request(
309
+ `preset/add/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
310
+ { presetId }
311
+ );
312
+ this.controller._emit('preset:saved', { presetId });
313
+ return this;
314
+ }
315
+
316
+ /**
317
+ * 调用预置点
318
+ * @param {number} presetId - 预置点ID
319
+ * @returns {Promise}
320
+ */
321
+ async call(presetId) {
322
+ await this.controller._request(
323
+ `preset/call/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
324
+ { presetId }
325
+ );
326
+ this.controller._emit('preset:called', { presetId });
327
+ return this;
328
+ }
329
+
330
+ /**
331
+ * 删除预置点
332
+ * @param {number} presetId - 预置点ID
333
+ * @returns {Promise}
334
+ */
335
+ async delete(presetId) {
336
+ await this.controller._request(
337
+ `preset/delete/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
338
+ { presetId }
339
+ );
340
+ this.controller._emit('preset:deleted', { presetId });
341
+ return this;
342
+ }
343
+ }
344
+
345
+ /**
346
+ * PTZ巡航管理器
347
+ */
348
+ class PTZCruiseManager {
349
+ constructor(controller) {
350
+ this.controller = controller;
351
+ }
352
+
353
+ /**
354
+ * 开始巡航
355
+ * @param {number} cruiseId - 巡航组ID
356
+ * @returns {Promise}
357
+ */
358
+ async start(cruiseId = 1) {
359
+ await this.controller._request(
360
+ `cruise/start/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
361
+ { cruiseId }
362
+ );
363
+ this.controller._emit('cruise:started', { cruiseId });
364
+ return this;
365
+ }
366
+
367
+ /**
368
+ * 停止巡航
369
+ * @param {number} cruiseId - 巡航组ID
370
+ * @returns {Promise}
371
+ */
372
+ async stop(cruiseId = 1) {
373
+ await this.controller._request(
374
+ `cruise/stop/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
375
+ { cruiseId }
376
+ );
377
+ this.controller._emit('cruise:stopped', { cruiseId });
378
+ return this;
379
+ }
380
+
381
+ /**
382
+ * 设置巡航速度
383
+ * @param {number} cruiseId - 巡航组ID
384
+ * @param {number} speed - 巡航速度
385
+ * @returns {Promise}
386
+ */
387
+ async setSpeed(cruiseId, speed) {
388
+ await this.controller._request(
389
+ `cruise/speed/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
390
+ { cruiseId, speed }
391
+ );
392
+ this.controller._emit('cruise:speed:set', { cruiseId, speed });
393
+ return this;
394
+ }
395
+ }
396
+
397
+ /**
398
+ * PTZ扫描管理器
399
+ */
400
+ class PTZScanManager {
401
+ constructor(controller) {
402
+ this.controller = controller;
403
+ }
404
+
405
+ /**
406
+ * 开始扫描
407
+ * @param {number} scanId - 扫描组ID
408
+ * @returns {Promise}
409
+ */
410
+ async start(scanId = 1) {
411
+ await this.controller._request(
412
+ `scan/start/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
413
+ { scanId }
414
+ );
415
+ this.controller._emit('scan:started', { scanId });
416
+ return this;
417
+ }
418
+
419
+ /**
420
+ * 停止扫描
421
+ * @param {number} scanId - 扫描组ID
422
+ * @returns {Promise}
423
+ */
424
+ async stop(scanId = 1) {
425
+ await this.controller._request(
426
+ `scan/stop/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
427
+ { scanId }
428
+ );
429
+ this.controller._emit('scan:stopped', { scanId });
430
+ return this;
431
+ }
432
+
433
+ /**
434
+ * 设置扫描左边界
435
+ * @param {number} scanId - 扫描组ID
436
+ * @returns {Promise}
437
+ */
438
+ async setLeftBoundary(scanId = 1) {
439
+ await this.controller._request(
440
+ `scan/set/left/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
441
+ { scanId }
442
+ );
443
+ this.controller._emit('scan:boundary:left', { scanId });
444
+ return this;
445
+ }
446
+
447
+ /**
448
+ * 设置扫描右边界
449
+ * @param {number} scanId - 扫描组ID
450
+ * @returns {Promise}
451
+ */
452
+ async setRightBoundary(scanId = 1) {
453
+ await this.controller._request(
454
+ `scan/set/right/${this.controller.config.deviceId}/${this.controller.config.channelId}`,
455
+ { scanId }
456
+ );
457
+ this.controller._emit('scan:boundary:right', { scanId });
458
+ return this;
459
+ }
460
+ }
461
+
462
+ export default PTZController;