UNPKG

arrakis-js

Version:

Arrakis Javascript client library

148 lines (147 loc) 5.84 kB
// Copyright (c) 2022, California Institute of Technology and contributors // // You should have received a copy of the licensing terms for this // software included in the file "LICENSE" located in the top-level // directory of this package. If you did not, you can view a copy at // https://git.ligo.org/ngdd/arrakis-js/-/raw/main/LICENSE import { DEFAULT_ARRAKIS_SERVER } from "./constants"; import * as Arrow from "apache-arrow"; import { Channel } from "./channel"; import { SeriesBlock } from "./block"; /** * Count channels matching a set of conditions * * @param {string} [pattern] - Channel pattern to match channels with, using regular expressions * @param {string|Array<string>} [dataType] - If set, find all channels with these data types * @param {number} [minRate] - Minimum sample rate for channels * @param {number} [maxRate] - Maximum sample rate for channels * @param {string|Array<string>} [publisher] - If set, find all channels associated with these publishers * @returns {Promise<number>} A promise that resolves to the number of channels matching query */ export async function count(pattern, dataType, minRate, maxRate, publisher) { const response = await fetch(`${DEFAULT_ARRAKIS_SERVER}/count`, { method: "POST", body: JSON.stringify({ pattern, dataType, minRate, maxRate, publisher }), }).then((res) => res.json()); return response.count; } /** * Find channels matching a set of conditions and stream results via Server-Sent Events (SSE) * * @param {string} [pattern] - Channel pattern to match channels with, using regular expressions * @param {string|Array<string>} [dataType] - If set, find all channels with these data types * @param {number} [minRate] - Minimum sample rate for channels * @param {number} [maxRate] - Maximum sample rate for channels * @param {string|Array<string>} [publisher] - If set, find all channels associated with these publishers * @returns {AsyncGenerator<Channel>} An async generator that yields channel objects as they are received via SSE */ export async function* find(pattern, dataType, minRate, maxRate, publisher) { // Build query parameters const params = new URLSearchParams(); if (pattern) params.append("pattern", pattern); if (dataType) { if (Array.isArray(dataType)) { params.append("dataType", dataType.join(",")); } else { params.append("dataType", dataType); } } if (minRate) params.append("minRate", minRate.toString()); if (maxRate) params.append("maxRate", maxRate.toString()); if (publisher) { if (Array.isArray(publisher)) { params.append("publisher", publisher.join(",")); } else { params.append("publisher", publisher); } } const findUrl = `${DEFAULT_ARRAKIS_SERVER}/find?${params.toString()}`; let queue = Array(); let resolveNext; let done = false; const eventSource = new EventSource(findUrl); // Fulfill the promise once the next channel // is received, inside onmessage eventSource.onmessage = (e) => { const message = JSON.parse(e.data); if (message.done) { done = true; eventSource.close(); return; } if (message.channel) { queue.push(Channel.fromJson(message.channel)); if (resolveNext) { // if there is a promise waiting, // fulfill it with the first channel in the queue resolveNext(queue.shift()); // reset the promise callback resolveNext = undefined; } } }; eventSource.onerror = (err) => { console.error("SSE connection error:", err); eventSource.close(); done = true; }; try { while (!done || queue.length > 0) { // yield if there are channels in the queue if (queue.length > 0) { yield queue.shift(); } else { // otherwise, wait for next channel with a promise yield new Promise((resolve) => { resolveNext = resolve; }); } } } finally { eventSource.close(); } } /** * Fetch timeseries data for a list of channels within a time range * * @param {string[]} channels - A list of channels to request * @param {number} start - GPS start time, in seconds * @param {number} end - GPS end time, in seconds * @returns {Promise<SeriesBlock>} A promise that resolves to a SeriesBlock containing the requested data */ export async function* stream(channels, start, end) { // Create a stream reader for Arrow record batches const response = await fetch("http://localhost:8000/stream", { method: "POST", body: JSON.stringify({ channels, start, end }), }); if (!response.ok || !response.body) throw new Error("Failed to fetch data"); const reader = await Arrow.RecordBatchStreamReader.from(response.body); for await (const batch of reader) { yield SeriesBlock.fromBatch(batch); } } /** * Get channel metadata for channels requested * * @param {string[]} channels - List of channels to request * @returns {Promise<Record<string, Channel>>} A promise that resolves to a mapping of channel names to channel metadata */ export async function describe(channels) { const params = new URLSearchParams(); params.append("channels", channels.join(",")); const response = await fetch(`${DEFAULT_ARRAKIS_SERVER}/describe?${params.toString()}`).then((res) => res.json()); const result = {}; for (const [name, data] of Object.entries(response)) { result[name] = Channel.fromJson(data); } return result; }