refactor: convert scheduler to typed module
This commit is contained in:
@@ -1258,7 +1258,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
|||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (rigListChanged) {
|
if (rigListChanged) {
|
||||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
@@ -3593,7 +3593,7 @@ async function switchRigFromSelect(selectEl) {
|
|||||||
updateRigSubtitle(lastActiveRigId);
|
updateRigSubtitle(lastActiveRigId);
|
||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||||
@@ -4333,10 +4333,8 @@ async function initializeApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function initSettingsUI() {
|
function initSettingsUI() {
|
||||||
if (typeof initScheduler === "function") {
|
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||||
initScheduler(lastActiveRigId, authRole);
|
window.trx.modules.scheduler?.wireEvents();
|
||||||
wireSchedulerEvents();
|
|
||||||
}
|
|
||||||
if (typeof initBackgroundDecode === "function") {
|
if (typeof initBackgroundDecode === "function") {
|
||||||
initBackgroundDecode(lastActiveRigId, authRole);
|
initBackgroundDecode(lastActiveRigId, authRole);
|
||||||
wireBackgroundDecodeEvents();
|
wireBackgroundDecodeEvents();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const pluginGroups = {
|
|||||||
};
|
};
|
||||||
const loaded = /* @__PURE__ */ new Set();
|
const loaded = /* @__PURE__ */ new Set();
|
||||||
const loading = /* @__PURE__ */ new Map();
|
const loading = /* @__PURE__ */ new Map();
|
||||||
const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js", "/vchan.js", "/bookmarks.js"]);
|
const modulePlugins = /* @__PURE__ */ new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js", "/vchan.js", "/bookmarks.js", "/scheduler.js"]);
|
||||||
function loadLegacyScript(path) {
|
function loadLegacyScript(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
|
|||||||
@@ -24,8 +24,9 @@
|
|||||||
centerHz: document.getElementById("scheduler-sat-center-hz")
|
centerHz: document.getElementById("scheduler-sat-center-hz")
|
||||||
};
|
};
|
||||||
let editIdx = null;
|
let editIdx = null;
|
||||||
|
let eventsWired = false;
|
||||||
function getBridge() {
|
function getBridge() {
|
||||||
return satSchedulerWindow.schedulerBridge ?? null;
|
return satSchedulerWindow.trx?.modules?.scheduler ?? null;
|
||||||
}
|
}
|
||||||
function getConfig() {
|
function getConfig() {
|
||||||
const b = getBridge();
|
const b = getBridge();
|
||||||
@@ -230,6 +231,8 @@
|
|||||||
if (dom.norad) dom.norad.value = parts[1] || "";
|
if (dom.norad) dom.norad.value = parts[1] || "";
|
||||||
}
|
}
|
||||||
function wireEvents() {
|
function wireEvents() {
|
||||||
|
if (eventsWired) return;
|
||||||
|
eventsWired = true;
|
||||||
if (dom.enabled) {
|
if (dom.enabled) {
|
||||||
const enabledInput = dom.enabled;
|
const enabledInput = dom.enabled;
|
||||||
dom.enabled.addEventListener("change", function() {
|
dom.enabled.addEventListener("change", function() {
|
||||||
@@ -255,5 +258,9 @@
|
|||||||
renderPassStatus,
|
renderPassStatus,
|
||||||
collectSatelliteConfig
|
collectSatelliteConfig
|
||||||
};
|
};
|
||||||
|
if (getBridge()) {
|
||||||
|
wireEvents();
|
||||||
|
renderSection();
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
})();
|
})();
|
||||||
|
|||||||
+1188
-1160
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,6 @@ await build({
|
|||||||
"plugin-runtime": path.join(sourceDir, "plugin-runtime.ts"),
|
"plugin-runtime": path.join(sourceDir, "plugin-runtime.ts"),
|
||||||
screenshot: path.join(sourceDir, "screenshot.ts"),
|
screenshot: path.join(sourceDir, "screenshot.ts"),
|
||||||
"webgl-renderer": path.join(sourceDir, "webgl-renderer.ts"),
|
"webgl-renderer": path.join(sourceDir, "webgl-renderer.ts"),
|
||||||
scheduler: path.join(sourceDir, "plugins", "scheduler.js"),
|
|
||||||
},
|
},
|
||||||
outdir: outputDir,
|
outdir: outputDir,
|
||||||
bundle: false,
|
bundle: false,
|
||||||
@@ -52,6 +51,7 @@ await build({
|
|||||||
"sat-scheduler": path.join(sourceDir, "plugins", "sat-scheduler.ts"),
|
"sat-scheduler": path.join(sourceDir, "plugins", "sat-scheduler.ts"),
|
||||||
vchan: path.join(sourceDir, "plugins", "vchan.ts"),
|
vchan: path.join(sourceDir, "plugins", "vchan.ts"),
|
||||||
bookmarks: path.join(sourceDir, "plugins", "bookmarks.ts"),
|
bookmarks: path.join(sourceDir, "plugins", "bookmarks.ts"),
|
||||||
|
scheduler: path.join(sourceDir, "plugins", "scheduler.ts"),
|
||||||
},
|
},
|
||||||
outdir: outputDir,
|
outdir: outputDir,
|
||||||
bundle: true,
|
bundle: true,
|
||||||
|
|||||||
@@ -1269,7 +1269,7 @@ function applyRigList(activeRigId, rigIds, displayNames) {
|
|||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (rigListChanged) {
|
if (rigListChanged) {
|
||||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||||
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
if (typeof bmPopulateScopePicker === "function") bmPopulateScopePicker();
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
@@ -3935,7 +3935,7 @@ async function switchRigFromSelect(selectEl) {
|
|||||||
updateRigSubtitle(lastActiveRigId);
|
updateRigSubtitle(lastActiveRigId);
|
||||||
updateRigIdentitySummary(lastActiveRigId);
|
updateRigIdentitySummary(lastActiveRigId);
|
||||||
window.trxUi?.setActiveRig(lastActiveRigId);
|
window.trxUi?.setActiveRig(lastActiveRigId);
|
||||||
if (typeof setSchedulerRig === "function") setSchedulerRig(lastActiveRigId);
|
window.trx.modules.scheduler?.setRig(lastActiveRigId);
|
||||||
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
if (typeof setBackgroundDecodeRig === "function") setBackgroundDecodeRig(lastActiveRigId);
|
||||||
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
if (typeof bmFetch === "function") bmFetch(document.getElementById("bm-category-filter")?.value || "");
|
||||||
window.trx.modules.map?.syncAprsReceiverMarker();
|
window.trx.modules.map?.syncAprsReceiverMarker();
|
||||||
@@ -4759,10 +4759,8 @@ async function initializeApp() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function initSettingsUI() {
|
function initSettingsUI() {
|
||||||
if (typeof initScheduler === "function") {
|
window.trx.modules.scheduler?.initialize(lastActiveRigId, authRole);
|
||||||
initScheduler(lastActiveRigId, authRole);
|
window.trx.modules.scheduler?.wireEvents();
|
||||||
wireSchedulerEvents();
|
|
||||||
}
|
|
||||||
if (typeof initBackgroundDecode === "function") {
|
if (typeof initBackgroundDecode === "function") {
|
||||||
initBackgroundDecode(lastActiveRigId, authRole);
|
initBackgroundDecode(lastActiveRigId, authRole);
|
||||||
wireBackgroundDecodeEvents();
|
wireBackgroundDecodeEvents();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const pluginGroups: Readonly<Record<PluginGroup, readonly string[]>> = {
|
|||||||
|
|
||||||
const loaded = new Set<string>();
|
const loaded = new Set<string>();
|
||||||
const loading = new Map<string, Promise<void>>();
|
const loading = new Map<string, Promise<void>>();
|
||||||
const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js", "/vchan.js", "/bookmarks.js"]);
|
const modulePlugins = new Set(["/ft8.js", "/ft4.js", "/ft2.js", "/wspr.js", "/cw.js", "/vdes.js", "/wefax.js", "/background-decode.js", "/ais.js", "/aprs.js", "/hf-aprs.js", "/sat.js", "/sat-scheduler.js", "/vchan.js", "/bookmarks.js", "/scheduler.js"]);
|
||||||
|
|
||||||
function loadLegacyScript(path: string): Promise<void> {
|
function loadLegacyScript(path: string): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|||||||
+13
-8
@@ -7,13 +7,12 @@ import type {
|
|||||||
SatelliteScheduleConfig,
|
SatelliteScheduleConfig,
|
||||||
SatelliteScheduleEntry,
|
SatelliteScheduleEntry,
|
||||||
SatelliteSchedulerApi,
|
SatelliteSchedulerApi,
|
||||||
SatelliteSchedulerBridge,
|
|
||||||
SatelliteSchedulerStatus,
|
SatelliteSchedulerStatus,
|
||||||
SchedulerConfigWithSatellites,
|
|
||||||
} from "./satellite-types";
|
} from "./satellite-types";
|
||||||
|
import type { SchedulerConfig, SchedulerService } from "./scheduler-types.js";
|
||||||
|
|
||||||
interface SatelliteSchedulerWindow {
|
interface SatelliteSchedulerWindow {
|
||||||
schedulerBridge?: SatelliteSchedulerBridge;
|
trx?: { modules?: { scheduler?: SchedulerService } };
|
||||||
satScheduler?: SatelliteSchedulerApi;
|
satScheduler?: SatelliteSchedulerApi;
|
||||||
trxUi?: { notify(message: string, options: { kind: "error" }): void };
|
trxUi?: { notify(message: string, options: { kind: "error" }): void };
|
||||||
}
|
}
|
||||||
@@ -49,15 +48,15 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
|||||||
|
|
||||||
// ── Local state ───────────────────────────────────────────────────
|
// ── Local state ───────────────────────────────────────────────────
|
||||||
let editIdx: number | null = null;
|
let editIdx: number | null = null;
|
||||||
|
let eventsWired = false;
|
||||||
|
|
||||||
// ── Scheduler bridge ──────────────────────────────────────────────
|
// ── Scheduler bridge ──────────────────────────────────────────────
|
||||||
// These accessors call into scheduler.js via window.schedulerBridge,
|
// These accessors use the shared typed scheduler service when available.
|
||||||
// which is set up by scheduler.js after it initializes.
|
function getBridge(): SchedulerService | null {
|
||||||
function getBridge(): SatelliteSchedulerBridge | null {
|
return satSchedulerWindow.trx?.modules?.scheduler ?? null;
|
||||||
return satSchedulerWindow.schedulerBridge ?? null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConfig(): SchedulerConfigWithSatellites | null {
|
function getConfig(): SchedulerConfig | null {
|
||||||
const b = getBridge();
|
const b = getBridge();
|
||||||
return b?.getConfig() ?? null;
|
return b?.getConfig() ?? null;
|
||||||
}
|
}
|
||||||
@@ -309,6 +308,8 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
|||||||
|
|
||||||
// ── Wire all events ───────────────────────────────────────────────
|
// ── Wire all events ───────────────────────────────────────────────
|
||||||
function wireEvents() {
|
function wireEvents() {
|
||||||
|
if (eventsWired) return;
|
||||||
|
eventsWired = true;
|
||||||
if (dom.enabled) {
|
if (dom.enabled) {
|
||||||
const enabledInput = dom.enabled;
|
const enabledInput = dom.enabled;
|
||||||
dom.enabled.addEventListener("change", function () {
|
dom.enabled.addEventListener("change", function () {
|
||||||
@@ -334,4 +335,8 @@ const satSchedulerWindow = window as unknown as SatelliteSchedulerWindow;
|
|||||||
renderPassStatus: renderPassStatus,
|
renderPassStatus: renderPassStatus,
|
||||||
collectSatelliteConfig: collectSatelliteConfig,
|
collectSatelliteConfig: collectSatelliteConfig,
|
||||||
};
|
};
|
||||||
|
if (getBridge()) {
|
||||||
|
wireEvents();
|
||||||
|
renderSection();
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import type { SatelliteScheduleConfig, SatelliteSchedulerApi } from "./satellite-types.js";
|
||||||
|
|
||||||
|
export type SchedulerMode = "disabled" | "grayline" | "time_span";
|
||||||
|
|
||||||
|
export interface SchedulerBookmark {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
freq_hz: number;
|
||||||
|
mode: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GraylineConfig {
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
transition_window_min: number;
|
||||||
|
day_bookmark_id: string | null;
|
||||||
|
night_bookmark_id: string | null;
|
||||||
|
dawn_bookmark_id: string | null;
|
||||||
|
dusk_bookmark_id: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScheduleEntry {
|
||||||
|
id?: string;
|
||||||
|
start_min: number;
|
||||||
|
end_min: number;
|
||||||
|
bookmark_id: string;
|
||||||
|
label: string | null;
|
||||||
|
interleave_min: number | null;
|
||||||
|
center_hz: number | null;
|
||||||
|
bookmark_ids: string[];
|
||||||
|
record: boolean;
|
||||||
|
exclusive?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SchedulerConfig {
|
||||||
|
remote: string | null;
|
||||||
|
mode: SchedulerMode;
|
||||||
|
grayline: GraylineConfig | null;
|
||||||
|
entries: ScheduleEntry[];
|
||||||
|
interleave_min?: number | null;
|
||||||
|
satellites?: SatelliteScheduleConfig | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SchedulerStatus {
|
||||||
|
active: boolean;
|
||||||
|
last_entry_id?: string | null;
|
||||||
|
last_bookmark_id?: string | null;
|
||||||
|
last_bookmark_name?: string | null;
|
||||||
|
last_applied_utc?: number | null;
|
||||||
|
last_center_hz?: number | null;
|
||||||
|
last_bookmark_ids?: string[];
|
||||||
|
active_satellite?: string | null;
|
||||||
|
freq_hz?: number | null;
|
||||||
|
mode?: string | null;
|
||||||
|
active_decoders?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SchedulerService {
|
||||||
|
initialize(rigId: string | null, role: string | null): void;
|
||||||
|
destroy(): void;
|
||||||
|
setRig(rigId: string | null): void;
|
||||||
|
wireEvents(): void;
|
||||||
|
getConfig(): SchedulerConfig | null;
|
||||||
|
getStatus(): SchedulerStatus | null;
|
||||||
|
getBookmarks(): SchedulerBookmark[];
|
||||||
|
markDirty(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SchedulerWindow extends Window {
|
||||||
|
trx: { modules: { scheduler?: SchedulerService } };
|
||||||
|
trxUi: {
|
||||||
|
confirm(options: { title: string; message: string; confirmLabel: string; danger?: boolean }): Promise<boolean>;
|
||||||
|
notify?(message: string, options: { kind: "error" }): void;
|
||||||
|
};
|
||||||
|
authRole?: string | null;
|
||||||
|
lastActiveRigId?: string | null;
|
||||||
|
serverLat?: number | null;
|
||||||
|
serverLon?: number | null;
|
||||||
|
vchanTakeSchedulerControl?(): Promise<void>;
|
||||||
|
vchanToggleSchedulerRelease?(): Promise<unknown>;
|
||||||
|
satScheduler?: SatelliteSchedulerApi;
|
||||||
|
}
|
||||||
+398
-349
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2026 Stan Grams <sjg@haxx.space>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { readFile } from "node:fs/promises";
|
||||||
|
import test from "node:test";
|
||||||
|
import vm from "node:vm";
|
||||||
|
|
||||||
|
test("scheduler registers a typed module service without lifecycle globals", async () => {
|
||||||
|
const window = { trx: { modules: {} }, trxUi: { confirm: async () => true } };
|
||||||
|
const context = vm.createContext({
|
||||||
|
window,
|
||||||
|
document: {
|
||||||
|
activeElement: null,
|
||||||
|
addEventListener() {},
|
||||||
|
getElementById: () => null,
|
||||||
|
querySelector: () => null,
|
||||||
|
},
|
||||||
|
localStorage: { getItem: () => null, setItem() {} },
|
||||||
|
setInterval: () => 1,
|
||||||
|
clearInterval() {},
|
||||||
|
setTimeout: () => 1,
|
||||||
|
HTMLElement: class HTMLElement {},
|
||||||
|
Element: class Element {},
|
||||||
|
Date,
|
||||||
|
Number,
|
||||||
|
String,
|
||||||
|
Array,
|
||||||
|
Promise,
|
||||||
|
WeakSet,
|
||||||
|
console,
|
||||||
|
});
|
||||||
|
const source = await readFile(new URL("../../assets/web/generated/scheduler.js", import.meta.url), "utf8");
|
||||||
|
new vm.Script(source).runInContext(context);
|
||||||
|
|
||||||
|
const service = window.trx.modules.scheduler;
|
||||||
|
assert.equal(typeof service.initialize, "function");
|
||||||
|
assert.equal(typeof service.setRig, "function");
|
||||||
|
assert.equal(typeof service.getConfig, "function");
|
||||||
|
assert.equal(window.initScheduler, undefined);
|
||||||
|
assert.equal(window.schedulerBridge, undefined);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user