@tx5dr/contracts 1.7.12 → 2.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.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.js +27 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.js.map +1 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.js +113 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.js.map +1 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.js +42 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.js.map +1 -0
- package/dist/schema/__tests__/audio.schema.test.js +92 -1
- package/dist/schema/__tests__/audio.schema.test.js.map +1 -1
- package/dist/schema/__tests__/diagnostics.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/diagnostics.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/diagnostics.schema.test.js +31 -0
- package/dist/schema/__tests__/diagnostics.schema.test.js.map +1 -0
- package/dist/schema/__tests__/logbook-health.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/logbook-health.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/logbook-health.schema.test.js +104 -0
- package/dist/schema/__tests__/logbook-health.schema.test.js.map +1 -0
- package/dist/schema/__tests__/observability.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/observability.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/observability.schema.test.js +12 -0
- package/dist/schema/__tests__/observability.schema.test.js.map +1 -0
- package/dist/schema/__tests__/operator.schema.test.js +9 -3
- package/dist/schema/__tests__/operator.schema.test.js.map +1 -1
- package/dist/schema/__tests__/plugin-market.schema.test.js +43 -1
- package/dist/schema/__tests__/plugin-market.schema.test.js.map +1 -1
- package/dist/schema/__tests__/radio-power-support.test.js +12 -0
- package/dist/schema/__tests__/radio-power-support.test.js.map +1 -1
- package/dist/schema/__tests__/radio-profile.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/radio-profile.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/radio-profile.schema.test.js +36 -0
- package/dist/schema/__tests__/radio-profile.schema.test.js.map +1 -0
- package/dist/schema/__tests__/radio.schema.test.js +41 -1
- package/dist/schema/__tests__/radio.schema.test.js.map +1 -1
- package/dist/schema/__tests__/realtime-settings.schema.test.js +2 -1
- package/dist/schema/__tests__/realtime-settings.schema.test.js.map +1 -1
- package/dist/schema/__tests__/remote-access.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/remote-access.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/remote-access.schema.test.js +18 -0
- package/dist/schema/__tests__/remote-access.schema.test.js.map +1 -0
- package/dist/schema/android-operator-audio.schema.d.ts +174 -0
- package/dist/schema/android-operator-audio.schema.d.ts.map +1 -0
- package/dist/schema/android-operator-audio.schema.js +38 -0
- package/dist/schema/android-operator-audio.schema.js.map +1 -0
- package/dist/schema/api-response.schema.d.ts +268 -44
- package/dist/schema/api-response.schema.d.ts.map +1 -1
- package/dist/schema/audio-sidecar.schema.d.ts +75 -0
- package/dist/schema/audio-sidecar.schema.d.ts.map +1 -1
- package/dist/schema/audio-sidecar.schema.js +32 -0
- package/dist/schema/audio-sidecar.schema.js.map +1 -1
- package/dist/schema/audio.schema.d.ts +4749 -3
- package/dist/schema/audio.schema.d.ts.map +1 -1
- package/dist/schema/audio.schema.js +101 -2
- package/dist/schema/audio.schema.js.map +1 -1
- package/dist/schema/auth.schema.d.ts +171 -12
- package/dist/schema/auth.schema.d.ts.map +1 -1
- package/dist/schema/auth.schema.js +65 -1
- package/dist/schema/auth.schema.js.map +1 -1
- package/dist/schema/cpu-profile.schema.d.ts +2 -2
- package/dist/schema/cw-decoder.schema.d.ts +74 -74
- package/dist/schema/cw-keyer.schema.d.ts +20 -14
- package/dist/schema/cw-keyer.schema.d.ts.map +1 -1
- package/dist/schema/cw-keyer.schema.js +3 -0
- package/dist/schema/cw-keyer.schema.js.map +1 -1
- package/dist/schema/desktop-https.schema.d.ts +6 -6
- package/dist/schema/device-ui.schema.d.ts +6 -6
- package/dist/schema/diagnostics.schema.d.ts +163 -0
- package/dist/schema/diagnostics.schema.d.ts.map +1 -0
- package/dist/schema/diagnostics.schema.js +46 -0
- package/dist/schema/diagnostics.schema.js.map +1 -0
- package/dist/schema/ft8.schema.d.ts +12 -2
- package/dist/schema/ft8.schema.d.ts.map +1 -1
- package/dist/schema/ft8.schema.js +8 -0
- package/dist/schema/ft8.schema.js.map +1 -1
- package/dist/schema/logbook.schema.d.ts +1940 -18
- package/dist/schema/logbook.schema.d.ts.map +1 -1
- package/dist/schema/logbook.schema.js +158 -1
- package/dist/schema/logbook.schema.js.map +1 -1
- package/dist/schema/mode.schema.d.ts +2 -6
- package/dist/schema/mode.schema.d.ts.map +1 -1
- package/dist/schema/mode.schema.js +2 -6
- package/dist/schema/mode.schema.js.map +1 -1
- package/dist/schema/observability.schema.d.ts +66 -0
- package/dist/schema/observability.schema.d.ts.map +1 -0
- package/dist/schema/observability.schema.js +20 -0
- package/dist/schema/observability.schema.js.map +1 -0
- package/dist/schema/operator.schema.d.ts +6 -6
- package/dist/schema/operator.schema.d.ts.map +1 -1
- package/dist/schema/operator.schema.js +6 -1
- package/dist/schema/operator.schema.js.map +1 -1
- package/dist/schema/plugin.schema.d.ts +258 -78
- package/dist/schema/plugin.schema.d.ts.map +1 -1
- package/dist/schema/plugin.schema.js +57 -2
- package/dist/schema/plugin.schema.js.map +1 -1
- package/dist/schema/process-monitor.schema.d.ts +132 -132
- package/dist/schema/pskreporter.schema.d.ts +1 -3
- package/dist/schema/pskreporter.schema.d.ts.map +1 -1
- package/dist/schema/pskreporter.schema.js +1 -3
- package/dist/schema/pskreporter.schema.js.map +1 -1
- package/dist/schema/qso.schema.d.ts +7 -0
- package/dist/schema/qso.schema.d.ts.map +1 -1
- package/dist/schema/qso.schema.js +7 -0
- package/dist/schema/qso.schema.js.map +1 -1
- package/dist/schema/radio-capability.schema.d.ts +31 -40
- package/dist/schema/radio-capability.schema.d.ts.map +1 -1
- package/dist/schema/radio-capability.schema.js +7 -15
- package/dist/schema/radio-capability.schema.js.map +1 -1
- package/dist/schema/radio-power-support.d.ts.map +1 -1
- package/dist/schema/radio-power-support.js +3 -0
- package/dist/schema/radio-power-support.js.map +1 -1
- package/dist/schema/radio-power.schema.d.ts +87 -13
- package/dist/schema/radio-power.schema.d.ts.map +1 -1
- package/dist/schema/radio-power.schema.js +15 -8
- package/dist/schema/radio-power.schema.js.map +1 -1
- package/dist/schema/radio-profile.schema.d.ts +1240 -97
- package/dist/schema/radio-profile.schema.d.ts.map +1 -1
- package/dist/schema/radio.schema.d.ts +271 -48
- package/dist/schema/radio.schema.d.ts.map +1 -1
- package/dist/schema/radio.schema.js +44 -5
- package/dist/schema/radio.schema.js.map +1 -1
- package/dist/schema/realtime.schema.d.ts +60 -52
- package/dist/schema/realtime.schema.d.ts.map +1 -1
- package/dist/schema/realtime.schema.js +9 -1
- package/dist/schema/realtime.schema.js.map +1 -1
- package/dist/schema/slot-info.schema.d.ts +15 -9
- package/dist/schema/slot-info.schema.d.ts.map +1 -1
- package/dist/schema/slot-info.schema.js +15 -9
- package/dist/schema/slot-info.schema.js.map +1 -1
- package/dist/schema/spectrum.schema.d.ts +12 -12
- package/dist/schema/station-info.schema.d.ts +5 -0
- package/dist/schema/station-info.schema.d.ts.map +1 -1
- package/dist/schema/station-info.schema.js +5 -0
- package/dist/schema/station-info.schema.js.map +1 -1
- package/dist/schema/system.schema.d.ts +56 -6
- package/dist/schema/system.schema.d.ts.map +1 -1
- package/dist/schema/system.schema.js +22 -0
- package/dist/schema/system.schema.js.map +1 -1
- package/dist/schema/transmission.schema.d.ts +1 -7
- package/dist/schema/transmission.schema.d.ts.map +1 -1
- package/dist/schema/transmission.schema.js +2 -7
- package/dist/schema/transmission.schema.js.map +1 -1
- package/dist/schema/voice-keyer.schema.d.ts +16 -16
- package/dist/schema/websocket.schema.d.ts +3582 -555
- package/dist/schema/websocket.schema.d.ts.map +1 -1
- package/dist/schema/websocket.schema.js +160 -3
- package/dist/schema/websocket.schema.js.map +1 -1
- package/package.json +6 -1
- package/src/index.ts +3 -0
- package/src/schema/__tests__/android-operator-audio.schema.test.ts +31 -0
- package/src/schema/__tests__/assisted-queue.schema.test.ts +121 -0
- package/src/schema/__tests__/audio-sidecar.schema.test.ts +48 -0
- package/src/schema/__tests__/audio.schema.test.ts +102 -0
- package/src/schema/__tests__/diagnostics.schema.test.ts +32 -0
- package/src/schema/__tests__/logbook-health.schema.test.ts +117 -0
- package/src/schema/__tests__/observability.schema.test.ts +15 -0
- package/src/schema/__tests__/operator.schema.test.ts +11 -3
- package/src/schema/__tests__/plugin-market.schema.test.ts +49 -0
- package/src/schema/__tests__/radio-power-support.test.ts +14 -0
- package/src/schema/__tests__/radio-profile.schema.test.ts +44 -0
- package/src/schema/__tests__/radio.schema.test.ts +49 -1
- package/src/schema/__tests__/realtime-settings.schema.test.ts +2 -1
- package/src/schema/__tests__/remote-access.schema.test.ts +22 -0
- package/src/schema/android-operator-audio.schema.ts +49 -0
- package/src/schema/audio-sidecar.schema.ts +35 -0
- package/src/schema/audio.schema.ts +115 -2
- package/src/schema/auth.schema.ts +79 -1
- package/src/schema/cw-keyer.schema.ts +5 -0
- package/src/schema/diagnostics.schema.ts +55 -0
- package/src/schema/ft8.schema.ts +8 -0
- package/src/schema/logbook.schema.ts +204 -1
- package/src/schema/mode.schema.ts +2 -6
- package/src/schema/observability.schema.ts +26 -0
- package/src/schema/operator.schema.ts +7 -1
- package/src/schema/plugin.schema.ts +67 -2
- package/src/schema/pskreporter.schema.ts +1 -3
- package/src/schema/qso.schema.ts +7 -0
- package/src/schema/radio-capability.schema.ts +7 -15
- package/src/schema/radio-power-support.ts +3 -0
- package/src/schema/radio-power.schema.ts +15 -8
- package/src/schema/radio.schema.ts +55 -5
- package/src/schema/realtime.schema.ts +9 -1
- package/src/schema/slot-info.schema.ts +16 -10
- package/src/schema/station-info.schema.ts +5 -0
- package/src/schema/system.schema.ts +29 -0
- package/src/schema/transmission.schema.ts +2 -7
- package/src/schema/websocket.schema.ts +223 -6
- package/test/radio-capability-schema.test.ts +44 -0
|
@@ -40,16 +40,11 @@ export const CallRequestSchema = z.object({
|
|
|
40
40
|
|
|
41
41
|
// ========== 传输请求类型 ==========
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
* 传输请求(已在 websocket.schema.ts 中定义)
|
|
45
|
-
* TransmitRequestSchema
|
|
46
|
-
*/
|
|
43
|
+
// TransmitRequestSchema is declared in websocket.schema.ts.
|
|
47
44
|
|
|
48
45
|
// ========== 时隙映射类型 ==========
|
|
49
46
|
|
|
50
|
-
/**
|
|
51
|
-
* 操作员时隙配置
|
|
52
|
-
*/
|
|
47
|
+
/** Transmit-message templates for the six logical slots of one operator. */
|
|
53
48
|
export const OperatorSlotsSchema = z.object({
|
|
54
49
|
TX1: z.string().optional(),
|
|
55
50
|
TX2: z.string().optional(),
|
|
@@ -21,7 +21,11 @@ import type {
|
|
|
21
21
|
} from './plugin.schema.js';
|
|
22
22
|
import { ModeDescriptorSchema } from './mode.schema.js';
|
|
23
23
|
import { QSORecordSchema, TargetSelectionPriorityModeSchema } from './qso.schema.js';
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
LogBookStatisticsSchema,
|
|
26
|
+
LogbookHealthSchema,
|
|
27
|
+
LogbookOperationErrorCodeSchema,
|
|
28
|
+
} from './logbook.schema.js';
|
|
25
29
|
import { RadioInfoSchema, HamlibConfigSchema, TunerStatusSchema, TunerCapabilitiesSchema, RadioConnectionStatusSchema, ReconnectProgressSchema, CoreRadioCapabilitiesSchema, CoreCapabilityDiagnosticsSchema } from './radio.schema.js';
|
|
26
30
|
import { RadioProfileSchema, ProfileChangedEventSchema } from './radio-profile.schema.js';
|
|
27
31
|
import { UserRole } from './auth.schema.js';
|
|
@@ -34,6 +38,7 @@ import { RadioPowerStateEventSchema } from './radio-power.schema.js';
|
|
|
34
38
|
import { AudioSidecarStatusPayloadSchema } from './audio-sidecar.schema.js';
|
|
35
39
|
import { SpectrumCapabilitiesSchema, SpectrumFrameSchema, SpectrumKindSchema, SpectrumSessionControlActionSchema, SpectrumSessionControlIdSchema, SpectrumSessionStateSchema } from './spectrum.schema.js';
|
|
36
40
|
import type { RealtimeSettingsResponseData } from './realtime.schema.js';
|
|
41
|
+
import type { AndroidOperatorAudioStatus } from './android-operator-audio.schema.js';
|
|
37
42
|
import { ClockStatusSummarySchema } from './system.schema.js';
|
|
38
43
|
|
|
39
44
|
// WebSocket消息类型枚举
|
|
@@ -80,6 +85,11 @@ export enum WSMessageType {
|
|
|
80
85
|
START_OPERATOR = 'startOperator',
|
|
81
86
|
STOP_OPERATOR = 'stopOperator',
|
|
82
87
|
OPERATOR_REQUEST_CALL = 'operatorRequestCall',
|
|
88
|
+
OPERATOR_QUEUE_ENQUEUE = 'operatorQueueEnqueue',
|
|
89
|
+
OPERATOR_QUEUE_REORDER = 'operatorQueueReorder',
|
|
90
|
+
OPERATOR_QUEUE_RETRY = 'operatorQueueRetry',
|
|
91
|
+
OPERATOR_QUEUE_REMOVE = 'operatorQueueRemove',
|
|
92
|
+
OPERATOR_QUEUE_CLEAR = 'operatorQueueClear',
|
|
83
93
|
|
|
84
94
|
// ===== 客户端操作员过滤 =====
|
|
85
95
|
SET_CLIENT_ENABLED_OPERATORS = 'setClientEnabledOperators',
|
|
@@ -102,6 +112,8 @@ export enum WSMessageType {
|
|
|
102
112
|
QSO_RECORD_ADDED = 'qsoRecordAdded',
|
|
103
113
|
QSO_RECORD_UPDATED = 'qsoRecordUpdated',
|
|
104
114
|
LOGBOOK_UPDATED = 'logbookUpdated',
|
|
115
|
+
LOGBOOK_HEALTH_CHANGED = 'logbookHealthChanged',
|
|
116
|
+
LOGBOOK_WRITE_FAILED = 'logbookWriteFailed',
|
|
105
117
|
// 仅通知的日志本变更事件(专用于日志本WS)
|
|
106
118
|
LOGBOOK_CHANGE_NOTICE = 'logbookChangeNotice',
|
|
107
119
|
|
|
@@ -143,6 +155,8 @@ export enum WSMessageType {
|
|
|
143
155
|
WRITE_RADIO_CAPABILITY = 'writeRadioCapability',
|
|
144
156
|
/** 客户端请求刷新所有能力值(client → server) */
|
|
145
157
|
REFRESH_RADIO_CAPABILITIES = 'refreshRadioCapabilities',
|
|
158
|
+
/** 客户端设置 Split TX 频率(client → server) */
|
|
159
|
+
SET_SPLIT_FREQUENCY = 'setSplitFrequency',
|
|
146
160
|
|
|
147
161
|
// ===== 电台重连控制 =====
|
|
148
162
|
RADIO_STOP_RECONNECT = 'radioStopReconnect',
|
|
@@ -168,6 +182,7 @@ export enum WSMessageType {
|
|
|
168
182
|
AUTH_PUBLIC_VIEWER = 'authPublicViewer',
|
|
169
183
|
AUTH_RESULT = 'authResult',
|
|
170
184
|
AUTH_EXPIRED = 'authExpired',
|
|
185
|
+
ACCESS_DENIED = 'accessDenied',
|
|
171
186
|
|
|
172
187
|
// ===== 语音模式 =====
|
|
173
188
|
VOICE_PTT_REQUEST = 'voicePttRequest',
|
|
@@ -178,6 +193,7 @@ export enum WSMessageType {
|
|
|
178
193
|
VOICE_KEYER_PLAY = 'voiceKeyerPlay',
|
|
179
194
|
VOICE_KEYER_STOP = 'voiceKeyerStop',
|
|
180
195
|
VOICE_KEYER_STATUS_CHANGED = 'voiceKeyerStatusChanged',
|
|
196
|
+
ANDROID_OPERATOR_AUDIO_STATUS_CHANGED = 'androidOperatorAudioStatusChanged',
|
|
181
197
|
|
|
182
198
|
// ===== CW 模式 =====
|
|
183
199
|
/** CW 键控器状态变化(server → client) */
|
|
@@ -325,7 +341,14 @@ export const DecodeErrorInfoSchema = z.object({
|
|
|
325
341
|
}),
|
|
326
342
|
});
|
|
327
343
|
|
|
328
|
-
|
|
344
|
+
/**
|
|
345
|
+
* Current dial-frequency projection broadcast to plugins and clients.
|
|
346
|
+
*
|
|
347
|
+
* `frequency` is RF dial frequency in hertz; `mode` is the TX-5DR engine mode;
|
|
348
|
+
* `band` and `description` are display labels; `radioMode` is the underlying
|
|
349
|
+
* modulation when known. `source` distinguishes application changes from
|
|
350
|
+
* updates reported by the radio.
|
|
351
|
+
*/
|
|
329
352
|
export const FrequencyStateSchema = z.object({
|
|
330
353
|
frequency: z.number(),
|
|
331
354
|
mode: z.string(),
|
|
@@ -346,6 +369,9 @@ export type SpectrumControlInvocation = z.infer<typeof SpectrumControlInvocation
|
|
|
346
369
|
export const PTTStatusSchema = z.object({
|
|
347
370
|
isTransmitting: z.boolean(),
|
|
348
371
|
operatorIds: z.array(z.string()),
|
|
372
|
+
phase: z.enum(['idle', 'starting', 'on_air', 'draining', 'stopping', 'unknown']).optional(),
|
|
373
|
+
frameId: z.string().optional(),
|
|
374
|
+
source: z.enum(['digital', 'voice', 'voice-keyer', 'cw', 'tune-tone', 'manual', 'test']).optional(),
|
|
349
375
|
});
|
|
350
376
|
|
|
351
377
|
export const TuneToneStartPayloadSchema = z.object({
|
|
@@ -383,6 +409,40 @@ export const StrategyRuntimeContextSchema = z.object({
|
|
|
383
409
|
});
|
|
384
410
|
export type StrategyRuntimeContext = z.infer<typeof StrategyRuntimeContextSchema>;
|
|
385
411
|
|
|
412
|
+
export const AssistedQueueDisplayStateSchema = z.enum([
|
|
413
|
+
'TX1', 'TX2', 'TX3', 'TX4', 'TX5', 'engaged', 'closing', 'paused', 'no-response', 'later', 'review',
|
|
414
|
+
]);
|
|
415
|
+
export const AssistedQueuePauseReasonSchema = z.enum(['target-busy', 'stale']);
|
|
416
|
+
export const AssistedQueueToneSchema = z.enum(['neutral', 'active', 'success', 'warning', 'danger']);
|
|
417
|
+
export const AssistedQueueIconSchema = z.enum([
|
|
418
|
+
'circle', 'radio', 'check-circle', 'loader-circle', 'clock', 'pause', 'triangle-alert',
|
|
419
|
+
]);
|
|
420
|
+
export const AssistedQueueRowSchema = z.object({
|
|
421
|
+
entryId: z.string(),
|
|
422
|
+
callsign: z.string(),
|
|
423
|
+
order: z.number().int().nonnegative(),
|
|
424
|
+
draggable: z.boolean(),
|
|
425
|
+
displayState: AssistedQueueDisplayStateSchema,
|
|
426
|
+
tone: AssistedQueueToneSchema,
|
|
427
|
+
icon: AssistedQueueIconSchema,
|
|
428
|
+
pauseReason: AssistedQueuePauseReasonSchema.optional(),
|
|
429
|
+
noResponseCycles: z.number().int().nonnegative().optional(),
|
|
430
|
+
targetGrid: z.string().optional(),
|
|
431
|
+
lastSnr: z.number().optional(),
|
|
432
|
+
lastHeardCyclesAgo: z.number().int().nonnegative().optional(),
|
|
433
|
+
});
|
|
434
|
+
export const AssistedQueueSnapshotSchema = z.object({
|
|
435
|
+
version: z.number().int().nonnegative(),
|
|
436
|
+
activeEntryId: z.string().optional(),
|
|
437
|
+
rows: z.array(AssistedQueueRowSchema),
|
|
438
|
+
});
|
|
439
|
+
export type AssistedQueueDisplayState = z.infer<typeof AssistedQueueDisplayStateSchema>;
|
|
440
|
+
export type AssistedQueuePauseReason = z.infer<typeof AssistedQueuePauseReasonSchema>;
|
|
441
|
+
export type AssistedQueueTone = z.infer<typeof AssistedQueueToneSchema>;
|
|
442
|
+
export type AssistedQueueIcon = z.infer<typeof AssistedQueueIconSchema>;
|
|
443
|
+
export type AssistedQueueRow = z.infer<typeof AssistedQueueRowSchema>;
|
|
444
|
+
export type AssistedQueueSnapshot = z.infer<typeof AssistedQueueSnapshotSchema>;
|
|
445
|
+
|
|
386
446
|
export const StrategyRuntimeSnapshotSchema = z.object({
|
|
387
447
|
currentState: z.string(),
|
|
388
448
|
slots: z.object({
|
|
@@ -395,6 +455,8 @@ export const StrategyRuntimeSnapshotSchema = z.object({
|
|
|
395
455
|
}).optional(),
|
|
396
456
|
context: StrategyRuntimeContextSchema.optional(),
|
|
397
457
|
availableSlots: z.array(z.string()).optional(),
|
|
458
|
+
qsoLifecycleEpoch: z.number().int().nonnegative().optional(),
|
|
459
|
+
queue: AssistedQueueSnapshotSchema.optional(),
|
|
398
460
|
});
|
|
399
461
|
export type StrategyRuntimeSnapshot = z.infer<typeof StrategyRuntimeSnapshotSchema>;
|
|
400
462
|
|
|
@@ -642,8 +704,9 @@ export const WSGetStatusMessageSchema = WSBaseMessageSchema.extend({
|
|
|
642
704
|
export const OperatorStatusSchema = z.object({
|
|
643
705
|
id: z.string(),
|
|
644
706
|
isActive: z.boolean(),
|
|
645
|
-
isTransmitting: z.boolean(), //
|
|
707
|
+
isTransmitting: z.boolean(), // 是否已授予策略自动发射许可(发射开关状态)
|
|
646
708
|
isInActivePTT: z.boolean().optional(), // 该操作员的音频是否正在被实际播放
|
|
709
|
+
hasTransmitIntent: z.boolean().optional(), // 当前策略是否有可在合法周期发射的有效内容
|
|
647
710
|
currentSlot: z.string().optional(),
|
|
648
711
|
context: z.object({
|
|
649
712
|
myCall: z.string(),
|
|
@@ -796,6 +859,52 @@ export const WSOperatorRequestCallMessageSchema = WSBaseMessageSchema.extend({
|
|
|
796
859
|
}),
|
|
797
860
|
});
|
|
798
861
|
|
|
862
|
+
export const WSOperatorQueueEnqueueMessageSchema = WSBaseMessageSchema.extend({
|
|
863
|
+
type: z.literal(WSMessageType.OPERATOR_QUEUE_ENQUEUE),
|
|
864
|
+
data: z.object({
|
|
865
|
+
operatorId: z.string(),
|
|
866
|
+
callsign: z.string(),
|
|
867
|
+
selectedFrame: WSSelectedFrameSchema.optional(),
|
|
868
|
+
startIfIdle: z.boolean().optional(),
|
|
869
|
+
}),
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
export const WSOperatorQueueReorderMessageSchema = WSBaseMessageSchema.extend({
|
|
873
|
+
type: z.literal(WSMessageType.OPERATOR_QUEUE_REORDER),
|
|
874
|
+
data: z.object({
|
|
875
|
+
operatorId: z.string(),
|
|
876
|
+
entryId: z.string(),
|
|
877
|
+
beforeEntryId: z.string().nullable(),
|
|
878
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
879
|
+
}),
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
export const WSOperatorQueueRetryMessageSchema = WSBaseMessageSchema.extend({
|
|
883
|
+
type: z.literal(WSMessageType.OPERATOR_QUEUE_RETRY),
|
|
884
|
+
data: z.object({
|
|
885
|
+
operatorId: z.string(),
|
|
886
|
+
entryId: z.string(),
|
|
887
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
888
|
+
}),
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
export const WSOperatorQueueRemoveMessageSchema = WSBaseMessageSchema.extend({
|
|
892
|
+
type: z.literal(WSMessageType.OPERATOR_QUEUE_REMOVE),
|
|
893
|
+
data: z.object({
|
|
894
|
+
operatorId: z.string(),
|
|
895
|
+
entryId: z.string(),
|
|
896
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
897
|
+
}),
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
export const WSOperatorQueueClearMessageSchema = WSBaseMessageSchema.extend({
|
|
901
|
+
type: z.literal(WSMessageType.OPERATOR_QUEUE_CLEAR),
|
|
902
|
+
data: z.object({
|
|
903
|
+
operatorId: z.string(),
|
|
904
|
+
expectedVersion: z.number().int().nonnegative(),
|
|
905
|
+
}),
|
|
906
|
+
});
|
|
907
|
+
|
|
799
908
|
/**
|
|
800
909
|
* 设置客户端启用的操作员列表消息
|
|
801
910
|
*/
|
|
@@ -875,6 +984,11 @@ export const WSTransmissionLogMessageSchema = WSBaseMessageSchema.extend({
|
|
|
875
984
|
type: z.literal(WSMessageType.TRANSMISSION_LOG),
|
|
876
985
|
data: z.object({
|
|
877
986
|
operatorId: z.string(),
|
|
987
|
+
frameId: z.string(),
|
|
988
|
+
revision: z.number().int().nonnegative(),
|
|
989
|
+
playbackGeneration: z.number().int().positive(),
|
|
990
|
+
phase: z.literal('on_air'),
|
|
991
|
+
physicalConfirmed: z.literal(true),
|
|
878
992
|
time: z.string(),
|
|
879
993
|
message: z.string(),
|
|
880
994
|
frequency: z.number(),
|
|
@@ -951,6 +1065,34 @@ export const WSLogbookUpdatedMessageSchema = WSBaseMessageSchema.extend({
|
|
|
951
1065
|
|
|
952
1066
|
export type WSLogbookUpdatedMessage = z.infer<typeof WSLogbookUpdatedMessageSchema>;
|
|
953
1067
|
|
|
1068
|
+
export const WSLogbookHealthChangedMessageSchema = WSBaseMessageSchema.extend({
|
|
1069
|
+
type: z.literal(WSMessageType.LOGBOOK_HEALTH_CHANGED),
|
|
1070
|
+
data: z.object({
|
|
1071
|
+
logBookId: z.string(),
|
|
1072
|
+
health: LogbookHealthSchema,
|
|
1073
|
+
}),
|
|
1074
|
+
});
|
|
1075
|
+
|
|
1076
|
+
export type WSLogbookHealthChangedMessage = z.infer<typeof WSLogbookHealthChangedMessageSchema>;
|
|
1077
|
+
|
|
1078
|
+
export const WSLogbookWriteFailedMessageSchema = WSBaseMessageSchema.extend({
|
|
1079
|
+
type: z.literal(WSMessageType.LOGBOOK_WRITE_FAILED),
|
|
1080
|
+
data: z.object({
|
|
1081
|
+
logBookId: z.string(),
|
|
1082
|
+
operatorId: z.string().optional(),
|
|
1083
|
+
attemptId: z.string().min(8).max(128).regex(/^[A-Za-z0-9._:-]+$/).optional(),
|
|
1084
|
+
unsavedCount: z.number().int().positive().optional(),
|
|
1085
|
+
error: z.object({
|
|
1086
|
+
code: LogbookOperationErrorCodeSchema,
|
|
1087
|
+
message: z.string(),
|
|
1088
|
+
systemCode: z.string().optional(),
|
|
1089
|
+
occurredAt: z.number(),
|
|
1090
|
+
}),
|
|
1091
|
+
}).strict(),
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
export type WSLogbookWriteFailedMessage = z.infer<typeof WSLogbookWriteFailedMessageSchema>;
|
|
1095
|
+
|
|
954
1096
|
/**
|
|
955
1097
|
* 电台状态变化消息
|
|
956
1098
|
*/
|
|
@@ -973,6 +1115,8 @@ export const WSRadioStatusChangedMessageSchema = WSBaseMessageSchema.extend({
|
|
|
973
1115
|
meterCapabilities: MeterCapabilitiesSchema.optional(), // 电台数值表能力(connected=true 时应始终携带)
|
|
974
1116
|
/** @deprecated Tuner capability is now in radioCapabilityList event. Kept for backward compat. */
|
|
975
1117
|
tunerCapabilities: TunerCapabilitiesSchema.optional(),
|
|
1118
|
+
/** 虚拟频差实际生效状态(多op发射时为 false) */
|
|
1119
|
+
fakeFrequencyEffective: z.boolean().optional(),
|
|
976
1120
|
}),
|
|
977
1121
|
});
|
|
978
1122
|
|
|
@@ -1199,6 +1343,8 @@ export const WSTextMessageSchema = WSBaseMessageSchema.extend({
|
|
|
1199
1343
|
text: z.string(),
|
|
1200
1344
|
color: z.enum(['success', 'warning', 'danger', 'default']).optional(),
|
|
1201
1345
|
timeout: z.number().nullable().optional(), // null 表示需要手动关闭,number 表示自动关闭的毫秒数
|
|
1346
|
+
/** 服务端创建消息的时间戳,用于前端识别后台积压消息 */
|
|
1347
|
+
createdAtMs: z.number().optional(),
|
|
1202
1348
|
/** i18n key,前端有此字段时优先翻译显示 */
|
|
1203
1349
|
key: z.string().optional(),
|
|
1204
1350
|
/** i18n 插值参数 */
|
|
@@ -1255,6 +1401,19 @@ export const WSWriteRadioCapabilityMessageSchema = WSBaseMessageSchema.extend({
|
|
|
1255
1401
|
|
|
1256
1402
|
export type WSWriteRadioCapabilityMessage = z.infer<typeof WSWriteRadioCapabilityMessageSchema>;
|
|
1257
1403
|
|
|
1404
|
+
export const SetSplitFrequencyPayloadSchema = z.object({
|
|
1405
|
+
txFrequency: z.number().finite().int().min(1_000_000).max(1_000_000_000),
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
export type SetSplitFrequencyPayload = z.infer<typeof SetSplitFrequencyPayloadSchema>;
|
|
1409
|
+
|
|
1410
|
+
export const WSSetSplitFrequencyMessageSchema = WSBaseMessageSchema.extend({
|
|
1411
|
+
type: z.literal(WSMessageType.SET_SPLIT_FREQUENCY),
|
|
1412
|
+
data: SetSplitFrequencyPayloadSchema,
|
|
1413
|
+
});
|
|
1414
|
+
|
|
1415
|
+
export type WSSetSplitFrequencyMessage = z.infer<typeof WSSetSplitFrequencyMessageSchema>;
|
|
1416
|
+
|
|
1258
1417
|
// ===== 认证相关消息 =====
|
|
1259
1418
|
|
|
1260
1419
|
/**
|
|
@@ -1291,6 +1450,20 @@ export const WSAuthPublicViewerMessageSchema = WSBaseMessageSchema.extend({
|
|
|
1291
1450
|
|
|
1292
1451
|
export type WSAuthPublicViewerMessage = z.infer<typeof WSAuthPublicViewerMessageSchema>;
|
|
1293
1452
|
|
|
1453
|
+
export const WSAccessDeniedDataSchema = z.object({
|
|
1454
|
+
reason: z.enum([
|
|
1455
|
+
'capacity_reached',
|
|
1456
|
+
'origin_not_allowed',
|
|
1457
|
+
'authentication_timeout',
|
|
1458
|
+
'handshake_timeout',
|
|
1459
|
+
'ip_limit_reached',
|
|
1460
|
+
]),
|
|
1461
|
+
current: z.number().int().nonnegative().optional(),
|
|
1462
|
+
limit: z.number().int().positive().optional(),
|
|
1463
|
+
retryAfterMs: z.number().int().nonnegative().optional(),
|
|
1464
|
+
});
|
|
1465
|
+
export type WSAccessDeniedData = z.infer<typeof WSAccessDeniedDataSchema>;
|
|
1466
|
+
|
|
1294
1467
|
/**
|
|
1295
1468
|
* 认证结果(服务端到客户端)
|
|
1296
1469
|
*/
|
|
@@ -1413,6 +1586,11 @@ export const WSMessageSchema = z.discriminatedUnion('type', [
|
|
|
1413
1586
|
WSStartOperatorMessageSchema,
|
|
1414
1587
|
WSStopOperatorMessageSchema,
|
|
1415
1588
|
WSOperatorRequestCallMessageSchema,
|
|
1589
|
+
WSOperatorQueueEnqueueMessageSchema,
|
|
1590
|
+
WSOperatorQueueReorderMessageSchema,
|
|
1591
|
+
WSOperatorQueueRetryMessageSchema,
|
|
1592
|
+
WSOperatorQueueRemoveMessageSchema,
|
|
1593
|
+
WSOperatorQueueClearMessageSchema,
|
|
1416
1594
|
|
|
1417
1595
|
// 音量控制消息
|
|
1418
1596
|
WSSetVolumeGainMessageSchema,
|
|
@@ -1422,6 +1600,8 @@ export const WSMessageSchema = z.discriminatedUnion('type', [
|
|
|
1422
1600
|
WSQSORecordAddedMessageSchema,
|
|
1423
1601
|
WSQSORecordUpdatedMessageSchema,
|
|
1424
1602
|
WSLogbookUpdatedMessageSchema,
|
|
1603
|
+
WSLogbookHealthChangedMessageSchema,
|
|
1604
|
+
WSLogbookWriteFailedMessageSchema,
|
|
1425
1605
|
|
|
1426
1606
|
// 客户端启用操作员列表消息
|
|
1427
1607
|
WSSetClientEnabledOperatorsMessageSchema,
|
|
@@ -1446,6 +1626,7 @@ export const WSMessageSchema = z.discriminatedUnion('type', [
|
|
|
1446
1626
|
|
|
1447
1627
|
// 频率管理消息
|
|
1448
1628
|
WSFrequencyChangedMessageSchema,
|
|
1629
|
+
WSSetSplitFrequencyMessageSchema,
|
|
1449
1630
|
|
|
1450
1631
|
// PTT状态管理消息
|
|
1451
1632
|
WSPTTStatusChangedMessageSchema,
|
|
@@ -1518,6 +1699,10 @@ export const TransmitRequestSchema = z.object({
|
|
|
1518
1699
|
transmission: z.string(),
|
|
1519
1700
|
/** 是否覆盖同一操作员在同一时隙的现有 TX 帧(自动重决策时为 true) */
|
|
1520
1701
|
replaceExisting: z.boolean().optional(),
|
|
1702
|
+
source: z.enum(['standard-qso', 'plugin', 'late-decode', 'operator-edit', 'manual']).optional(),
|
|
1703
|
+
reason: z.string().optional(),
|
|
1704
|
+
/** Host command epoch used to tombstone stale encode/mix callbacks. */
|
|
1705
|
+
decisionEpoch: z.number().int().nonnegative().optional(),
|
|
1521
1706
|
});
|
|
1522
1707
|
|
|
1523
1708
|
export type TransmitRequest = z.infer<typeof TransmitRequestSchema>;
|
|
@@ -1530,6 +1715,9 @@ export type TransmitRequest = z.infer<typeof TransmitRequestSchema>;
|
|
|
1530
1715
|
export const TransmissionCompleteInfoSchema = z.object({
|
|
1531
1716
|
operatorId: z.string(),
|
|
1532
1717
|
success: z.boolean(),
|
|
1718
|
+
frameId: z.string().optional(),
|
|
1719
|
+
physicalConfirmed: z.boolean().optional(),
|
|
1720
|
+
terminalReason: z.string().optional(),
|
|
1533
1721
|
duration: z.number().optional(),
|
|
1534
1722
|
error: z.string().optional(),
|
|
1535
1723
|
mixedWith: z.array(z.string()).optional(), // 与其他操作员混音的ID列表
|
|
@@ -1562,6 +1750,11 @@ export interface DigitalRadioEngineEvents {
|
|
|
1562
1750
|
transmissionComplete: (info: TransmissionCompleteInfo) => void;
|
|
1563
1751
|
transmissionLog: (data: {
|
|
1564
1752
|
operatorId: string;
|
|
1753
|
+
frameId: string;
|
|
1754
|
+
revision: number;
|
|
1755
|
+
playbackGeneration: number;
|
|
1756
|
+
phase: 'on_air';
|
|
1757
|
+
physicalConfirmed: true;
|
|
1565
1758
|
time: string;
|
|
1566
1759
|
message: string;
|
|
1567
1760
|
frequency: number;
|
|
@@ -1584,7 +1777,7 @@ export interface DigitalRadioEngineEvents {
|
|
|
1584
1777
|
// 连接事件
|
|
1585
1778
|
connected: () => void;
|
|
1586
1779
|
reconnecting: (data: { attempt: number; delayMs: number }) => void;
|
|
1587
|
-
disconnected: () => void;
|
|
1780
|
+
disconnected: (data?: WSAccessDeniedData) => void;
|
|
1588
1781
|
handshakeComplete: (data: z.infer<typeof WSServerHandshakeCompleteMessageSchema>['data']) => void;
|
|
1589
1782
|
connectionReplaced: (data: { reason: string }) => void;
|
|
1590
1783
|
error: (error: Error) => void;
|
|
@@ -1609,6 +1802,8 @@ export interface DigitalRadioEngineEvents {
|
|
|
1609
1802
|
qsoRecordAdded: (data: { operatorId: string; logBookId: string; qsoRecord: z.infer<typeof QSORecordSchema> }) => void;
|
|
1610
1803
|
qsoRecordUpdated: (data: { operatorId: string; logBookId: string; qsoRecord: z.infer<typeof QSORecordSchema> }) => void;
|
|
1611
1804
|
logbookUpdated: (data: { logBookId: string; statistics: z.infer<typeof LogBookStatisticsSchema>; operatorId?: string }) => void;
|
|
1805
|
+
logbookHealthChanged: (data: { logBookId: string; health: z.infer<typeof LogbookHealthSchema> }) => void;
|
|
1806
|
+
logbookWriteFailed: (data: z.infer<typeof WSLogbookWriteFailedMessageSchema>['data']) => void;
|
|
1612
1807
|
textMessage: (data: z.infer<typeof WSTextMessageSchema>['data']) => void;
|
|
1613
1808
|
|
|
1614
1809
|
/** @deprecated Tuner state is now delivered via radioCapabilityChanged event (id='tuner_switch') */
|
|
@@ -1636,11 +1831,13 @@ export interface DigitalRadioEngineEvents {
|
|
|
1636
1831
|
authRequired: (data: { allowPublicViewing: boolean }) => void;
|
|
1637
1832
|
authResult: (data: { success: boolean; role?: UserRole; label?: string; operatorIds?: string[]; error?: string }) => void;
|
|
1638
1833
|
authExpired: (data: { reason?: string }) => void;
|
|
1834
|
+
accessDenied: (data: WSAccessDeniedData) => void;
|
|
1639
1835
|
|
|
1640
1836
|
// 语音模式事件
|
|
1641
1837
|
voicePttLockChanged: (data: VoicePTTLock) => void;
|
|
1642
1838
|
voiceRadioModeChanged: (data: { radioMode: string }) => void;
|
|
1643
1839
|
voiceKeyerStatusChanged: (data: VoiceKeyerStatus) => void;
|
|
1840
|
+
androidOperatorAudioStatusChanged: (data: AndroidOperatorAudioStatus) => void;
|
|
1644
1841
|
|
|
1645
1842
|
// CW 模式事件
|
|
1646
1843
|
cwKeyerStatusChanged: (data: CWKeyerStatus) => void;
|
|
@@ -1695,8 +1892,28 @@ export interface DigitalRadioEngineEvents {
|
|
|
1695
1892
|
operatorSlotChanged: (data: { operatorId: string; slot: string }) => void;
|
|
1696
1893
|
operatorSlotContentChanged: (data: { operatorId: string; slot: string; content: string }) => void;
|
|
1697
1894
|
operatorFrequencyChanged: (data: { operatorId: string; frequency: number }) => void;
|
|
1698
|
-
operatorTransmitCyclesChanged: (data: {
|
|
1699
|
-
|
|
1895
|
+
operatorTransmitCyclesChanged: (data: {
|
|
1896
|
+
operatorId: string;
|
|
1897
|
+
transmitCycles: number[];
|
|
1898
|
+
commandEpoch?: number;
|
|
1899
|
+
source?: 'manual' | 'plugin' | 'late-decode' | 'slot-auto';
|
|
1900
|
+
reason?: string;
|
|
1901
|
+
}) => void;
|
|
1902
|
+
qsoLifecycleChanged: (data: {
|
|
1903
|
+
operatorId: string;
|
|
1904
|
+
lifecycleEpoch: number;
|
|
1905
|
+
runtimeGeneration?: number;
|
|
1906
|
+
}) => void;
|
|
1907
|
+
recordQSO: (data: {
|
|
1908
|
+
operatorId: string;
|
|
1909
|
+
qsoLifecycleId?: string;
|
|
1910
|
+
qsoLifecycleEpoch?: number;
|
|
1911
|
+
qsoRuntimeGeneration?: number;
|
|
1912
|
+
qsoRecord: z.infer<typeof QSORecordSchema>;
|
|
1913
|
+
retryAttemptId?: string;
|
|
1914
|
+
resolve?: (record: z.infer<typeof QSORecordSchema>) => void;
|
|
1915
|
+
reject?: (error: unknown) => void;
|
|
1916
|
+
}) => void;
|
|
1700
1917
|
checkHasWorkedCallsign: (data: { operatorId: string; callsign: string; requestId: string }) => void;
|
|
1701
1918
|
hasWorkedCallsignResponse: (data: { requestId: string; hasWorked: boolean }) => void;
|
|
1702
1919
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
CAPABILITY_IDS,
|
|
4
|
+
CapabilityDescriptorSchema,
|
|
5
|
+
CapabilityStateSchema,
|
|
6
|
+
type CapabilityId,
|
|
7
|
+
} from '../src/schema/radio-capability.schema';
|
|
8
|
+
|
|
9
|
+
describe('CAPABILITY_IDS', () => {
|
|
10
|
+
it('includes radio_mode for dynamic voice radio mode discovery', () => {
|
|
11
|
+
const id: CapabilityId = 'radio_mode';
|
|
12
|
+
|
|
13
|
+
expect(CAPABILITY_IDS).toContain(id);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('accepts radio_mode as an enum capability descriptor and state', () => {
|
|
17
|
+
expect(CapabilityDescriptorSchema.parse({
|
|
18
|
+
id: 'radio_mode',
|
|
19
|
+
category: 'operation',
|
|
20
|
+
valueType: 'enum',
|
|
21
|
+
options: [{ value: 'USB' }, { value: 'WFM' }],
|
|
22
|
+
readable: true,
|
|
23
|
+
writable: false,
|
|
24
|
+
updateMode: 'polling',
|
|
25
|
+
labelI18nKey: 'radio:capability.radio_mode.label',
|
|
26
|
+
hasSurfaceControl: false,
|
|
27
|
+
})).toMatchObject({
|
|
28
|
+
id: 'radio_mode',
|
|
29
|
+
valueType: 'enum',
|
|
30
|
+
options: [{ value: 'USB' }, { value: 'WFM' }],
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(CapabilityStateSchema.parse({
|
|
34
|
+
id: 'radio_mode',
|
|
35
|
+
supported: true,
|
|
36
|
+
value: 'WFM',
|
|
37
|
+
updatedAt: 1,
|
|
38
|
+
})).toMatchObject({
|
|
39
|
+
id: 'radio_mode',
|
|
40
|
+
supported: true,
|
|
41
|
+
value: 'WFM',
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
});
|