@smartnet360/svelte-components 0.0.122 → 0.0.124
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-tools/band-config.d.ts +3 -2
- package/dist/apps/antenna-tools/band-config.js +6 -4
- package/dist/apps/antenna-tools/components/AntennaControls.svelte +71 -9
- package/dist/apps/antenna-tools/components/AntennaControls.svelte.d.ts +2 -0
- package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +5 -2
- package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte.d.ts +3 -0
- package/dist/apps/antenna-tools/components/AntennaTools.svelte +55 -85
- package/dist/apps/antenna-tools/components/AntennaTools.svelte.d.ts +5 -3
- package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +3 -6
- package/dist/apps/antenna-tools/components/MSIConverter.svelte +123 -15
- package/dist/apps/antenna-tools/components/MSIConverter.svelte.d.ts +3 -0
- package/dist/apps/antenna-tools/types.d.ts +8 -3
- package/dist/apps/antenna-tools/types.js +14 -8
- package/dist/apps/antenna-tools/utils/db-utils.d.ts +3 -2
- package/dist/apps/antenna-tools/utils/db-utils.js +3 -2
- package/dist/apps/antenna-tools/utils/msi-parser.d.ts +50 -2
- package/dist/apps/antenna-tools/utils/msi-parser.js +110 -27
- package/dist/apps/index.d.ts +2 -0
- package/dist/apps/index.js +1 -0
- package/dist/core/Benchmark/Benchmark.svelte +662 -0
- package/dist/core/Benchmark/Benchmark.svelte.d.ts +3 -0
- package/dist/core/Benchmark/benchmark-utils.d.ts +48 -0
- package/dist/core/Benchmark/benchmark-utils.js +80 -0
- package/dist/core/Benchmark/index.d.ts +2 -0
- package/dist/core/Benchmark/index.js +3 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/package.json +1 -1
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
<svelte:options runes={true} />
|
|
2
2
|
|
|
3
3
|
<script lang="ts">
|
|
4
|
-
import {
|
|
4
|
+
import { parseFolderWithErrors, type ParseProgress, type ParseError } from '../utils/msi-parser';
|
|
5
5
|
import { saveAntennasWithPurge, type ImportResult } from '../utils/db-utils';
|
|
6
6
|
import { STANDARD_BANDS } from '../band-config';
|
|
7
|
-
import type { RawAntenna, Antenna } from '../types';
|
|
7
|
+
import type { RawAntenna, Antenna, BandDefinition } from '../types';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
onDataRefresh?: () => void;
|
|
11
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
12
|
+
customBands?: BandDefinition[];
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
let { onDataRefresh }: Props = $props();
|
|
15
|
+
let { onDataRefresh, customBands }: Props = $props();
|
|
16
|
+
|
|
17
|
+
// Use custom bands if provided, otherwise use standard bands
|
|
18
|
+
let activeBands = $derived(customBands ?? STANDARD_BANDS);
|
|
14
19
|
|
|
15
20
|
let rawAntennas = $state<RawAntenna[]>([]);
|
|
16
21
|
let isLoading = $state(false);
|
|
17
22
|
let error = $state('');
|
|
18
23
|
let message = $state('');
|
|
19
24
|
let importResult = $state<ImportResult | null>(null);
|
|
25
|
+
let parseProgress = $state<ParseProgress | null>(null);
|
|
26
|
+
let parseErrors = $state<ParseError[]>([]);
|
|
27
|
+
let showErrorDetails = $state(false);
|
|
20
28
|
|
|
21
29
|
let recursiveScan = $state(true);
|
|
22
30
|
|
|
@@ -32,19 +40,32 @@
|
|
|
32
40
|
error = '';
|
|
33
41
|
message = 'Selecting folder...';
|
|
34
42
|
importResult = null;
|
|
43
|
+
parseProgress = null;
|
|
44
|
+
parseErrors = [];
|
|
45
|
+
showErrorDetails = false;
|
|
35
46
|
|
|
36
47
|
// Ask user to select a folder
|
|
37
48
|
const directoryPicker = window.showDirectoryPicker as () => Promise<FileSystemDirectoryHandle>;
|
|
38
49
|
const directoryHandle = await directoryPicker();
|
|
39
50
|
message = recursiveScan ?
|
|
40
|
-
'
|
|
41
|
-
'
|
|
51
|
+
'Scanning folders and parsing files...' :
|
|
52
|
+
'Parsing files (top-level folder only)...';
|
|
53
|
+
|
|
54
|
+
// Parse all MSI/PNT files in the folder, with recursive option and progress
|
|
55
|
+
const result = await parseFolderWithErrors(directoryHandle, recursiveScan, (progress) => {
|
|
56
|
+
parseProgress = progress;
|
|
57
|
+
});
|
|
42
58
|
|
|
43
|
-
|
|
44
|
-
|
|
59
|
+
rawAntennas = result.antennas;
|
|
60
|
+
parseErrors = result.errors;
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
parseProgress = null;
|
|
63
|
+
if (rawAntennas.length === 0 && parseErrors.length === 0) {
|
|
47
64
|
message = 'No MSI or PNT files found in the selected folder.';
|
|
65
|
+
} else if (rawAntennas.length === 0 && parseErrors.length > 0) {
|
|
66
|
+
message = `No files were successfully parsed. ${parseErrors.length} file(s) failed.`;
|
|
67
|
+
} else if (parseErrors.length > 0) {
|
|
68
|
+
message = `Parsed ${rawAntennas.length} antenna files${recursiveScan ? ' from all folders' : ''}. ${parseErrors.length} file(s) failed.`;
|
|
48
69
|
} else {
|
|
49
70
|
message = `Successfully parsed ${rawAntennas.length} antenna files${recursiveScan ? ' from all folders' : ''}.`;
|
|
50
71
|
}
|
|
@@ -75,8 +96,8 @@
|
|
|
75
96
|
isLoading = true;
|
|
76
97
|
message = 'Purging frequencies and saving to database...';
|
|
77
98
|
|
|
78
|
-
// Save with automatic purge
|
|
79
|
-
importResult = await saveAntennasWithPurge(rawAntennas);
|
|
99
|
+
// Save with automatic purge (using custom bands if provided)
|
|
100
|
+
importResult = await saveAntennasWithPurge(rawAntennas, customBands);
|
|
80
101
|
|
|
81
102
|
if (importResult.success) {
|
|
82
103
|
const stats = importResult.purgeStats;
|
|
@@ -122,16 +143,16 @@
|
|
|
122
143
|
<header class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-3 mb-4">
|
|
123
144
|
<div>
|
|
124
145
|
<h1 class="h3 mb-2">Convert MSI / PNT to JSON</h1>
|
|
125
|
-
<p class="text-muted mb-0">Scan a folder of antenna definitions. Files will be purged to keep only
|
|
146
|
+
<p class="text-muted mb-0">Scan a folder of antenna definitions. Files will be purged to keep only configured bands.</p>
|
|
126
147
|
</div>
|
|
127
148
|
<span class="badge text-bg-success">Local processing</span>
|
|
128
149
|
</header>
|
|
129
150
|
|
|
130
|
-
<!--
|
|
151
|
+
<!-- Bands Info -->
|
|
131
152
|
<div class="alert alert-info border-0 bg-info-subtle mb-4">
|
|
132
|
-
<strong><i class="bi bi-info-circle me-2"></i>Standard Bands:</strong>
|
|
153
|
+
<strong><i class="bi bi-info-circle me-2"></i>{customBands ? 'Custom' : 'Standard'} Bands:</strong>
|
|
133
154
|
<div class="d-flex flex-wrap gap-2 mt-2">
|
|
134
|
-
{#each
|
|
155
|
+
{#each activeBands as band}
|
|
135
156
|
<span class="badge text-bg-primary">{band.name} MHz ({band.dlMin}-{band.dlMax})</span>
|
|
136
157
|
{/each}
|
|
137
158
|
</div>
|
|
@@ -183,13 +204,100 @@
|
|
|
183
204
|
</div>
|
|
184
205
|
{/if}
|
|
185
206
|
|
|
186
|
-
|
|
207
|
+
<!-- Progress indicator during parsing -->
|
|
208
|
+
{#if isLoading && parseProgress}
|
|
209
|
+
<div class="card border-info">
|
|
210
|
+
<div class="card-body">
|
|
211
|
+
<div class="d-flex align-items-center gap-3 mb-3">
|
|
212
|
+
<div class="spinner-border spinner-border-sm text-info" role="status">
|
|
213
|
+
<span class="visually-hidden">Loading...</span>
|
|
214
|
+
</div>
|
|
215
|
+
<span class="text-info fw-semibold">Parsing antenna files...</span>
|
|
216
|
+
</div>
|
|
217
|
+
<div class="row text-center mb-3">
|
|
218
|
+
<div class="col-3">
|
|
219
|
+
<div class="h4 mb-0 text-primary">{parseProgress.directoriesScanned}</div>
|
|
220
|
+
<small class="text-muted">Folders</small>
|
|
221
|
+
</div>
|
|
222
|
+
<div class="col-3">
|
|
223
|
+
<div class="h4 mb-0 text-info">{parseProgress.filesProcessed}</div>
|
|
224
|
+
<small class="text-muted">Processed</small>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="col-3">
|
|
227
|
+
<div class="h4 mb-0 text-success">{parseProgress.antennaFilesFound}</div>
|
|
228
|
+
<small class="text-muted">Parsed</small>
|
|
229
|
+
</div>
|
|
230
|
+
<div class="col-3">
|
|
231
|
+
<div class="h4 mb-0 {parseProgress.failedFiles > 0 ? 'text-danger' : 'text-muted'}">{parseProgress.failedFiles}</div>
|
|
232
|
+
<small class="text-muted">Failed</small>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
<div class="text-muted small text-truncate">
|
|
236
|
+
<i class="bi bi-file-earmark me-1"></i>
|
|
237
|
+
{parseProgress.currentFile}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
{/if}
|
|
242
|
+
|
|
243
|
+
{#if message && !error && !parseProgress}
|
|
187
244
|
<div class="alert alert-info border-0 bg-info-subtle text-info-emphasis" role="alert">
|
|
188
245
|
<i class="bi bi-info-circle me-2"></i>
|
|
189
246
|
{message}
|
|
190
247
|
</div>
|
|
191
248
|
{/if}
|
|
192
249
|
|
|
250
|
+
<!-- Parse Errors Summary -->
|
|
251
|
+
{#if parseErrors.length > 0 && !parseProgress}
|
|
252
|
+
<div class="alert alert-warning border-0 bg-warning-subtle" role="alert">
|
|
253
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
254
|
+
<div>
|
|
255
|
+
<i class="bi bi-exclamation-triangle me-2"></i>
|
|
256
|
+
<strong>{parseErrors.length} file(s) failed to parse</strong>
|
|
257
|
+
<span class="text-muted ms-2">- Import continued with remaining files</span>
|
|
258
|
+
</div>
|
|
259
|
+
<button
|
|
260
|
+
class="btn btn-sm btn-outline-warning"
|
|
261
|
+
onclick={() => showErrorDetails = !showErrorDetails}
|
|
262
|
+
>
|
|
263
|
+
{showErrorDetails ? 'Hide' : 'Show'} details
|
|
264
|
+
</button>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{#if showErrorDetails}
|
|
268
|
+
<hr class="my-2">
|
|
269
|
+
<div class="small" style="max-height: 200px; overflow-y: auto;">
|
|
270
|
+
<table class="table table-sm table-borderless mb-0">
|
|
271
|
+
<thead>
|
|
272
|
+
<tr>
|
|
273
|
+
<th style="width: 60px;">Type</th>
|
|
274
|
+
<th>Path</th>
|
|
275
|
+
<th>Error</th>
|
|
276
|
+
</tr>
|
|
277
|
+
</thead>
|
|
278
|
+
<tbody>
|
|
279
|
+
{#each parseErrors as parseError}
|
|
280
|
+
<tr>
|
|
281
|
+
<td>
|
|
282
|
+
{#if parseError.type === 'folder'}
|
|
283
|
+
<span class="badge bg-secondary"><i class="bi bi-folder"></i></span>
|
|
284
|
+
{:else}
|
|
285
|
+
<span class="badge bg-danger"><i class="bi bi-file-earmark"></i></span>
|
|
286
|
+
{/if}
|
|
287
|
+
</td>
|
|
288
|
+
<td class="text-truncate" style="max-width: 300px;" title={parseError.path}>
|
|
289
|
+
{parseError.path}
|
|
290
|
+
</td>
|
|
291
|
+
<td class="text-muted">{parseError.message}</td>
|
|
292
|
+
</tr>
|
|
293
|
+
{/each}
|
|
294
|
+
</tbody>
|
|
295
|
+
</table>
|
|
296
|
+
</div>
|
|
297
|
+
{/if}
|
|
298
|
+
</div>
|
|
299
|
+
{/if}
|
|
300
|
+
|
|
193
301
|
{#if error}
|
|
194
302
|
<div class="alert alert-danger border-0 bg-danger-subtle text-danger-emphasis" role="alert">
|
|
195
303
|
<i class="bi bi-exclamation-octagon me-2"></i>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { BandDefinition } from '../types';
|
|
1
2
|
interface Props {
|
|
2
3
|
onDataRefresh?: () => void;
|
|
4
|
+
/** Custom band definitions for purge (defaults to STANDARD_BANDS) */
|
|
5
|
+
customBands?: BandDefinition[];
|
|
3
6
|
}
|
|
4
7
|
declare const MsiConverter: import("svelte").Component<Props, {}, "">;
|
|
5
8
|
type MsiConverter = ReturnType<typeof MsiConverter>;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Frequency band definition for filtering antennas
|
|
7
7
|
*/
|
|
8
8
|
export interface BandDefinition {
|
|
9
|
-
/** Simple band name: "700", "800", "900", "1800", "2100", "2600" */
|
|
9
|
+
/** Simple band name: "700", "800", "900", "1800", "2100", "2600", "3500" */
|
|
10
10
|
name: string;
|
|
11
11
|
/** Downlink minimum frequency (MHz) */
|
|
12
12
|
dlMin: number;
|
|
@@ -14,8 +14,13 @@ export interface BandDefinition {
|
|
|
14
14
|
dlMax: number;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Standard frequency bands
|
|
18
|
-
*
|
|
17
|
+
* Standard frequency bands with FULL downlink ranges per 3GPP standards
|
|
18
|
+
* These are the complete DL frequency ranges, not narrow slices
|
|
19
|
+
*
|
|
20
|
+
* References:
|
|
21
|
+
* - 3GPP TS 36.101 (LTE)
|
|
22
|
+
* - 3GPP TS 38.101 (5G NR)
|
|
23
|
+
* - ETSI standards for GSM
|
|
19
24
|
*/
|
|
20
25
|
export declare const STANDARD_BANDS: BandDefinition[];
|
|
21
26
|
/**
|
|
@@ -3,14 +3,20 @@
|
|
|
3
3
|
* Clean, centralized type definitions for the antenna-tools module
|
|
4
4
|
*/
|
|
5
5
|
/**
|
|
6
|
-
* Standard frequency bands
|
|
7
|
-
*
|
|
6
|
+
* Standard frequency bands with FULL downlink ranges per 3GPP standards
|
|
7
|
+
* These are the complete DL frequency ranges, not narrow slices
|
|
8
|
+
*
|
|
9
|
+
* References:
|
|
10
|
+
* - 3GPP TS 36.101 (LTE)
|
|
11
|
+
* - 3GPP TS 38.101 (5G NR)
|
|
12
|
+
* - ETSI standards for GSM
|
|
8
13
|
*/
|
|
9
14
|
export const STANDARD_BANDS = [
|
|
10
|
-
{ name: '700', dlMin: 758, dlMax:
|
|
11
|
-
{ name: '800', dlMin: 791, dlMax:
|
|
12
|
-
{ name: '900', dlMin:
|
|
13
|
-
{ name: '1800', dlMin: 1805, dlMax: 1880 }, // GSM 1800 / LTE B3
|
|
14
|
-
{ name: '2100', dlMin: 2110, dlMax: 2170 }, // LTE B1
|
|
15
|
-
{ name: '2600', dlMin: 2620, dlMax: 2690 }, // LTE B7
|
|
15
|
+
{ name: '700', dlMin: 758, dlMax: 803 }, // LTE B28 (APT 700) - Full DL: 758-803 MHz
|
|
16
|
+
{ name: '800', dlMin: 791, dlMax: 862 }, // LTE B20 (EU 800) - Full DL: 791-862 MHz
|
|
17
|
+
{ name: '900', dlMin: 925, dlMax: 960 }, // GSM 900 / LTE B8 - Full DL: 925-960 MHz
|
|
18
|
+
{ name: '1800', dlMin: 1805, dlMax: 1880 }, // GSM 1800 / LTE B3 - Full DL: 1805-1880 MHz
|
|
19
|
+
{ name: '2100', dlMin: 2110, dlMax: 2170 }, // LTE B1 / UMTS - Full DL: 2110-2170 MHz
|
|
20
|
+
{ name: '2600', dlMin: 2620, dlMax: 2690 }, // LTE B7 - Full DL: 2620-2690 MHz
|
|
21
|
+
{ name: '3500', dlMin: 3400, dlMax: 3800 }, // 5G NR n78 (C-Band) - Full: 3400-3800 MHz
|
|
16
22
|
];
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Antenna Tools - Database Utilities
|
|
3
3
|
* CRUD operations for antenna data with automatic purge on import
|
|
4
4
|
*/
|
|
5
|
-
import type { Antenna, RawAntenna } from '../types';
|
|
5
|
+
import type { Antenna, RawAntenna, BandDefinition } from '../types';
|
|
6
6
|
import { type PurgeStats } from '../band-config';
|
|
7
7
|
/**
|
|
8
8
|
* Load all antennas from database
|
|
@@ -25,9 +25,10 @@ export interface ImportResult {
|
|
|
25
25
|
* 3. Saves purged antennas to database
|
|
26
26
|
*
|
|
27
27
|
* @param rawAntennas - Antennas parsed from MSI files
|
|
28
|
+
* @param customBands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
28
29
|
* @returns Import result with purge statistics
|
|
29
30
|
*/
|
|
30
|
-
export declare function saveAntennasWithPurge(rawAntennas: RawAntenna[]): Promise<ImportResult>;
|
|
31
|
+
export declare function saveAntennasWithPurge(rawAntennas: RawAntenna[], customBands?: BandDefinition[]): Promise<ImportResult>;
|
|
31
32
|
/**
|
|
32
33
|
* Save antennas directly (without purge) - for pre-purged data
|
|
33
34
|
*/
|
|
@@ -52,13 +52,14 @@ export async function loadAntennas() {
|
|
|
52
52
|
* 3. Saves purged antennas to database
|
|
53
53
|
*
|
|
54
54
|
* @param rawAntennas - Antennas parsed from MSI files
|
|
55
|
+
* @param customBands - Optional custom band definitions (defaults to STANDARD_BANDS)
|
|
55
56
|
* @returns Import result with purge statistics
|
|
56
57
|
*/
|
|
57
|
-
export async function saveAntennasWithPurge(rawAntennas) {
|
|
58
|
+
export async function saveAntennasWithPurge(rawAntennas, customBands) {
|
|
58
59
|
try {
|
|
59
60
|
trackDataOperation('import', { inProgress: true, message: 'Purging frequencies...' });
|
|
60
61
|
// Step 1: Purge - keep only one antenna per band
|
|
61
|
-
const purgedAntennas = purgeAntennas(rawAntennas);
|
|
62
|
+
const purgedAntennas = purgeAntennas(rawAntennas, customBands);
|
|
62
63
|
const purgeStats = calculatePurgeStats(rawAntennas.length, purgedAntennas);
|
|
63
64
|
console.log(`[saveAntennasWithPurge] Purge complete: ${purgeStats.totalBefore} → ${purgeStats.totalAfter} antennas (${purgeStats.reductionPercent}% reduction)`);
|
|
64
65
|
trackDataOperation('import', { inProgress: true, message: `Saving ${purgedAntennas.length} antennas...` });
|
|
@@ -12,10 +12,58 @@ import type { RawAntenna } from '../types';
|
|
|
12
12
|
* @returns Parsed antenna with actual MHz frequency
|
|
13
13
|
*/
|
|
14
14
|
export declare function parseMSIFile(file: File): Promise<RawAntenna>;
|
|
15
|
+
/**
|
|
16
|
+
* Progress information during folder parsing
|
|
17
|
+
*/
|
|
18
|
+
export interface ParseProgress {
|
|
19
|
+
/** Current file being processed */
|
|
20
|
+
currentFile: string;
|
|
21
|
+
/** Number of files processed so far */
|
|
22
|
+
filesProcessed: number;
|
|
23
|
+
/** Number of directories scanned */
|
|
24
|
+
directoriesScanned: number;
|
|
25
|
+
/** Number of MSI/PNT files found */
|
|
26
|
+
antennaFilesFound: number;
|
|
27
|
+
/** Number of files that failed to parse */
|
|
28
|
+
failedFiles: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Error information for failed file/folder parsing
|
|
32
|
+
*/
|
|
33
|
+
export interface ParseError {
|
|
34
|
+
/** Path to the file or folder that failed */
|
|
35
|
+
path: string;
|
|
36
|
+
/** Error message */
|
|
37
|
+
message: string;
|
|
38
|
+
/** Type of error */
|
|
39
|
+
type: 'file' | 'folder';
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Result of folder parsing including errors
|
|
43
|
+
*/
|
|
44
|
+
export interface ParseFolderResult {
|
|
45
|
+
/** Successfully parsed antennas */
|
|
46
|
+
antennas: RawAntenna[];
|
|
47
|
+
/** Errors encountered during parsing */
|
|
48
|
+
errors: ParseError[];
|
|
49
|
+
/** Total files attempted */
|
|
50
|
+
totalFilesAttempted: number;
|
|
51
|
+
/** Total directories scanned */
|
|
52
|
+
totalDirectoriesScanned: number;
|
|
53
|
+
}
|
|
15
54
|
/**
|
|
16
55
|
* Parse a folder of MSI files recursively
|
|
17
56
|
* @param directoryHandle - File system directory handle
|
|
18
57
|
* @param recursive - Whether to process subdirectories
|
|
19
|
-
* @
|
|
58
|
+
* @param onProgress - Optional callback for progress updates
|
|
59
|
+
* @returns Array of parsed antennas (for backward compatibility)
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseFolder(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean, onProgress?: (progress: ParseProgress) => void): Promise<RawAntenna[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Parse a folder of MSI files recursively with detailed error reporting
|
|
64
|
+
* @param directoryHandle - File system directory handle
|
|
65
|
+
* @param recursive - Whether to process subdirectories
|
|
66
|
+
* @param onProgress - Optional callback for progress updates
|
|
67
|
+
* @returns Object containing parsed antennas and any errors encountered
|
|
20
68
|
*/
|
|
21
|
-
export declare function
|
|
69
|
+
export declare function parseFolderWithErrors(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean, onProgress?: (progress: ParseProgress) => void): Promise<ParseFolderResult>;
|
|
@@ -173,43 +173,126 @@ export async function parseMSIFile(file) {
|
|
|
173
173
|
* Parse a folder of MSI files recursively
|
|
174
174
|
* @param directoryHandle - File system directory handle
|
|
175
175
|
* @param recursive - Whether to process subdirectories
|
|
176
|
-
* @
|
|
176
|
+
* @param onProgress - Optional callback for progress updates
|
|
177
|
+
* @returns Array of parsed antennas (for backward compatibility)
|
|
177
178
|
*/
|
|
178
|
-
export async function parseFolder(directoryHandle, recursive = true) {
|
|
179
|
+
export async function parseFolder(directoryHandle, recursive = true, onProgress) {
|
|
180
|
+
const result = await parseFolderWithErrors(directoryHandle, recursive, onProgress);
|
|
181
|
+
return result.antennas;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Parse a folder of MSI files recursively with detailed error reporting
|
|
185
|
+
* @param directoryHandle - File system directory handle
|
|
186
|
+
* @param recursive - Whether to process subdirectories
|
|
187
|
+
* @param onProgress - Optional callback for progress updates
|
|
188
|
+
* @returns Object containing parsed antennas and any errors encountered
|
|
189
|
+
*/
|
|
190
|
+
export async function parseFolderWithErrors(directoryHandle, recursive = true, onProgress) {
|
|
179
191
|
const antennas = [];
|
|
192
|
+
const errors = [];
|
|
193
|
+
let filesProcessed = 0;
|
|
194
|
+
let directoriesScanned = 0;
|
|
195
|
+
let failedFiles = 0;
|
|
180
196
|
// Process directory recursively
|
|
181
197
|
async function processDirectory(dirHandle, path = '') {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
directoriesScanned++;
|
|
199
|
+
let iterator;
|
|
200
|
+
try {
|
|
201
|
+
iterator = dirHandle.values();
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const dirPath = path || dirHandle.name;
|
|
205
|
+
errors.push({
|
|
206
|
+
path: dirPath,
|
|
207
|
+
message: error instanceof Error ? error.message : 'Failed to read directory',
|
|
208
|
+
type: 'folder'
|
|
209
|
+
});
|
|
210
|
+
console.error(`Error reading directory ${dirPath}:`, error);
|
|
211
|
+
return; // Skip this directory but continue with others
|
|
212
|
+
}
|
|
213
|
+
try {
|
|
214
|
+
for await (const entry of iterator) {
|
|
215
|
+
if (entry.kind === 'file') {
|
|
216
|
+
try {
|
|
217
|
+
// Type assertion to access getFile method
|
|
218
|
+
const fileHandle = entry;
|
|
219
|
+
const file = await fileHandle.getFile();
|
|
220
|
+
const ext = file.name.split('.').pop()?.toLowerCase();
|
|
221
|
+
if (ext === 'msi' || ext === 'pnt') {
|
|
222
|
+
filesProcessed++;
|
|
223
|
+
const currentPath = path ? `${path}/${file.name}` : file.name;
|
|
224
|
+
// Report progress
|
|
225
|
+
onProgress?.({
|
|
226
|
+
currentFile: currentPath,
|
|
227
|
+
filesProcessed,
|
|
228
|
+
directoriesScanned,
|
|
229
|
+
antennaFilesFound: antennas.length,
|
|
230
|
+
failedFiles
|
|
231
|
+
});
|
|
232
|
+
try {
|
|
233
|
+
const antenna = await parseMSIFile(file);
|
|
234
|
+
// Add the path info to help identify where the file was found
|
|
235
|
+
antenna.sourcePath = currentPath;
|
|
236
|
+
antennas.push(antenna);
|
|
237
|
+
}
|
|
238
|
+
catch (parseError) {
|
|
239
|
+
failedFiles++;
|
|
240
|
+
errors.push({
|
|
241
|
+
path: currentPath,
|
|
242
|
+
message: parseError instanceof Error ? parseError.message : 'Failed to parse file',
|
|
243
|
+
type: 'file'
|
|
244
|
+
});
|
|
245
|
+
console.error(`Error parsing file ${currentPath}:`, parseError);
|
|
246
|
+
// Continue with next file
|
|
247
|
+
}
|
|
199
248
|
}
|
|
200
249
|
}
|
|
250
|
+
catch (fileError) {
|
|
251
|
+
const filePath = path ? `${path}/${entry.name}` : entry.name;
|
|
252
|
+
failedFiles++;
|
|
253
|
+
errors.push({
|
|
254
|
+
path: filePath,
|
|
255
|
+
message: fileError instanceof Error ? fileError.message : 'Failed to access file',
|
|
256
|
+
type: 'file'
|
|
257
|
+
});
|
|
258
|
+
console.error(`Error accessing file ${filePath}:`, fileError);
|
|
259
|
+
// Continue with next file
|
|
260
|
+
}
|
|
201
261
|
}
|
|
202
|
-
|
|
203
|
-
|
|
262
|
+
else if (entry.kind === 'directory' && recursive) {
|
|
263
|
+
// Process subdirectory if recursive flag is true
|
|
264
|
+
const subDirPath = path ? `${path}/${entry.name}` : entry.name;
|
|
265
|
+
try {
|
|
266
|
+
await processDirectory(entry, subDirPath);
|
|
267
|
+
}
|
|
268
|
+
catch (subDirError) {
|
|
269
|
+
errors.push({
|
|
270
|
+
path: subDirPath,
|
|
271
|
+
message: subDirError instanceof Error ? subDirError.message : 'Failed to process subdirectory',
|
|
272
|
+
type: 'folder'
|
|
273
|
+
});
|
|
274
|
+
console.error(`Error processing subdirectory ${subDirPath}:`, subDirError);
|
|
275
|
+
// Continue with next entry
|
|
276
|
+
}
|
|
204
277
|
}
|
|
205
278
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
279
|
+
}
|
|
280
|
+
catch (iterError) {
|
|
281
|
+
const dirPath = path || dirHandle.name;
|
|
282
|
+
errors.push({
|
|
283
|
+
path: dirPath,
|
|
284
|
+
message: iterError instanceof Error ? iterError.message : 'Error iterating directory contents',
|
|
285
|
+
type: 'folder'
|
|
286
|
+
});
|
|
287
|
+
console.error(`Error iterating directory ${dirPath}:`, iterError);
|
|
288
|
+
// Continue - the directory iteration failed but we can still return what we have
|
|
211
289
|
}
|
|
212
290
|
}
|
|
213
291
|
await processDirectory(directoryHandle);
|
|
214
|
-
return
|
|
292
|
+
return {
|
|
293
|
+
antennas,
|
|
294
|
+
errors,
|
|
295
|
+
totalFilesAttempted: filesProcessed,
|
|
296
|
+
totalDirectoriesScanned: directoriesScanned
|
|
297
|
+
};
|
|
215
298
|
}
|
package/dist/apps/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export * from './antenna-pattern/index.js';
|
|
|
2
2
|
export { default as AntennaTools } from './antenna-tools/components/AntennaTools.svelte';
|
|
3
3
|
export { default as AntennaDatabaseModal } from './antenna-tools/components/AntennaSettingsModal.svelte';
|
|
4
4
|
export { default as AntennaDatabaseViewer } from './antenna-tools/components/DatabaseViewer.svelte';
|
|
5
|
+
export type { BandDefinition, Antenna, RawAntenna, ExternalAntennaInput } from './antenna-tools/types.js';
|
|
6
|
+
export { STANDARD_BANDS } from './antenna-tools/types.js';
|
|
5
7
|
export * from './site-check/index.js';
|
package/dist/apps/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './antenna-pattern/index.js';
|
|
|
7
7
|
export { default as AntennaTools } from './antenna-tools/components/AntennaTools.svelte';
|
|
8
8
|
export { default as AntennaDatabaseModal } from './antenna-tools/components/AntennaSettingsModal.svelte';
|
|
9
9
|
export { default as AntennaDatabaseViewer } from './antenna-tools/components/DatabaseViewer.svelte';
|
|
10
|
+
export { STANDARD_BANDS } from './antenna-tools/types.js';
|
|
10
11
|
// Test app (if needed for demos)
|
|
11
12
|
// export * from './test/index.js';
|
|
12
13
|
export * from './site-check/index.js';
|