Wideband autonomous SDR analysis engine forked from sdr-visual-suite
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

75 строки
3.2KB

  1. (function (global) {
  2. const DEFAULT_TIMEOUT_MS = 5000;
  3. function toErrorMessage(err) {
  4. if (!err) return 'request failed';
  5. if (typeof err === 'string') return err;
  6. return err.message || 'request failed';
  7. }
  8. function createClient(opts = {}) {
  9. const baseUrl = opts.baseUrl || '';
  10. const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
  11. async function request(path, options = {}) {
  12. const controller = new AbortController();
  13. const start = performance.now();
  14. const timer = setTimeout(() => controller.abort(), timeoutMs);
  15. const init = {
  16. method: options.method || 'GET',
  17. headers: { ...(options.headers || {}) },
  18. signal: controller.signal,
  19. };
  20. if (options.body !== undefined) {
  21. init.headers['Content-Type'] = 'application/json';
  22. init.body = JSON.stringify(options.body);
  23. }
  24. try {
  25. const res = await fetch(`${baseUrl}${path}`, init);
  26. const durationMs = Math.round(performance.now() - start);
  27. const contentType = res.headers.get('content-type') || '';
  28. let data = null;
  29. if (contentType.includes('application/json')) data = await res.json();
  30. else data = await res.text();
  31. if (!res.ok) {
  32. const error = typeof data === 'string' ? data : (data?.error || `http ${res.status}`);
  33. return { ok: false, status: res.status, error, data, meta: { duration_ms: durationMs } };
  34. }
  35. return { ok: true, status: res.status, data, meta: { duration_ms: durationMs } };
  36. } catch (err) {
  37. const durationMs = Math.round(performance.now() - start);
  38. return { ok: false, status: 0, error: toErrorMessage(err), data: null, meta: { duration_ms: durationMs } };
  39. } finally {
  40. clearTimeout(timer);
  41. }
  42. }
  43. return {
  44. getConfig: () => request('/api/config'),
  45. postConfig: (payload) => request('/api/config', { method: 'POST', body: payload }),
  46. postSettings: (payload) => request('/api/sdr/settings', { method: 'POST', body: payload }),
  47. getSignals: () => request('/api/signals'),
  48. getStats: () => request('/api/stats'),
  49. getGPU: () => request('/api/gpu'),
  50. getPolicy: () => request('/api/pipeline/policy'),
  51. getRecommendations: () => request('/api/pipeline/recommendations'),
  52. getRefinement: () => request('/api/refinement'),
  53. getEvents: ({ limit, since } = {}) => {
  54. const params = new URLSearchParams();
  55. if (Number.isFinite(limit) && limit > 0) params.set('limit', String(limit));
  56. if (Number.isFinite(since) && since > 0) params.set('since', String(since));
  57. const suffix = params.toString() ? `?${params.toString()}` : '';
  58. return request(`/api/events${suffix}`);
  59. },
  60. getRecordings: () => request('/api/recordings'),
  61. getRecording: (id) => request(`/api/recordings/${encodeURIComponent(id)}`),
  62. decodeRecording: (id, mode) => request(`/api/recordings/${encodeURIComponent(id)}/decode?mode=${encodeURIComponent(mode || '')}`),
  63. getDecoders: () => request('/api/decoders'),
  64. getTelemetryLive: () => request('/api/debug/telemetry/live'),
  65. };
  66. }
  67. global.SpectreApi = { createClient };
  68. })(window);