Files
OpsLog/frontend/wailsjs/go/models.ts
T
rouggy 7ace2cc602 Initial codebase: Go + Wails amateur radio logbook
Backend (Go 1.25 / Wails v2):
- QSO storage on SQLite (modernc) with embedded migrations (0001..0005)
- Streaming ADIF import (batch insert) + WorkedBefore per callsign and DXCC
- Callsign lookup with QRZ.com + HamQTH providers (primary/failsafe routing)
  and SQLite-backed TTL cache
- DXCC resolver from cty.dat (auto-download, longest-prefix-match)
- Multi-profile operator identities (home/portable/SOTA/contest) — every
  QSO stamps MY_* from the active profile
- CAT control via OmniRig COM on a single OS-locked goroutine, with
  bidirectional sync (freq/mode/band/split/VFOs) and Rig1/Rig2 hot-swap
- Settings store (key/value), CAT debug log at %APPDATA%/HamLog/cat.log

Frontend (React 18 + TypeScript + Tailwind v4 + shadcn-style):
- Single-row entry strip with CAT-aware band/mode/freq, RST, Start/End
  UTC, per-field locks (band/mode/freq/start/end) for backdated QSOs
- Topbar: live freq (MHz.kHz.Hz dotted), live UTC, band/mode/SPLIT badges,
  CAT pill with rig selector and clickable Azimuth pill (rotor TODO)
- Settings tree: Profiles (Log4OM-style manager), Station Information
  (edits the active profile), unified Callsign Lookup with Test buttons,
  Bands/Modes lists, CAT
- Worked-before matrix (band × mode × class) with new-DXCC highlighting
- ADIF import from menu + Maintenance > Refresh cty.dat

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 00:16:45 +02:00

777 lines
23 KiB
TypeScript

