@smartnet360/svelte-components 0.0.11 → 0.0.13

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.
Files changed (88) hide show
  1. package/dist/apps/antenna-pattern/components/AntennaControls.svelte +506 -0
  2. package/dist/apps/antenna-pattern/components/AntennaControls.svelte.d.ts +16 -0
  3. package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte +62 -0
  4. package/dist/apps/antenna-pattern/components/AntennaDataDropdown.svelte.d.ts +18 -0
  5. package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +339 -0
  6. package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte.d.ts +3 -0
  7. package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +299 -0
  8. package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte.d.ts +24 -0
  9. package/dist/apps/antenna-pattern/components/DbNotification.svelte +67 -0
  10. package/dist/apps/antenna-pattern/components/DbNotification.svelte.d.ts +18 -0
  11. package/dist/apps/antenna-pattern/components/JsonImporter.svelte +116 -0
  12. package/dist/apps/antenna-pattern/components/JsonImporter.svelte.d.ts +18 -0
  13. package/dist/apps/antenna-pattern/components/MSIConverter.svelte +207 -0
  14. package/dist/apps/antenna-pattern/components/MSIConverter.svelte.d.ts +18 -0
  15. package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +252 -0
  16. package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte.d.ts +22 -0
  17. package/dist/apps/antenna-pattern/db.d.ts +24 -0
  18. package/dist/apps/antenna-pattern/db.js +15 -0
  19. package/dist/apps/antenna-pattern/helpers/plotly-utils.d.ts +54 -0
  20. package/dist/apps/antenna-pattern/helpers/plotly-utils.js +324 -0
  21. package/dist/apps/antenna-pattern/index.d.ts +15 -0
  22. package/dist/apps/antenna-pattern/index.js +19 -0
  23. package/dist/apps/antenna-pattern/stores/antennas.d.ts +5 -0
  24. package/dist/apps/antenna-pattern/stores/antennas.js +14 -0
  25. package/dist/apps/antenna-pattern/stores/db-status.d.ts +28 -0
  26. package/dist/apps/antenna-pattern/stores/db-status.js +34 -0
  27. package/dist/apps/antenna-pattern/utils/db-utils.d.ts +9 -0
  28. package/dist/apps/antenna-pattern/utils/db-utils.js +180 -0
  29. package/dist/apps/antenna-pattern/utils/init-db.d.ts +2 -0
  30. package/dist/apps/antenna-pattern/utils/init-db.js +95 -0
  31. package/dist/apps/antenna-pattern/utils/msi-parser.d.ts +3 -0
  32. package/dist/apps/antenna-pattern/utils/msi-parser.js +197 -0
  33. package/dist/apps/antenna-pattern/utils/plotly-chart-utils.d.ts +101 -0
  34. package/dist/apps/antenna-pattern/utils/plotly-chart-utils.js +152 -0
  35. package/dist/apps/index.d.ts +1 -0
  36. package/dist/apps/index.js +6 -0
  37. package/dist/{Charts → core/Charts}/ChartComponent.svelte +131 -39
  38. package/dist/{Charts → core/Charts}/charts.model.d.ts +1 -1
  39. package/dist/{Desktop → core/Desktop}/GridRenderer.svelte +1 -1
  40. package/dist/core/index.d.ts +2 -0
  41. package/dist/core/index.js +6 -0
  42. package/dist/index.d.ts +2 -2
  43. package/dist/index.js +6 -2
  44. package/package.json +6 -2
  45. /package/dist/{Charts → core/Charts}/ChartCard.svelte +0 -0
  46. /package/dist/{Charts → core/Charts}/ChartCard.svelte.d.ts +0 -0
  47. /package/dist/{Charts → core/Charts}/ChartComponent.svelte.d.ts +0 -0
  48. /package/dist/{Charts → core/Charts}/adapt.d.ts +0 -0
  49. /package/dist/{Charts → core/Charts}/adapt.js +0 -0
  50. /package/dist/{Charts → core/Charts}/charts.model.js +0 -0
  51. /package/dist/{Charts → core/Charts}/data-utils.d.ts +0 -0
  52. /package/dist/{Charts → core/Charts}/data-utils.js +0 -0
  53. /package/dist/{Charts → core/Charts}/index.d.ts +0 -0
  54. /package/dist/{Charts → core/Charts}/index.js +0 -0
  55. /package/dist/{Charts → core/Charts}/plotly.d.ts +0 -0
  56. /package/dist/{Desktop → core/Desktop}/Desktop.svelte +0 -0
  57. /package/dist/{Desktop → core/Desktop}/Desktop.svelte.d.ts +0 -0
  58. /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte +0 -0
  59. /package/dist/{Desktop → core/Desktop}/Grid/Half.svelte.d.ts +0 -0
  60. /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte +0 -0
  61. /package/dist/{Desktop → core/Desktop}/Grid/Quarter.svelte.d.ts +0 -0
  62. /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte +0 -0
  63. /package/dist/{Desktop → core/Desktop}/Grid/ResizeHandle.svelte.d.ts +0 -0
  64. /package/dist/{Desktop → core/Desktop}/Grid/index.d.ts +0 -0
  65. /package/dist/{Desktop → core/Desktop}/Grid/index.js +0 -0
  66. /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.d.ts +0 -0
  67. /package/dist/{Desktop → core/Desktop}/Grid/resizeStore.js +0 -0
  68. /package/dist/{Desktop → core/Desktop}/GridRenderer.svelte.d.ts +0 -0
  69. /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte +0 -0
  70. /package/dist/{Desktop → core/Desktop}/GridSelector/ComponentPalette.svelte.d.ts +0 -0
  71. /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte +0 -0
  72. /package/dist/{Desktop → core/Desktop}/GridSelector/ConfigurationPanel.svelte.d.ts +0 -0
  73. /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte +0 -0
  74. /package/dist/{Desktop → core/Desktop}/GridSelector/GridSelector.svelte.d.ts +0 -0
  75. /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte +0 -0
  76. /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPicker.svelte.d.ts +0 -0
  77. /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte +0 -0
  78. /package/dist/{Desktop → core/Desktop}/GridSelector/LayoutPreview.svelte.d.ts +0 -0
  79. /package/dist/{Desktop → core/Desktop}/GridSelector/index.d.ts +0 -0
  80. /package/dist/{Desktop → core/Desktop}/GridSelector/index.js +0 -0
  81. /package/dist/{Desktop → core/Desktop}/GridViewer.svelte +0 -0
  82. /package/dist/{Desktop → core/Desktop}/GridViewer.svelte.d.ts +0 -0
  83. /package/dist/{Desktop → core/Desktop}/gridLayouts.d.ts +0 -0
  84. /package/dist/{Desktop → core/Desktop}/gridLayouts.js +0 -0
  85. /package/dist/{Desktop → core/Desktop}/index.d.ts +0 -0
  86. /package/dist/{Desktop → core/Desktop}/index.js +0 -0
  87. /package/dist/{Desktop → core/Desktop}/launchHelpers.d.ts +0 -0
  88. /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,2 @@
1
+ export declare function checkAndInitDatabase(): Promise<boolean>;
2
+ export declare function loadSampleData(): Promise<boolean>;
@@ -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,3 @@
1
+ import type { Antenna } from '../db';
2
+ export declare function parseMSIFile(file: File): Promise<Antenna>;
3
+ export declare function parseFolder(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean): Promise<Antenna[]>;
@@ -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
+ }