update
This commit is contained in:
+124
-34
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
AlertCircle, Antenna, CheckCircle2, Clock, Compass, Hash, Loader2, Lock,
|
||||
Maximize2, Minimize2, Pencil, RadioTower, RefreshCw, Send, Settings, Trash2, Unlock, X,
|
||||
Maximize2, Minimize2, Pencil, RadioTower, RefreshCw, Send, Settings, Square, Trash2, Unlock, X,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
SetCompactMode,
|
||||
GetCATState, SetCATFrequency, SetCATMode, SwitchCATRig,
|
||||
RefreshCtyDat,
|
||||
RotatorGoTo, RotatorStop,
|
||||
GetCATSettings,
|
||||
} from '../wailsjs/go/main/App';
|
||||
import { EventsOn, EventsOff } from '../wailsjs/runtime/runtime';
|
||||
@@ -32,6 +33,10 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import {
|
||||
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
|
||||
import {
|
||||
Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
|
||||
@@ -344,6 +349,11 @@ export default function App() {
|
||||
const [importing, setImporting] = useState(false);
|
||||
const [importResult, setImportResult] = useState<ImportResult | null>(null);
|
||||
const [importErrorsOpen, setImportErrorsOpen] = useState(false);
|
||||
const [importDupsOpen, setImportDupsOpen] = useState(false);
|
||||
// ADIF import confirmation: after the user picks a file, hold the path
|
||||
// until they confirm the options (skip duplicates etc.).
|
||||
const [pendingImportPath, setPendingImportPath] = useState<string | null>(null);
|
||||
const [importSkipDups, setImportSkipDups] = useState(true);
|
||||
|
||||
// === Lookup + WB ===
|
||||
const [lookupResult, setLookupResult] = useState<LookupResult | null>(null);
|
||||
@@ -673,14 +683,29 @@ export default function App() {
|
||||
try {
|
||||
const path = await OpenADIFFile();
|
||||
if (!path) return;
|
||||
setImporting(true);
|
||||
setImportResult(null);
|
||||
setImportErrorsOpen(false);
|
||||
const res = await ImportADIF(path);
|
||||
// Stash the path and open the options dialog. The actual import
|
||||
// is fired from runImport() when the user clicks "Import".
|
||||
setPendingImportPath(path);
|
||||
} catch (e: any) { setError(String(e?.message ?? e)); }
|
||||
}
|
||||
|
||||
async function runImport() {
|
||||
const path = pendingImportPath;
|
||||
if (!path || importing) return;
|
||||
setPendingImportPath(null);
|
||||
setImporting(true);
|
||||
setImportResult(null);
|
||||
setImportErrorsOpen(false);
|
||||
setImportDupsOpen(false);
|
||||
try {
|
||||
const res = await ImportADIF(path, importSkipDups);
|
||||
setImportResult(res);
|
||||
await refresh();
|
||||
} catch (e: any) { setError(String(e?.message ?? e)); }
|
||||
finally { setImporting(false); }
|
||||
} catch (e: any) {
|
||||
setError(String(e?.message ?? e));
|
||||
} finally {
|
||||
setImporting(false);
|
||||
}
|
||||
}
|
||||
|
||||
const menus: Menu[] = useMemo(() => [
|
||||
@@ -820,37 +845,55 @@ export default function App() {
|
||||
<div className="w-px h-4 bg-border mx-2" />
|
||||
<Badge variant="accent" className="font-mono">{band}</Badge>
|
||||
<Badge className="bg-emerald-100 text-emerald-700 hover:bg-emerald-100 font-mono" variant="outline">{mode}</Badge>
|
||||
{/* Bearing pill — clickable hook for the future rotator action.
|
||||
Today it's a passive display; once the rotor backend lands we
|
||||
wire onClick → rotate to short-path azimuth. */}
|
||||
{/* Bearing controls — three separate buttons so SP and LP are
|
||||
both directly clickable, plus an always-visible Stop. The
|
||||
old Shift/Ctrl shortcuts were not discoverable enough. */}
|
||||
{(() => {
|
||||
const p = pathBetween(station.my_grid, grid);
|
||||
const disabled = !p;
|
||||
const goto = (az: number) => RotatorGoTo(Math.round(az), -1).catch((err) => setError(String(err?.message ?? err)));
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => { /* TODO: rotor.gotoAzimuth(p.bearingShort) */ }}
|
||||
title={
|
||||
p
|
||||
? `Short-path ${Math.round(p.bearingShort)}° · ${Math.round(p.distanceShort).toLocaleString()} km\nLong-path ${Math.round(p.bearingLong)}° · ${Math.round(p.distanceLong).toLocaleString()} km\n(rotor control coming soon)`
|
||||
: (station.my_grid ? 'No remote grid' : 'Set your station grid in Preferences')
|
||||
}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-[11px] font-mono font-semibold border transition-colors',
|
||||
disabled
|
||||
? 'bg-muted/40 text-muted-foreground/60 border-border/40 cursor-not-allowed'
|
||||
: 'bg-sky-100 text-sky-800 border-sky-300 hover:bg-sky-200 cursor-pointer',
|
||||
)}
|
||||
>
|
||||
<Compass className="size-3" />
|
||||
{p ? `${Math.round(p.bearingShort)}°` : '—°'}
|
||||
{p && (
|
||||
<span className="text-[9px] text-sky-700/80 font-medium ml-0.5">
|
||||
LP {Math.round(p.bearingLong)}°
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
<div className="inline-flex items-center rounded-full border border-sky-300 bg-sky-100 overflow-hidden text-[11px] font-mono font-semibold">
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => p && goto(p.bearingShort)}
|
||||
title={p
|
||||
? `Rotate short-path · ${Math.round(p.distanceShort).toLocaleString()} km`
|
||||
: (station.my_grid ? 'No remote grid' : 'Set your station grid in Preferences')}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1 px-2 py-0.5 transition-colors',
|
||||
disabled
|
||||
? 'text-muted-foreground/60 cursor-not-allowed'
|
||||
: 'text-sky-800 hover:bg-sky-200 cursor-pointer',
|
||||
)}
|
||||
>
|
||||
<Compass className="size-3" />
|
||||
{p ? `${Math.round(p.bearingShort)}°` : '—°'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={() => p && goto(p.bearingLong)}
|
||||
title={p ? `Rotate long-path · ${Math.round(p.distanceLong).toLocaleString()} km` : ''}
|
||||
className={cn(
|
||||
'px-1.5 py-0.5 border-l border-sky-300 text-[10px] transition-colors',
|
||||
disabled
|
||||
? 'text-muted-foreground/60 cursor-not-allowed'
|
||||
: 'text-sky-800 hover:bg-sky-200 cursor-pointer',
|
||||
)}
|
||||
>
|
||||
LP {p ? `${Math.round(p.bearingLong)}°` : '—'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => RotatorStop().catch((err) => setError(String(err?.message ?? err)))}
|
||||
title="Stop rotation"
|
||||
className="px-1.5 py-0.5 border-l border-sky-300 text-rose-700 hover:bg-rose-100 hover:text-rose-800 cursor-pointer transition-colors"
|
||||
>
|
||||
<Square className="size-2.5 fill-current" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
{catState.enabled && (
|
||||
@@ -1226,8 +1269,19 @@ export default function App() {
|
||||
<div className="flex items-center gap-3 flex-wrap">
|
||||
<strong>Import complete.</strong>
|
||||
<Badge variant="outline" className="bg-white/60 font-mono text-emerald-700 border-emerald-300">{importResult.imported} imported</Badge>
|
||||
{importResult.duplicates > 0 && (
|
||||
<Badge variant="outline" className="bg-white/60 font-mono text-sky-700 border-sky-300">{importResult.duplicates} duplicates</Badge>
|
||||
)}
|
||||
<Badge variant="outline" className="bg-white/60 font-mono text-amber-700 border-amber-300">{importResult.skipped} skipped</Badge>
|
||||
<Badge variant="outline" className="bg-white/60 font-mono">{importResult.total} total</Badge>
|
||||
{importResult.duplicates > 0 && importResult.duplicate_samples && importResult.duplicate_samples.length > 0 && (
|
||||
<button className="underline text-xs" onClick={() => setImportDupsOpen((v) => !v)}>
|
||||
{importDupsOpen ? 'Hide' : 'Show'} duplicates
|
||||
{importResult.duplicates > importResult.duplicate_samples.length
|
||||
? ` (first ${importResult.duplicate_samples.length} of ${importResult.duplicates})`
|
||||
: ''}
|
||||
</button>
|
||||
)}
|
||||
{importResult.errors && importResult.errors.length > 0 && (
|
||||
<button className="underline text-xs" onClick={() => setImportErrorsOpen((v) => !v)}>
|
||||
{importErrorsOpen ? 'Hide' : 'Show'} {importResult.errors.length} error{importResult.errors.length > 1 ? 's' : ''}
|
||||
@@ -1235,6 +1289,11 @@ export default function App() {
|
||||
)}
|
||||
<button className="ml-auto" onClick={() => setImportResult(null)}><X className="size-4" /></button>
|
||||
</div>
|
||||
{importDupsOpen && importResult.duplicate_samples && (
|
||||
<ul className="font-mono text-[11px] pl-6 max-h-32 overflow-y-auto list-disc border-t border-current/20 pt-2 mt-1">
|
||||
{importResult.duplicate_samples.map((d, i) => <li key={i}>{d}</li>)}
|
||||
</ul>
|
||||
)}
|
||||
{importErrorsOpen && importResult.errors && (
|
||||
<ul className="font-mono text-[11px] pl-6 max-h-32 overflow-y-auto list-disc border-t border-current/20 pt-2 mt-1">
|
||||
{importResult.errors.map((e, i) => <li key={i}>{e}</li>)}
|
||||
@@ -1352,6 +1411,37 @@ export default function App() {
|
||||
onCancel={() => { if (!deletingAll) setShowDeleteAll(false); }}
|
||||
/>
|
||||
)}
|
||||
{pendingImportPath && (
|
||||
<Dialog open onOpenChange={(o) => { if (!o) setPendingImportPath(null); }}>
|
||||
<DialogContent className="max-w-lg px-6">
|
||||
<DialogHeader className="px-2">
|
||||
<DialogTitle>Import ADIF</DialogTitle>
|
||||
<DialogDescription className="text-xs break-all">
|
||||
{pendingImportPath}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="py-2 px-2 space-y-3">
|
||||
<label className="flex items-start gap-2 text-sm cursor-pointer">
|
||||
<Checkbox
|
||||
checked={importSkipDups}
|
||||
onCheckedChange={(c) => setImportSkipDups(!!c)}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<span>
|
||||
Skip duplicates
|
||||
<span className="block text-xs text-muted-foreground mt-0.5">
|
||||
Records that match an existing QSO on (callsign + UTC minute + band + mode) are not re-inserted. Uncheck to import everything as-is — useful for merging two logs that overlap intentionally.
|
||||
</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<DialogFooter className="px-2">
|
||||
<Button variant="outline" onClick={() => setPendingImportPath(null)}>Cancel</Button>
|
||||
<Button onClick={runImport}>Import</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
GetListsSettings, SaveListsSettings,
|
||||
GetCATSettings, SaveCATSettings,
|
||||
ListProfiles, GetActiveProfile, SaveProfile, DeleteProfile, ActivateProfile, DuplicateProfile,
|
||||
GetRotatorSettings, SaveRotatorSettings, TestRotator, RotatorPark, RotatorStop,
|
||||
} from '../../wailsjs/go/main/App';
|
||||
import type { profile as profileModels } from '../../wailsjs/go/models';
|
||||
import type { LookupSettingsForm, StationSettingsForm, ListsSettingsForm, ModePresetForm } from '@/types';
|
||||
@@ -33,6 +34,7 @@ type StationSettings = StationSettingsForm;
|
||||
type ListsSettings = ListsSettingsForm;
|
||||
type ModePreset = ModePresetForm;
|
||||
type CATSettings = Omit<mainModels.CATSettings, 'convertValues'>;
|
||||
type RotatorSettings = Omit<mainModels.RotatorSettings, 'convertValues'>;
|
||||
type Profile = Omit<profileModels.Profile, 'convertValues'>;
|
||||
|
||||
const emptyProfile = (): Profile => ({
|
||||
@@ -100,7 +102,7 @@ const TREE: TreeNode[] = [
|
||||
{
|
||||
kind: 'group', label: 'Hardware Configuration', icon: Server, children: [
|
||||
{ kind: 'item', label: 'CAT interface (OmniRig)', id: 'cat' },
|
||||
{ kind: 'item', label: 'Rotator (PstRotator)', id: 'rotator', disabled: true },
|
||||
{ kind: 'item', label: 'Rotator (PstRotator)', id: 'rotator' },
|
||||
{ kind: 'item', label: 'Antenna (Ultrabeam)', id: 'antenna', disabled: true },
|
||||
{ kind: 'item', label: 'Audio devices', id: 'audio', disabled: true },
|
||||
],
|
||||
@@ -243,6 +245,11 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
enabled: false, backend: 'omnirig', omnirig_rig: 1, poll_ms: 250, delay_ms: 0,
|
||||
digital_default: 'FT8',
|
||||
});
|
||||
const [rotator, setRotator] = useState<RotatorSettings>({
|
||||
enabled: false, host: '127.0.0.1', port: 12000, has_elevation: false,
|
||||
});
|
||||
const [rotatorTesting, setRotatorTesting] = useState(false);
|
||||
const [rotatorTest, setRotatorTest] = useState<{ ok: boolean; msg: string } | null>(null);
|
||||
const [profiles, setProfiles] = useState<Profile[]>([]);
|
||||
// State for ProfilesPanel — lifted here because PANELS[selected]() calls
|
||||
// the panel as a plain function, not as a JSX element, so any useState
|
||||
@@ -278,8 +285,9 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const [l, ls, c, ap] = await Promise.all([
|
||||
const [l, ls, c, ap, r] = await Promise.all([
|
||||
GetLookupSettings(), GetListsSettings(), GetCATSettings(), GetActiveProfile(),
|
||||
GetRotatorSettings(),
|
||||
]);
|
||||
setLookup(l);
|
||||
setActiveProfile(ap as Profile);
|
||||
@@ -287,6 +295,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
await reloadProfiles();
|
||||
setBandsText((ls.bands ?? []).join('\n'));
|
||||
setCatCfg(c);
|
||||
setRotator(r);
|
||||
} catch (e: any) {
|
||||
setErr(String(e?.message ?? e));
|
||||
} finally {
|
||||
@@ -356,6 +365,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
}
|
||||
await SaveLookupSettings(lookup as any);
|
||||
await SaveCATSettings(catCfg as any);
|
||||
await SaveRotatorSettings(rotator as any);
|
||||
|
||||
setMsg('Settings saved.');
|
||||
onSaved();
|
||||
@@ -858,6 +868,85 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
async function testRotator() {
|
||||
setRotatorTesting(true);
|
||||
setRotatorTest(null);
|
||||
try {
|
||||
await TestRotator(rotator as any);
|
||||
setRotatorTest({ ok: true, msg: 'Packet sent — antenna should swing to 0° (north). If it didn\'t, check PstRotator host/port and that PstRotator\'s UDP listener is enabled.' });
|
||||
} catch (e: any) {
|
||||
setRotatorTest({ ok: false, msg: String(e?.message ?? e) });
|
||||
} finally {
|
||||
setRotatorTesting(false);
|
||||
}
|
||||
}
|
||||
|
||||
function RotatorPanel() {
|
||||
return (
|
||||
<>
|
||||
<SectionHeader
|
||||
title="Rotator (PstRotator)"
|
||||
hint="HamLog sends UDP commands to PstRotator. Enable PstRotator's UDP listener (Setup → Communication → UDP) before testing."
|
||||
/>
|
||||
<div className="space-y-4 max-w-xl">
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<Checkbox checked={rotator.enabled} onCheckedChange={(c) => setRotator((s) => ({ ...s, enabled: !!c }))} />
|
||||
Enable PstRotator control
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<div className="space-y-1 col-span-2">
|
||||
<Label>Host</Label>
|
||||
<Input
|
||||
value={rotator.host ?? ''}
|
||||
onChange={(e) => setRotator((s) => ({ ...s, host: e.target.value }))}
|
||||
placeholder="127.0.0.1"
|
||||
className="font-mono"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<Label>UDP port</Label>
|
||||
<Input
|
||||
type="number" min={1} max={65535}
|
||||
value={rotator.port}
|
||||
onChange={(e) => setRotator((s) => ({ ...s, port: parseInt(e.target.value) || 12000 }))}
|
||||
className="font-mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
||||
<Checkbox checked={rotator.has_elevation} onCheckedChange={(c) => setRotator((s) => ({ ...s, has_elevation: !!c }))} />
|
||||
This rotator supports elevation (VHF / satellite)
|
||||
</label>
|
||||
<div className="flex items-center gap-2 pt-2">
|
||||
<Button variant="outline" size="sm" onClick={testRotator} disabled={rotatorTesting}>
|
||||
{rotatorTesting ? 'Sending…' : 'Test (point to 0°)'}
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => RotatorStop().catch((e) => setErr(String(e?.message ?? e)))} disabled={!rotator.enabled}>
|
||||
Stop
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => RotatorPark().catch((e) => setErr(String(e?.message ?? e)))} disabled={!rotator.enabled}>
|
||||
Park
|
||||
</Button>
|
||||
</div>
|
||||
{rotatorTest && (
|
||||
<div className={cn(
|
||||
'text-xs rounded-md p-2.5 border',
|
||||
rotatorTest.ok
|
||||
? 'bg-emerald-50 text-emerald-800 border-emerald-200'
|
||||
: 'bg-destructive/10 text-destructive border-destructive/30',
|
||||
)}>
|
||||
{rotatorTest.msg}
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-muted-foreground">
|
||||
From the main entry strip, click the bearing pill to rotate to the short-path azimuth.
|
||||
Shift+click for long-path, Ctrl+click to stop.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Map sections to their content + icon (for placeholder).
|
||||
const PANELS: Record<SectionId, () => JSX.Element> = {
|
||||
station: StationPanel,
|
||||
@@ -869,7 +958,7 @@ export function SettingsModal({ onClose, onSaved, initialSection }: Props) {
|
||||
backup: () => <ComingSoon id="backup" icon={Database} />,
|
||||
awards: () => <ComingSoon id="awards" icon={Award} />,
|
||||
cat: CATPanel,
|
||||
rotator: () => <ComingSoon id="rotator" icon={Compass} />,
|
||||
rotator: RotatorPanel,
|
||||
antenna: () => <ComingSoon id="antenna" icon={AntennaIcon} />,
|
||||
audio: () => <ComingSoon id="audio" icon={Server} />,
|
||||
};
|
||||
|
||||
Vendored
+13
-1
@@ -37,11 +37,13 @@ export function GetLookupSettings():Promise<main.LookupSettings>;
|
||||
|
||||
export function GetQSO(arg1:number):Promise<qso.QSO>;
|
||||
|
||||
export function GetRotatorSettings():Promise<main.RotatorSettings>;
|
||||
|
||||
export function GetStartupStatus():Promise<main.StartupStatus>;
|
||||
|
||||
export function GetStationSettings():Promise<main.StationSettings>;
|
||||
|
||||
export function ImportADIF(arg1:string):Promise<adif.ImportResult>;
|
||||
export function ImportADIF(arg1:string,arg2:boolean):Promise<adif.ImportResult>;
|
||||
|
||||
export function ListProfiles():Promise<Array<profile.Profile>>;
|
||||
|
||||
@@ -53,6 +55,12 @@ export function OpenADIFFile():Promise<string>;
|
||||
|
||||
export function RefreshCtyDat():Promise<main.CtyDatInfo>;
|
||||
|
||||
export function RotatorGoTo(arg1:number,arg2:number):Promise<void>;
|
||||
|
||||
export function RotatorPark():Promise<void>;
|
||||
|
||||
export function RotatorStop():Promise<void>;
|
||||
|
||||
export function SaveCATSettings(arg1:main.CATSettings):Promise<void>;
|
||||
|
||||
export function SaveListsSettings(arg1:main.ListsSettings):Promise<void>;
|
||||
@@ -61,6 +69,8 @@ export function SaveLookupSettings(arg1:main.LookupSettings):Promise<void>;
|
||||
|
||||
export function SaveProfile(arg1:profile.Profile):Promise<profile.Profile>;
|
||||
|
||||
export function SaveRotatorSettings(arg1:main.RotatorSettings):Promise<void>;
|
||||
|
||||
export function SaveStationSettings(arg1:main.StationSettings):Promise<void>;
|
||||
|
||||
export function SetCATFrequency(arg1:number):Promise<void>;
|
||||
@@ -73,6 +83,8 @@ export function SwitchCATRig(arg1:number):Promise<void>;
|
||||
|
||||
export function TestLookupProvider(arg1:string,arg2:string,arg3:string,arg4:string):Promise<lookup.Result>;
|
||||
|
||||
export function TestRotator(arg1:main.RotatorSettings):Promise<void>;
|
||||
|
||||
export function UpdateQSO(arg1:qso.QSO):Promise<void>;
|
||||
|
||||
export function WorkedBefore(arg1:string,arg2:number):Promise<qso.WorkedBefore>;
|
||||
|
||||
@@ -62,6 +62,10 @@ export function GetQSO(arg1) {
|
||||
return window['go']['main']['App']['GetQSO'](arg1);
|
||||
}
|
||||
|
||||
export function GetRotatorSettings() {
|
||||
return window['go']['main']['App']['GetRotatorSettings']();
|
||||
}
|
||||
|
||||
export function GetStartupStatus() {
|
||||
return window['go']['main']['App']['GetStartupStatus']();
|
||||
}
|
||||
@@ -70,8 +74,8 @@ export function GetStationSettings() {
|
||||
return window['go']['main']['App']['GetStationSettings']();
|
||||
}
|
||||
|
||||
export function ImportADIF(arg1) {
|
||||
return window['go']['main']['App']['ImportADIF'](arg1);
|
||||
export function ImportADIF(arg1, arg2) {
|
||||
return window['go']['main']['App']['ImportADIF'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function ListProfiles() {
|
||||
@@ -94,6 +98,18 @@ export function RefreshCtyDat() {
|
||||
return window['go']['main']['App']['RefreshCtyDat']();
|
||||
}
|
||||
|
||||
export function RotatorGoTo(arg1, arg2) {
|
||||
return window['go']['main']['App']['RotatorGoTo'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function RotatorPark() {
|
||||
return window['go']['main']['App']['RotatorPark']();
|
||||
}
|
||||
|
||||
export function RotatorStop() {
|
||||
return window['go']['main']['App']['RotatorStop']();
|
||||
}
|
||||
|
||||
export function SaveCATSettings(arg1) {
|
||||
return window['go']['main']['App']['SaveCATSettings'](arg1);
|
||||
}
|
||||
@@ -110,6 +126,10 @@ export function SaveProfile(arg1) {
|
||||
return window['go']['main']['App']['SaveProfile'](arg1);
|
||||
}
|
||||
|
||||
export function SaveRotatorSettings(arg1) {
|
||||
return window['go']['main']['App']['SaveRotatorSettings'](arg1);
|
||||
}
|
||||
|
||||
export function SaveStationSettings(arg1) {
|
||||
return window['go']['main']['App']['SaveStationSettings'](arg1);
|
||||
}
|
||||
@@ -134,6 +154,10 @@ export function TestLookupProvider(arg1, arg2, arg3, arg4) {
|
||||
return window['go']['main']['App']['TestLookupProvider'](arg1, arg2, arg3, arg4);
|
||||
}
|
||||
|
||||
export function TestRotator(arg1) {
|
||||
return window['go']['main']['App']['TestRotator'](arg1);
|
||||
}
|
||||
|
||||
export function UpdateQSO(arg1) {
|
||||
return window['go']['main']['App']['UpdateQSO'](arg1);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ export namespace adif {
|
||||
total: number;
|
||||
imported: number;
|
||||
skipped: number;
|
||||
duplicates: number;
|
||||
duplicate_samples: string[];
|
||||
errors: string[];
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
@@ -15,6 +17,8 @@ export namespace adif {
|
||||
this.total = source["total"];
|
||||
this.imported = source["imported"];
|
||||
this.skipped = source["skipped"];
|
||||
this.duplicates = source["duplicates"];
|
||||
this.duplicate_samples = source["duplicate_samples"];
|
||||
this.errors = source["errors"];
|
||||
}
|
||||
}
|
||||
@@ -266,6 +270,24 @@ export namespace main {
|
||||
}
|
||||
}
|
||||
|
||||
export class RotatorSettings {
|
||||
enabled: boolean;
|
||||
host: string;
|
||||
port: number;
|
||||
has_elevation: boolean;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new RotatorSettings(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.enabled = source["enabled"];
|
||||
this.host = source["host"];
|
||||
this.port = source["port"];
|
||||
this.has_elevation = source["has_elevation"];
|
||||
}
|
||||
}
|
||||
export class StartupStatus {
|
||||
ok: boolean;
|
||||
err: string;
|
||||
|
||||
Reference in New Issue
Block a user