export namespace adif {
export class ImportResult {
total: number;
imported: number;
skipped: number;
errors: string[];
static createFrom(source: any = {}) {
return new ImportResult(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.total = source["total"];
this.imported = source["imported"];
this.skipped = source["skipped"];
this.errors = source["errors"];
}
}
}
export namespace cat {
export class RigState {
enabled: boolean;
connected: boolean;
backend?: string;
rig_num?: number;
rig?: string;
freq_hz?: number;
freq_rx_hz?: number;
split?: boolean;
mode?: string;
band?: string;
vfo?: string;
error?: string;
// Go type: time
updated_at?: any;
static createFrom(source: any = {}) {
return new RigState(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.enabled = source["enabled"];
this.connected = source["connected"];
this.backend = source["backend"];
this.rig_num = source["rig_num"];
this.rig = source["rig"];
this.freq_hz = source["freq_hz"];
this.freq_rx_hz = source["freq_rx_hz"];
this.split = source["split"];
this.mode = source["mode"];
this.band = source["band"];
this.vfo = source["vfo"];
this.error = source["error"];
this.updated_at = this.convertValues(source["updated_at"], null);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}
export namespace lookup {
export class Result {
callsign: string;
name?: string;
qth?: string;
address?: string;
state?: string;
cnty?: string;
country?: string;
grid?: string;
lat?: number;
lon?: number;
dxcc?: number;
cqz?: number;
ituz?: number;
cont?: string;
email?: string;
qsl_via?: string;
source: string;
// Go type: time
fetched_at: any;
static createFrom(source: any = {}) {
return new Result(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.callsign = source["callsign"];
this.name = source["name"];
this.qth = source["qth"];
this.address = source["address"];
this.state = source["state"];
this.cnty = source["cnty"];
this.country = source["country"];
this.grid = source["grid"];
this.lat = source["lat"];
this.lon = source["lon"];
this.dxcc = source["dxcc"];
this.cqz = source["cqz"];
this.ituz = source["ituz"];
this.cont = source["cont"];
this.email = source["email"];
this.qsl_via = source["qsl_via"];
this.source = source["source"];
this.fetched_at = this.convertValues(source["fetched_at"], null);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}
export namespace main {
export class CATSettings {
enabled: boolean;
backend: string;
omnirig_rig: number;
poll_ms: number;
delay_ms: number;
digital_default: string;
static createFrom(source: any = {}) {
return new CATSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.enabled = source["enabled"];
this.backend = source["backend"];
this.omnirig_rig = source["omnirig_rig"];
this.poll_ms = source["poll_ms"];
this.delay_ms = source["delay_ms"];
this.digital_default = source["digital_default"];
}
}
export class CtyDatInfo {
path: string;
entities: number;
loaded_at?: string;
file_mod_time?: string;
static createFrom(source: any = {}) {
return new CtyDatInfo(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.path = source["path"];
this.entities = source["entities"];
this.loaded_at = source["loaded_at"];
this.file_mod_time = source["file_mod_time"];
}
}
export class ModePreset {
name: string;
default_rst_sent?: string;
default_rst_rcvd?: string;
static createFrom(source: any = {}) {
return new ModePreset(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.name = source["name"];
this.default_rst_sent = source["default_rst_sent"];
this.default_rst_rcvd = source["default_rst_rcvd"];
}
}
export class ListsSettings {
bands: string[];
modes: ModePreset[];
static createFrom(source: any = {}) {
return new ListsSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.bands = source["bands"];
this.modes = this.convertValues(source["modes"], ModePreset);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class LookupSettings {
qrz_user: string;
qrz_password: string;
hamqth_user: string;
hamqth_password: string;
primary: string;
failsafe: string;
cache_ttl_days: number;
static createFrom(source: any = {}) {
return new LookupSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.qrz_user = source["qrz_user"];
this.qrz_password = source["qrz_password"];
this.hamqth_user = source["hamqth_user"];
this.hamqth_password = source["hamqth_password"];
this.primary = source["primary"];
this.failsafe = source["failsafe"];
this.cache_ttl_days = source["cache_ttl_days"];
}
}
export class StartupStatus {
ok: boolean;
err: string;
db_path: string;
static createFrom(source: any = {}) {
return new StartupStatus(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.ok = source["ok"];
this.err = source["err"];
this.db_path = source["db_path"];
}
}
export class StationSettings {
callsign: string;
operator: string;
my_grid: string;
my_country: string;
my_sota_ref: string;
my_pota_ref: string;
static createFrom(source: any = {}) {
return new StationSettings(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.callsign = source["callsign"];
this.operator = source["operator"];
this.my_grid = source["my_grid"];
this.my_country = source["my_country"];
this.my_sota_ref = source["my_sota_ref"];
this.my_pota_ref = source["my_pota_ref"];
}
}
}
export namespace profile {
export class Profile {
id: number;
name: string;
callsign: string;
operator: string;
my_grid: string;
my_country: string;
my_state: string;
my_cnty: string;
my_street: string;
my_city: string;
my_postal_code: string;
my_sota_ref: string;
my_pota_ref: string;
my_rig: string;
my_antenna: string;
tx_pwr?: number;
is_active: boolean;
sort_order: number;
// Go type: time
created_at: any;
// Go type: time
updated_at: any;
static createFrom(source: any = {}) {
return new Profile(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
this.callsign = source["callsign"];
this.operator = source["operator"];
this.my_grid = source["my_grid"];
this.my_country = source["my_country"];
this.my_state = source["my_state"];
this.my_cnty = source["my_cnty"];
this.my_street = source["my_street"];
this.my_city = source["my_city"];
this.my_postal_code = source["my_postal_code"];
this.my_sota_ref = source["my_sota_ref"];
this.my_pota_ref = source["my_pota_ref"];
this.my_rig = source["my_rig"];
this.my_antenna = source["my_antenna"];
this.tx_pwr = source["tx_pwr"];
this.is_active = source["is_active"];
this.sort_order = source["sort_order"];
this.created_at = this.convertValues(source["created_at"], null);
this.updated_at = this.convertValues(source["updated_at"], null);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}
export namespace qso {
export class BandMode {
band: string;
mode: string;
static createFrom(source: any = {}) {
return new BandMode(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.band = source["band"];
this.mode = source["mode"];
}
}
export class BandStatus {
band: string;
class: string;
status: string;
static createFrom(source: any = {}) {
return new BandStatus(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.band = source["band"];
this.class = source["class"];
this.status = source["status"];
}
}
export class ListFilter {
callsign?: string;
band?: string;
mode?: string;
station_callsign?: string;
limit?: number;
offset?: number;
static createFrom(source: any = {}) {
return new ListFilter(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.callsign = source["callsign"];
this.band = source["band"];
this.mode = source["mode"];
this.station_callsign = source["station_callsign"];
this.limit = source["limit"];
this.offset = source["offset"];
}
}
export class QSO {
id: number;
callsign: string;
// Go type: time
qso_date: any;
// Go type: time
qso_date_off?: any;
band: string;
band_rx?: string;
mode: string;
submode?: string;
freq_hz?: number;
freq_rx_hz?: number;
rst_sent?: string;
rst_rcvd?: string;
name?: string;
qth?: string;
address?: string;
email?: string;
web?: string;
grid?: string;
gridsquare_ext?: string;
vucc_grids?: string;
country?: string;
state?: string;
cnty?: string;
dxcc?: number;
cont?: string;
cqz?: number;
ituz?: number;
iota?: string;
sota_ref?: string;
pota_ref?: string;
age?: number;
lat?: number;
lon?: number;
rig?: string;
ant?: string;
qsl_sent?: string;
qsl_rcvd?: string;
qsl_sent_date?: string;
qsl_rcvd_date?: string;
qsl_via?: string;
qsl_msg?: string;
qslmsg_rcvd?: string;
lotw_sent?: string;
lotw_rcvd?: string;
lotw_sent_date?: string;
lotw_rcvd_date?: string;
eqsl_sent?: string;
eqsl_rcvd?: string;
eqsl_sent_date?: string;
eqsl_rcvd_date?: string;
clublog_qso_upload_date?: string;
clublog_qso_upload_status?: string;
hrdlog_qso_upload_date?: string;
hrdlog_qso_upload_status?: string;
contest_id?: string;
srx?: number;
stx?: number;
srx_string?: string;
stx_string?: string;
check?: string;
precedence?: string;
arrl_sect?: string;
prop_mode?: string;
sat_name?: string;
sat_mode?: string;
ant_az?: number;
ant_el?: number;
ant_path?: string;
station_callsign?: string;
operator?: string;
my_grid?: string;
my_gridsquare_ext?: string;
my_country?: string;
my_state?: string;
my_cnty?: string;
my_iota?: string;
my_sota_ref?: string;
my_pota_ref?: string;
my_dxcc?: number;
my_cq_zone?: number;
my_itu_zone?: number;
my_lat?: number;
my_lon?: number;
my_street?: string;
my_city?: string;
my_postal_code?: string;
my_rig?: string;
my_antenna?: string;
tx_pwr?: number;
comment?: string;
notes?: string;
extras?: Record<string, string>;
// Go type: time
created_at: any;
// Go type: time
updated_at: any;
static createFrom(source: any = {}) {
return new QSO(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.callsign = source["callsign"];
this.qso_date = this.convertValues(source["qso_date"], null);
this.qso_date_off = this.convertValues(source["qso_date_off"], null);
this.band = source["band"];
this.band_rx = source["band_rx"];
this.mode = source["mode"];
this.submode = source["submode"];
this.freq_hz = source["freq_hz"];
this.freq_rx_hz = source["freq_rx_hz"];
this.rst_sent = source["rst_sent"];
this.rst_rcvd = source["rst_rcvd"];
this.name = source["name"];
this.qth = source["qth"];
this.address = source["address"];
this.email = source["email"];
this.web = source["web"];
this.grid = source["grid"];
this.gridsquare_ext = source["gridsquare_ext"];
this.vucc_grids = source["vucc_grids"];
this.country = source["country"];
this.state = source["state"];
this.cnty = source["cnty"];
this.dxcc = source["dxcc"];
this.cont = source["cont"];
this.cqz = source["cqz"];
this.ituz = source["ituz"];
this.iota = source["iota"];
this.sota_ref = source["sota_ref"];
this.pota_ref = source["pota_ref"];
this.age = source["age"];
this.lat = source["lat"];
this.lon = source["lon"];
this.rig = source["rig"];
this.ant = source["ant"];
this.qsl_sent = source["qsl_sent"];
this.qsl_rcvd = source["qsl_rcvd"];
this.qsl_sent_date = source["qsl_sent_date"];
this.qsl_rcvd_date = source["qsl_rcvd_date"];
this.qsl_via = source["qsl_via"];
this.qsl_msg = source["qsl_msg"];
this.qslmsg_rcvd = source["qslmsg_rcvd"];
this.lotw_sent = source["lotw_sent"];
this.lotw_rcvd = source["lotw_rcvd"];
this.lotw_sent_date = source["lotw_sent_date"];
this.lotw_rcvd_date = source["lotw_rcvd_date"];
this.eqsl_sent = source["eqsl_sent"];
this.eqsl_rcvd = source["eqsl_rcvd"];
this.eqsl_sent_date = source["eqsl_sent_date"];
this.eqsl_rcvd_date = source["eqsl_rcvd_date"];
this.clublog_qso_upload_date = source["clublog_qso_upload_date"];
this.clublog_qso_upload_status = source["clublog_qso_upload_status"];
this.hrdlog_qso_upload_date = source["hrdlog_qso_upload_date"];
this.hrdlog_qso_upload_status = source["hrdlog_qso_upload_status"];
this.contest_id = source["contest_id"];
this.srx = source["srx"];
this.stx = source["stx"];
this.srx_string = source["srx_string"];
this.stx_string = source["stx_string"];
this.check = source["check"];
this.precedence = source["precedence"];
this.arrl_sect = source["arrl_sect"];
this.prop_mode = source["prop_mode"];
this.sat_name = source["sat_name"];
this.sat_mode = source["sat_mode"];
this.ant_az = source["ant_az"];
this.ant_el = source["ant_el"];
this.ant_path = source["ant_path"];
this.station_callsign = source["station_callsign"];
this.operator = source["operator"];
this.my_grid = source["my_grid"];
this.my_gridsquare_ext = source["my_gridsquare_ext"];
this.my_country = source["my_country"];
this.my_state = source["my_state"];
this.my_cnty = source["my_cnty"];
this.my_iota = source["my_iota"];
this.my_sota_ref = source["my_sota_ref"];
this.my_pota_ref = source["my_pota_ref"];
this.my_dxcc = source["my_dxcc"];
this.my_cq_zone = source["my_cq_zone"];
this.my_itu_zone = source["my_itu_zone"];
this.my_lat = source["my_lat"];
this.my_lon = source["my_lon"];
this.my_street = source["my_street"];
this.my_city = source["my_city"];
this.my_postal_code = source["my_postal_code"];
this.my_rig = source["my_rig"];
this.my_antenna = source["my_antenna"];
this.tx_pwr = source["tx_pwr"];
this.comment = source["comment"];
this.notes = source["notes"];
this.extras = source["extras"];
this.created_at = this.convertValues(source["created_at"], null);
this.updated_at = this.convertValues(source["updated_at"], null);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class WorkedEntry {
id: number;
// Go type: time
qso_date: any;
band: string;
mode: string;
rst_sent?: string;
rst_rcvd?: string;
qsl_sent?: string;
qsl_rcvd?: string;
lotw_sent?: string;
lotw_rcvd?: string;
static createFrom(source: any = {}) {
return new WorkedEntry(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.qso_date = this.convertValues(source["qso_date"], null);
this.band = source["band"];
this.mode = source["mode"];
this.rst_sent = source["rst_sent"];
this.rst_rcvd = source["rst_rcvd"];
this.qsl_sent = source["qsl_sent"];
this.qsl_rcvd = source["qsl_rcvd"];
this.lotw_sent = source["lotw_sent"];
this.lotw_rcvd = source["lotw_rcvd"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class WorkedBefore {
callsign: string;
count: number;
// Go type: time
first?: any;
// Go type: time
last?: any;
bands: string[];
modes: string[];
band_modes: BandMode[];
entries: WorkedEntry[];
dxcc?: number;
dxcc_name?: string;
dxcc_count: number;
// Go type: time
dxcc_first?: any;
// Go type: time
dxcc_last?: any;
dxcc_bands: string[];
dxcc_modes: string[];
dxcc_band_modes: BandMode[];
band_status: BandStatus[];
static createFrom(source: any = {}) {
return new WorkedBefore(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.callsign = source["callsign"];
this.count = source["count"];
this.first = this.convertValues(source["first"], null);
this.last = this.convertValues(source["last"], null);
this.bands = source["bands"];
this.modes = source["modes"];
this.band_modes = this.convertValues(source["band_modes"], BandMode);
this.entries = this.convertValues(source["entries"], WorkedEntry);
this.dxcc = source["dxcc"];
this.dxcc_name = source["dxcc_name"];
this.dxcc_count = source["dxcc_count"];
this.dxcc_first = this.convertValues(source["dxcc_first"], null);
this.dxcc_last = this.convertValues(source["dxcc_last"], null);
this.dxcc_bands = source["dxcc_bands"];
this.dxcc_modes = source["dxcc_modes"];
this.dxcc_band_modes = this.convertValues(source["dxcc_band_modes"], BandMode);
this.band_status = this.convertValues(source["band_status"], BandStatus);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice && a.map) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
}