@smartnet360/svelte-components 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apps/antenna-pattern/components/AntennaControls.svelte +424 -0
- package/dist/apps/antenna-pattern/components/AntennaControls.svelte.d.ts +16 -0
- package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte +62 -0
- package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +339 -0
- package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte.d.ts +3 -0
- package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +299 -0
- package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte.d.ts +24 -0
- package/dist/apps/antenna-pattern/components/DbNotification.svelte +67 -0
- package/dist/apps/antenna-pattern/components/DbNotification.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/JsonImporter.svelte +116 -0
- package/dist/apps/antenna-pattern/components/JsonImporter.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/MSIConverter.svelte +209 -0
- package/dist/apps/antenna-pattern/components/MSIConverter.svelte.d.ts +18 -0
- package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +252 -0
- package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte.d.ts +22 -0
- package/dist/apps/antenna-pattern/db.d.ts +24 -0
- package/dist/apps/antenna-pattern/db.js +15 -0
- package/dist/apps/antenna-pattern/helpers/plotly-utils.d.ts +54 -0
- package/dist/apps/antenna-pattern/helpers/plotly-utils.js +324 -0
- package/dist/apps/antenna-pattern/index.d.ts +15 -0
- package/dist/apps/antenna-pattern/index.js +19 -0
- package/dist/apps/antenna-pattern/stores/antennas.d.ts +5 -0
- package/dist/apps/antenna-pattern/stores/antennas.js +14 -0
- package/dist/apps/antenna-pattern/stores/db-status.d.ts +28 -0
- package/dist/apps/antenna-pattern/stores/db-status.js +34 -0
- package/dist/apps/antenna-pattern/utils/db-utils.d.ts +9 -0
- package/dist/apps/antenna-pattern/utils/db-utils.js +180 -0
- package/dist/apps/antenna-pattern/utils/init-db.d.ts +2 -0
- package/dist/apps/antenna-pattern/utils/init-db.js +95 -0
- package/dist/apps/antenna-pattern/utils/msi-parser.d.ts +3 -0
- package/dist/apps/antenna-pattern/utils/msi-parser.js +197 -0
- package/dist/apps/antenna-pattern/utils/plotly-chart-utils.d.ts +101 -0
- package/dist/apps/antenna-pattern/utils/plotly-chart-utils.js +152 -0
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.js +6 -0
- package/dist/{Charts → core/Charts}/ChartComponent.svelte +131 -39
- package/dist/{Charts → core/Charts}/charts.model.d.ts +1 -1
- package/dist/{Charts → core/Charts}/data-utils.js +0 -4
- package/dist/{Desktop → core/Desktop}/GridRenderer.svelte +1 -1
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +6 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -2
- package/package.json +6 -2
- /package/dist/{Charts → core/Charts}/ChartCard.svelte +0 -0
- /package/dist/{Charts → core/Charts}/ChartCard.svelte.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/ChartComponent.svelte.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/adapt.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/adapt.js +0 -0
- /package/dist/{Charts → core/Charts}/charts.model.js +0 -0
- /package/dist/{Charts → core/Charts}/data-utils.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/index.d.ts +0 -0
- /package/dist/{Charts → core/Charts}/index.js +0 -0
- /package/dist/{Charts → core/Charts}/plotly.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Desktop.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Desktop.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.js +0 -0
- /package/dist/{Desktop → core/Desktop}/GridRenderer.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/GridSelector/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/GridViewer.svelte +0 -0
- /package/dist/{Desktop → core/Desktop}/GridViewer.svelte.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/gridLayouts.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/gridLayouts.js +0 -0
- /package/dist/{Desktop → core/Desktop}/index.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/index.js +0 -0
- /package/dist/{Desktop → core/Desktop}/launchHelpers.d.ts +0 -0
- /package/dist/{Desktop → core/Desktop}/launchHelpers.js +0 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
export { default as PlotlyRadarChart } from './components/PlotlyRadarChart.svelte';
|
2
|
+
export { default as AntennaControls } from './components/AntennaControls.svelte';
|
3
|
+
export { default as AntennaDiagrams } from './components/AntennaDiagrams.svelte';
|
4
|
+
export { default as AntennaSettingsModal } from './components/AntennaSettingsModal.svelte';
|
5
|
+
export { default as JsonImporter } from './components/JsonImporter.svelte';
|
6
|
+
export { default as MSIConverter } from './components/MSIConverter.svelte';
|
7
|
+
export { default as DbNotification } from './components/DbNotification.svelte';
|
8
|
+
export { default as AntennaDataDropdown } from './components/AntennaDataDropdown.svelte';
|
9
|
+
export { antennas, selectedAntenna, searchQuery, filteredAntennas } from './stores/antennas';
|
10
|
+
export { dbStatus, dataOperationStatus, updateDbStatus, trackDataOperation } from './stores/db-status';
|
11
|
+
export { db, type Antenna } from './db';
|
12
|
+
export * from './utils/db-utils';
|
13
|
+
export * from './utils/init-db';
|
14
|
+
export * from './utils/msi-parser';
|
15
|
+
export * from './utils/plotly-chart-utils';
|
@@ -0,0 +1,19 @@
|
|
1
|
+
// Individual components
|
2
|
+
export { default as PlotlyRadarChart } from './components/PlotlyRadarChart.svelte';
|
3
|
+
export { default as AntennaControls } from './components/AntennaControls.svelte';
|
4
|
+
export { default as AntennaDiagrams } from './components/AntennaDiagrams.svelte';
|
5
|
+
export { default as AntennaSettingsModal } from './components/AntennaSettingsModal.svelte';
|
6
|
+
export { default as JsonImporter } from './components/JsonImporter.svelte';
|
7
|
+
export { default as MSIConverter } from './components/MSIConverter.svelte';
|
8
|
+
export { default as DbNotification } from './components/DbNotification.svelte';
|
9
|
+
export { default as AntennaDataDropdown } from './components/AntennaDataDropdown.svelte';
|
10
|
+
// Stores
|
11
|
+
export { antennas, selectedAntenna, searchQuery, filteredAntennas } from './stores/antennas';
|
12
|
+
export { dbStatus, dataOperationStatus, updateDbStatus, trackDataOperation } from './stores/db-status';
|
13
|
+
// Database and types
|
14
|
+
export { db } from './db';
|
15
|
+
// Utilities
|
16
|
+
export * from './utils/db-utils';
|
17
|
+
export * from './utils/init-db';
|
18
|
+
export * from './utils/msi-parser';
|
19
|
+
export * from './utils/plotly-chart-utils';
|
@@ -0,0 +1,5 @@
|
|
1
|
+
import type { Antenna } from '../db';
|
2
|
+
export declare const antennas: import("svelte/store").Writable<Antenna[]>;
|
3
|
+
export declare const selectedAntenna: import("svelte/store").Writable<Antenna | null>;
|
4
|
+
export declare const searchQuery: import("svelte/store").Writable<string>;
|
5
|
+
export declare const filteredAntennas: import("svelte/store").Readable<Antenna[]>;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { writable, derived } from 'svelte/store';
|
2
|
+
export const antennas = writable([]);
|
3
|
+
export const selectedAntenna = writable(null);
|
4
|
+
export const searchQuery = writable('');
|
5
|
+
export const filteredAntennas = derived([antennas, searchQuery], ([$antennas, $searchQuery]) => {
|
6
|
+
if (!$searchQuery)
|
7
|
+
return $antennas;
|
8
|
+
const query = $searchQuery.toLowerCase();
|
9
|
+
return $antennas.filter(antenna => antenna.name?.toLowerCase().includes(query) ||
|
10
|
+
antenna.frequency?.toString().includes(query) ||
|
11
|
+
antenna.tilt?.toString().includes(query) ||
|
12
|
+
antenna.comment?.toLowerCase().includes(query) ||
|
13
|
+
antenna.polarization?.toLowerCase().includes(query));
|
14
|
+
});
|
@@ -0,0 +1,28 @@
|
|
1
|
+
export declare const dbStatus: import("svelte/store").Writable<{
|
2
|
+
initialized: boolean;
|
3
|
+
antennaCount: number;
|
4
|
+
lastUpdated: Date | null;
|
5
|
+
loading: boolean;
|
6
|
+
error: string | null;
|
7
|
+
}>;
|
8
|
+
export declare const dataOperationStatus: import("svelte/store").Writable<{
|
9
|
+
operation: "import" | "export" | "clear" | null;
|
10
|
+
inProgress: boolean;
|
11
|
+
success: boolean;
|
12
|
+
error: string | null;
|
13
|
+
message: string;
|
14
|
+
timestamp: Date | null;
|
15
|
+
}>;
|
16
|
+
export declare function updateDbStatus(status: Partial<{
|
17
|
+
initialized: boolean;
|
18
|
+
antennaCount: number;
|
19
|
+
lastUpdated: Date | null;
|
20
|
+
loading: boolean;
|
21
|
+
error: string | null;
|
22
|
+
}>): void;
|
23
|
+
export declare function trackDataOperation(operation: 'import' | 'export' | 'clear', status: {
|
24
|
+
inProgress: boolean;
|
25
|
+
success?: boolean;
|
26
|
+
error?: string | null;
|
27
|
+
message?: string;
|
28
|
+
}): void;
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { writable } from 'svelte/store';
|
2
|
+
// Store to track database status
|
3
|
+
export const dbStatus = writable({
|
4
|
+
initialized: false,
|
5
|
+
antennaCount: 0,
|
6
|
+
lastUpdated: null,
|
7
|
+
loading: false,
|
8
|
+
error: null
|
9
|
+
});
|
10
|
+
// Store to track import/export operations
|
11
|
+
export const dataOperationStatus = writable({
|
12
|
+
operation: null,
|
13
|
+
inProgress: false,
|
14
|
+
success: false,
|
15
|
+
error: null,
|
16
|
+
message: '',
|
17
|
+
timestamp: null
|
18
|
+
});
|
19
|
+
// Helper function to update database status
|
20
|
+
export function updateDbStatus(status) {
|
21
|
+
dbStatus.update(current => ({ ...current, ...status }));
|
22
|
+
}
|
23
|
+
// Helper function to track data operations
|
24
|
+
export function trackDataOperation(operation, status) {
|
25
|
+
dataOperationStatus.update(current => ({
|
26
|
+
...current,
|
27
|
+
operation,
|
28
|
+
inProgress: status.inProgress,
|
29
|
+
success: status.success !== undefined ? status.success : current.success,
|
30
|
+
error: status.error !== undefined ? status.error : current.error,
|
31
|
+
message: status.message || current.message,
|
32
|
+
timestamp: new Date()
|
33
|
+
}));
|
34
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import type { Antenna } from '../db';
|
2
|
+
export declare function loadAntennas(): Promise<Antenna[]>;
|
3
|
+
export declare function saveAntennas(antennaData: Antenna[]): Promise<void>;
|
4
|
+
export declare function importFromJson(jsonFile: File): Promise<Antenna[]>;
|
5
|
+
export declare function hasImportedData(): boolean;
|
6
|
+
export declare function exportToJson(data: Antenna[]): void;
|
7
|
+
export declare function exportAntennas(): Promise<void>;
|
8
|
+
export declare function clearAllAntennas(): Promise<void>;
|
9
|
+
export declare function clearDatabase(): Promise<void>;
|
@@ -0,0 +1,180 @@
|
|
1
|
+
import { db } from '../db';
|
2
|
+
import { antennas } from '../stores/antennas';
|
3
|
+
import { updateDbStatus, trackDataOperation } from '../stores/db-status';
|
4
|
+
export async function loadAntennas() {
|
5
|
+
try {
|
6
|
+
updateDbStatus({ loading: true, error: null });
|
7
|
+
// Return empty array if db is not available (SSR environment)
|
8
|
+
if (!db) {
|
9
|
+
updateDbStatus({
|
10
|
+
initialized: false,
|
11
|
+
loading: false,
|
12
|
+
error: 'Database not available in this environment'
|
13
|
+
});
|
14
|
+
return [];
|
15
|
+
}
|
16
|
+
const data = await db.antennas.toArray();
|
17
|
+
antennas.set(data);
|
18
|
+
updateDbStatus({
|
19
|
+
initialized: true,
|
20
|
+
antennaCount: data.length,
|
21
|
+
lastUpdated: new Date(),
|
22
|
+
loading: false
|
23
|
+
});
|
24
|
+
return data;
|
25
|
+
}
|
26
|
+
catch (error) {
|
27
|
+
console.error('Error loading antennas from IndexedDB:', error);
|
28
|
+
updateDbStatus({
|
29
|
+
loading: false,
|
30
|
+
error: error instanceof Error ? error.message : 'Unknown database error'
|
31
|
+
});
|
32
|
+
return [];
|
33
|
+
}
|
34
|
+
}
|
35
|
+
export async function saveAntennas(antennaData) {
|
36
|
+
try {
|
37
|
+
trackDataOperation('import', { inProgress: true, message: 'Saving antenna data...' });
|
38
|
+
// Clear existing data
|
39
|
+
await db.antennas.clear();
|
40
|
+
// Bulk add the new antennas
|
41
|
+
await db.antennas.bulkAdd(antennaData);
|
42
|
+
// Update the store
|
43
|
+
antennas.set(antennaData);
|
44
|
+
// Remember that we've imported data
|
45
|
+
localStorage.setItem('antenna-data-imported', 'true');
|
46
|
+
// Update database status
|
47
|
+
updateDbStatus({
|
48
|
+
initialized: true,
|
49
|
+
antennaCount: antennaData.length,
|
50
|
+
lastUpdated: new Date()
|
51
|
+
});
|
52
|
+
trackDataOperation('import', {
|
53
|
+
inProgress: false,
|
54
|
+
success: true,
|
55
|
+
message: `Successfully imported ${antennaData.length} antennas`
|
56
|
+
});
|
57
|
+
}
|
58
|
+
catch (error) {
|
59
|
+
console.error('Error saving antennas to IndexedDB:', error);
|
60
|
+
trackDataOperation('import', {
|
61
|
+
inProgress: false,
|
62
|
+
success: false,
|
63
|
+
error: error instanceof Error ? error.message : 'Unknown error during import',
|
64
|
+
message: 'Import failed'
|
65
|
+
});
|
66
|
+
throw error;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
export async function importFromJson(jsonFile) {
|
70
|
+
try {
|
71
|
+
trackDataOperation('import', {
|
72
|
+
inProgress: true,
|
73
|
+
message: `Reading file: ${jsonFile.name}...`
|
74
|
+
});
|
75
|
+
const text = await jsonFile.text();
|
76
|
+
const data = JSON.parse(text);
|
77
|
+
trackDataOperation('import', {
|
78
|
+
inProgress: true,
|
79
|
+
message: `Importing ${data.length} antennas...`
|
80
|
+
});
|
81
|
+
await saveAntennas(data);
|
82
|
+
return data;
|
83
|
+
}
|
84
|
+
catch (error) {
|
85
|
+
console.error('Error importing antennas from JSON:', error);
|
86
|
+
trackDataOperation('import', {
|
87
|
+
inProgress: false,
|
88
|
+
success: false,
|
89
|
+
error: error instanceof Error ? error.message : 'Invalid JSON file or format',
|
90
|
+
message: 'Import failed'
|
91
|
+
});
|
92
|
+
throw error;
|
93
|
+
}
|
94
|
+
}
|
95
|
+
export function hasImportedData() {
|
96
|
+
return localStorage.getItem('antenna-data-imported') === 'true';
|
97
|
+
}
|
98
|
+
export function exportToJson(data) {
|
99
|
+
try {
|
100
|
+
trackDataOperation('export', { inProgress: true, message: 'Preparing data export...' });
|
101
|
+
const json = JSON.stringify(data, null, 2);
|
102
|
+
const blob = new Blob([json], { type: 'application/json' });
|
103
|
+
const url = URL.createObjectURL(blob);
|
104
|
+
const a = document.createElement('a');
|
105
|
+
a.href = url;
|
106
|
+
a.download = 'antennas.json';
|
107
|
+
document.body.appendChild(a);
|
108
|
+
a.click();
|
109
|
+
// Cleanup
|
110
|
+
setTimeout(() => {
|
111
|
+
document.body.removeChild(a);
|
112
|
+
URL.revokeObjectURL(url);
|
113
|
+
trackDataOperation('export', {
|
114
|
+
inProgress: false,
|
115
|
+
success: true,
|
116
|
+
message: `Successfully exported ${data.length} antennas`
|
117
|
+
});
|
118
|
+
}, 100);
|
119
|
+
}
|
120
|
+
catch (error) {
|
121
|
+
console.error('Error exporting antennas to JSON:', error);
|
122
|
+
trackDataOperation('export', {
|
123
|
+
inProgress: false,
|
124
|
+
success: false,
|
125
|
+
error: error instanceof Error ? error.message : 'Error exporting data',
|
126
|
+
message: 'Export failed'
|
127
|
+
});
|
128
|
+
}
|
129
|
+
}
|
130
|
+
export async function exportAntennas() {
|
131
|
+
try {
|
132
|
+
const data = await db.antennas.toArray();
|
133
|
+
const jsonData = JSON.stringify(data, null, 2);
|
134
|
+
// Create and download file
|
135
|
+
const blob = new Blob([jsonData], { type: 'application/json' });
|
136
|
+
const url = URL.createObjectURL(blob);
|
137
|
+
const link = document.createElement('a');
|
138
|
+
link.href = url;
|
139
|
+
link.download = `antenna-patterns-${new Date().toISOString().split('T')[0]}.json`;
|
140
|
+
document.body.appendChild(link);
|
141
|
+
link.click();
|
142
|
+
document.body.removeChild(link);
|
143
|
+
URL.revokeObjectURL(url);
|
144
|
+
}
|
145
|
+
catch (error) {
|
146
|
+
console.error('Error exporting antennas:', error);
|
147
|
+
throw error;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
export async function clearAllAntennas() {
|
151
|
+
return clearDatabase();
|
152
|
+
}
|
153
|
+
export async function clearDatabase() {
|
154
|
+
try {
|
155
|
+
trackDataOperation('clear', { inProgress: true, message: 'Clearing database...' });
|
156
|
+
await db.antennas.clear();
|
157
|
+
antennas.set([]);
|
158
|
+
localStorage.removeItem('antenna-data-imported');
|
159
|
+
updateDbStatus({
|
160
|
+
antennaCount: 0,
|
161
|
+
lastUpdated: new Date()
|
162
|
+
});
|
163
|
+
trackDataOperation('clear', {
|
164
|
+
inProgress: false,
|
165
|
+
success: true,
|
166
|
+
message: 'Database cleared successfully'
|
167
|
+
});
|
168
|
+
return Promise.resolve();
|
169
|
+
}
|
170
|
+
catch (error) {
|
171
|
+
console.error('Error clearing database:', error);
|
172
|
+
trackDataOperation('clear', {
|
173
|
+
inProgress: false,
|
174
|
+
success: false,
|
175
|
+
error: error instanceof Error ? error.message : 'Unknown error clearing database',
|
176
|
+
message: 'Failed to clear database'
|
177
|
+
});
|
178
|
+
return Promise.reject(error);
|
179
|
+
}
|
180
|
+
}
|
@@ -0,0 +1,95 @@
|
|
1
|
+
import { db } from '../db';
|
2
|
+
import { antennas } from '../stores/antennas';
|
3
|
+
import { updateDbStatus, trackDataOperation } from '../stores/db-status';
|
4
|
+
// Initial data loading and checking
|
5
|
+
export async function checkAndInitDatabase() {
|
6
|
+
try {
|
7
|
+
updateDbStatus({ loading: true, error: null });
|
8
|
+
// Check if database is available (not in SSR)
|
9
|
+
if (!db) {
|
10
|
+
updateDbStatus({
|
11
|
+
initialized: false,
|
12
|
+
loading: false,
|
13
|
+
error: 'Database not available in this environment'
|
14
|
+
});
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
// Check if we already have data
|
18
|
+
const count = await db.antennas.count();
|
19
|
+
if (count === 0) {
|
20
|
+
// If we don't have data, check localStorage to see if we should prompt
|
21
|
+
const hasPromptedImport = localStorage.getItem('antenna-import-prompted');
|
22
|
+
updateDbStatus({
|
23
|
+
initialized: true,
|
24
|
+
antennaCount: 0,
|
25
|
+
loading: false,
|
26
|
+
lastUpdated: null
|
27
|
+
});
|
28
|
+
if (!hasPromptedImport) {
|
29
|
+
// Remember that we've prompted the user
|
30
|
+
localStorage.setItem('antenna-import-prompted', 'true');
|
31
|
+
// We'll return false to indicate the app should prompt for import
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
else {
|
36
|
+
// If we have data, load it into the store
|
37
|
+
const data = await db.antennas.toArray();
|
38
|
+
antennas.set(data);
|
39
|
+
updateDbStatus({
|
40
|
+
initialized: true,
|
41
|
+
antennaCount: count,
|
42
|
+
loading: false,
|
43
|
+
lastUpdated: new Date()
|
44
|
+
});
|
45
|
+
}
|
46
|
+
return count > 0;
|
47
|
+
}
|
48
|
+
catch (error) {
|
49
|
+
console.error('Error checking database:', error);
|
50
|
+
updateDbStatus({
|
51
|
+
loading: false,
|
52
|
+
error: error instanceof Error ? error.message : 'Unknown error checking database'
|
53
|
+
});
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
}
|
57
|
+
// Helper for setting up sample data for testing
|
58
|
+
export async function loadSampleData() {
|
59
|
+
try {
|
60
|
+
trackDataOperation('import', { inProgress: true, message: 'Loading sample data...' });
|
61
|
+
// Clear existing data
|
62
|
+
await db.antennas.clear();
|
63
|
+
// Fetch sample data from one of our JSON files
|
64
|
+
const response = await fetch('/sample-data/antennas.json');
|
65
|
+
const data = await response.json();
|
66
|
+
// Add to database
|
67
|
+
await db.antennas.bulkAdd(data);
|
68
|
+
// Update store
|
69
|
+
antennas.set(data);
|
70
|
+
// Remember that we've imported data
|
71
|
+
localStorage.setItem('antenna-data-imported', 'true');
|
72
|
+
// Update database status
|
73
|
+
updateDbStatus({
|
74
|
+
initialized: true,
|
75
|
+
antennaCount: data.length,
|
76
|
+
lastUpdated: new Date()
|
77
|
+
});
|
78
|
+
trackDataOperation('import', {
|
79
|
+
inProgress: false,
|
80
|
+
success: true,
|
81
|
+
message: `Successfully loaded ${data.length} sample antennas`
|
82
|
+
});
|
83
|
+
return true;
|
84
|
+
}
|
85
|
+
catch (error) {
|
86
|
+
console.error('Error loading sample data:', error);
|
87
|
+
trackDataOperation('import', {
|
88
|
+
inProgress: false,
|
89
|
+
success: false,
|
90
|
+
error: error instanceof Error ? error.message : 'Unknown error loading sample data',
|
91
|
+
message: 'Failed to load sample data'
|
92
|
+
});
|
93
|
+
throw error; // Re-throw to allow caller to handle
|
94
|
+
}
|
95
|
+
}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
export async function parseMSIFile(file) {
|
2
|
+
const text = await file.text();
|
3
|
+
const lines = text.split('\n');
|
4
|
+
// Initialize the result object with default values
|
5
|
+
const result = {
|
6
|
+
name: '',
|
7
|
+
frequency: 0,
|
8
|
+
gain_dBd: 0.0,
|
9
|
+
tilt: 'NO',
|
10
|
+
horizontal: 360,
|
11
|
+
pattern: Array(360).fill(0),
|
12
|
+
vertical_pattern: Array(360).fill(0)
|
13
|
+
};
|
14
|
+
// Parse filename for metadata
|
15
|
+
const filename = file.name;
|
16
|
+
const filenameParts = filename.split('_');
|
17
|
+
// Initialize filename metadata
|
18
|
+
const filenameMetadata = {
|
19
|
+
name_from_filename: '',
|
20
|
+
frequency_from_filename: 0,
|
21
|
+
x_pol: '',
|
22
|
+
co: '',
|
23
|
+
polarization: '',
|
24
|
+
tilt_from_filename: ''
|
25
|
+
};
|
26
|
+
if (filenameParts.length >= 5) {
|
27
|
+
filenameMetadata.name_from_filename = filenameParts[0];
|
28
|
+
// Extract frequency from filename
|
29
|
+
if (filenameParts.length > 1 && !isNaN(parseInt(filenameParts[1]))) {
|
30
|
+
filenameMetadata.frequency_from_filename = parseInt(filenameParts[1]);
|
31
|
+
}
|
32
|
+
// Extract X polarization
|
33
|
+
if (filenameParts.length > 2) {
|
34
|
+
filenameMetadata.x_pol = filenameParts[2];
|
35
|
+
}
|
36
|
+
// Extract CO
|
37
|
+
if (filenameParts.length > 3) {
|
38
|
+
filenameMetadata.co = filenameParts[3];
|
39
|
+
}
|
40
|
+
// Extract polarization
|
41
|
+
if (filenameParts.length > 4 && filenameParts[4].startsWith('M')) {
|
42
|
+
filenameMetadata.polarization = filenameParts[4].replace('M', '-');
|
43
|
+
}
|
44
|
+
// Extract tilt
|
45
|
+
if (filenameParts.length > 5 && filenameParts[5].endsWith('T')) {
|
46
|
+
filenameMetadata.tilt_from_filename = filenameParts[5].replace('T', '');
|
47
|
+
}
|
48
|
+
}
|
49
|
+
let currentSection = null;
|
50
|
+
// Process each line
|
51
|
+
for (const line of lines) {
|
52
|
+
const trimmedLine = line.trim();
|
53
|
+
// Skip empty lines
|
54
|
+
if (!trimmedLine)
|
55
|
+
continue;
|
56
|
+
// Check for metadata lines
|
57
|
+
if (trimmedLine.startsWith('NAME ')) {
|
58
|
+
if (!filenameMetadata.name_from_filename) {
|
59
|
+
result.name = trimmedLine.substring(5).trim();
|
60
|
+
}
|
61
|
+
else {
|
62
|
+
result.name = filenameMetadata.name_from_filename;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
else if (trimmedLine.startsWith('FREQUENCY ')) {
|
66
|
+
if (!filenameMetadata.frequency_from_filename) {
|
67
|
+
const freqText = trimmedLine.substring(10).trim();
|
68
|
+
const freqParts = freqText.split(' ');
|
69
|
+
if (freqParts.length > 0) {
|
70
|
+
try {
|
71
|
+
result.frequency = parseInt(freqParts[0]);
|
72
|
+
}
|
73
|
+
catch (error) {
|
74
|
+
result.frequency = 0;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
else {
|
79
|
+
result.frequency = filenameMetadata.frequency_from_filename;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
else if (trimmedLine.startsWith('GAIN ')) {
|
83
|
+
const gainParts = trimmedLine.substring(5).trim().split(' ');
|
84
|
+
result.gain_dBd = parseFloat(gainParts[0]);
|
85
|
+
}
|
86
|
+
else if (trimmedLine.startsWith('TILT ')) {
|
87
|
+
if (!filenameMetadata.tilt_from_filename) {
|
88
|
+
result.tilt = trimmedLine.substring(5).trim();
|
89
|
+
}
|
90
|
+
else {
|
91
|
+
result.tilt = filenameMetadata.tilt_from_filename;
|
92
|
+
}
|
93
|
+
}
|
94
|
+
else if (trimmedLine.startsWith('COMMENT ')) {
|
95
|
+
result.comment = trimmedLine.substring(8).trim();
|
96
|
+
}
|
97
|
+
else if (trimmedLine.startsWith('HORIZONTAL ')) {
|
98
|
+
currentSection = 'horizontal';
|
99
|
+
result.horizontal = parseInt(trimmedLine.substring(11).trim());
|
100
|
+
}
|
101
|
+
else if (trimmedLine.startsWith('VERTICAL ')) {
|
102
|
+
currentSection = 'vertical';
|
103
|
+
}
|
104
|
+
// Parse pattern data lines (pattern values represent attenuation where 0 is max signal)
|
105
|
+
else if (currentSection === 'horizontal' && trimmedLine.includes(' ')) {
|
106
|
+
try {
|
107
|
+
const parts = trimmedLine.split(' ').filter(part => part.trim() !== '');
|
108
|
+
if (parts.length >= 2) {
|
109
|
+
const angleStr = parts[0];
|
110
|
+
const attenuationStr = parts[1];
|
111
|
+
const angle = parseInt(angleStr);
|
112
|
+
const attenuation = parseFloat(attenuationStr);
|
113
|
+
// Ensure the angle is within bounds
|
114
|
+
if (angle >= 0 && angle < 360) {
|
115
|
+
// Store as attenuation values (0 = max signal, higher values = more attenuation)
|
116
|
+
result.pattern[angle] = attenuation;
|
117
|
+
}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
catch (error) {
|
121
|
+
// Skip lines that can't be parsed
|
122
|
+
}
|
123
|
+
}
|
124
|
+
else if (currentSection === 'vertical' && trimmedLine.includes(' ')) {
|
125
|
+
try {
|
126
|
+
const parts = trimmedLine.split(' ').filter(part => part.trim() !== '');
|
127
|
+
if (parts.length >= 2) {
|
128
|
+
const angleStr = parts[0];
|
129
|
+
const attenuationStr = parts[1];
|
130
|
+
const angle = parseInt(angleStr);
|
131
|
+
const attenuation = parseFloat(attenuationStr);
|
132
|
+
// Ensure the angle is within bounds
|
133
|
+
if (angle >= 0 && angle < 360) {
|
134
|
+
// Store as attenuation values (0 = max signal, higher values = more attenuation)
|
135
|
+
result.vertical_pattern[angle] = attenuation;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
catch (error) {
|
140
|
+
// Skip lines that can't be parsed
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
// Ensure filename metadata is included in the final result
|
145
|
+
if (!result.name && filenameMetadata.name_from_filename) {
|
146
|
+
result.name = filenameMetadata.name_from_filename;
|
147
|
+
}
|
148
|
+
if (!result.frequency && filenameMetadata.frequency_from_filename) {
|
149
|
+
result.frequency = filenameMetadata.frequency_from_filename;
|
150
|
+
}
|
151
|
+
if (!result.tilt && filenameMetadata.tilt_from_filename) {
|
152
|
+
result.tilt = filenameMetadata.tilt_from_filename;
|
153
|
+
}
|
154
|
+
// Always include these fields from the filename
|
155
|
+
result.x_pol = filenameMetadata.x_pol;
|
156
|
+
result.co = filenameMetadata.co;
|
157
|
+
result.polarization = filenameMetadata.polarization;
|
158
|
+
return result;
|
159
|
+
}
|
160
|
+
export async function parseFolder(directoryHandle, recursive = true) {
|
161
|
+
const antennas = [];
|
162
|
+
// Process directory recursively
|
163
|
+
async function processDirectory(dirHandle, path = '') {
|
164
|
+
const iterator = dirHandle.values();
|
165
|
+
for await (const entry of iterator) {
|
166
|
+
if (entry.kind === 'file') {
|
167
|
+
try {
|
168
|
+
// Type assertion to access getFile method
|
169
|
+
const fileHandle = entry;
|
170
|
+
const file = await fileHandle.getFile();
|
171
|
+
const ext = file.name.split('.').pop()?.toLowerCase();
|
172
|
+
if (ext === 'msi' || ext === 'pnt') {
|
173
|
+
try {
|
174
|
+
const antenna = await parseMSIFile(file);
|
175
|
+
// Add the path info to help identify where the file was found
|
176
|
+
antenna.sourcePath = path ? `${path}/${file.name}` : file.name;
|
177
|
+
antennas.push(antenna);
|
178
|
+
}
|
179
|
+
catch (error) {
|
180
|
+
console.error(`Error parsing file ${path ? `${path}/` : ''}${file.name}:`, error);
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}
|
184
|
+
catch (error) {
|
185
|
+
console.error('Error accessing file:', error);
|
186
|
+
}
|
187
|
+
}
|
188
|
+
else if (entry.kind === 'directory' && recursive) {
|
189
|
+
// Process subdirectory if recursive flag is true
|
190
|
+
const subDirPath = path ? `${path}/${entry.name}` : entry.name;
|
191
|
+
await processDirectory(entry, subDirPath);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
await processDirectory(directoryHandle);
|
196
|
+
return antennas;
|
197
|
+
}
|