@smartnet360/svelte-components 0.0.105 → 0.0.106
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/README.md +75 -0
- package/dist/core/CellTable/CellTableDemo.svelte +83 -127
- package/dist/core/CellTable/CellTablePanel.svelte +210 -25
- package/dist/core/CellTable/CellTablePanel.svelte.d.ts +15 -1
- package/dist/core/CellTable/index.d.ts +1 -1
- package/dist/core/CellTable/index.js +4 -2
- package/dist/core/CellTable/types.d.ts +5 -46
- package/dist/map-v3/demo/demo-cells.d.ts +14 -10
- package/dist/map-v3/demo/demo-cells.js +21 -244
- package/dist/map-v3/features/cells/types.d.ts +3 -54
- package/dist/map-v3/features/cells/types.js +2 -1
- package/dist/shared/demo/cell-generator.d.ts +73 -0
- package/dist/shared/demo/cell-generator.js +254 -0
- package/dist/shared/demo/cell-types.d.ts +62 -0
- package/dist/shared/demo/cell-types.js +6 -0
- package/dist/shared/demo/index.d.ts +7 -0
- package/dist/shared/demo/index.js +7 -0
- package/package.json +1 -1
- package/dist/core/CellTable/demo-data.d.ts +0 -5
- package/dist/core/CellTable/demo-data.js +0 -501
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell Generator - Configurable Demo Cell Data Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates realistic cell network data with configurable parameters.
|
|
5
|
+
* Supports density zones, multiple technologies, and various site configurations.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Preset configurations
|
|
9
|
+
*/
|
|
10
|
+
export const GENERATOR_PRESETS = {
|
|
11
|
+
/** Small dataset for quick demos - 10 sites, ~300 cells */
|
|
12
|
+
small: {
|
|
13
|
+
numSites: 10,
|
|
14
|
+
centerLat: 47.4979,
|
|
15
|
+
centerLng: 19.0402,
|
|
16
|
+
radiusKm: 3
|
|
17
|
+
},
|
|
18
|
+
/** Medium dataset for testing - 100 sites, ~3000 cells */
|
|
19
|
+
medium: {
|
|
20
|
+
numSites: 100,
|
|
21
|
+
centerLat: 47.4979,
|
|
22
|
+
centerLng: 19.0402,
|
|
23
|
+
radiusKm: 8
|
|
24
|
+
},
|
|
25
|
+
/** Large dataset for performance testing - 500 sites, ~15000 cells */
|
|
26
|
+
large: {
|
|
27
|
+
numSites: 500,
|
|
28
|
+
centerLat: 47.4979,
|
|
29
|
+
centerLng: 19.0402,
|
|
30
|
+
radiusKm: 12
|
|
31
|
+
},
|
|
32
|
+
/** Extra large dataset - 2000 sites, ~60000 cells */
|
|
33
|
+
xlarge: {
|
|
34
|
+
numSites: 2000,
|
|
35
|
+
centerLat: 47.4979,
|
|
36
|
+
centerLng: 19.0402,
|
|
37
|
+
radiusKm: 15
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
// Density zones (distance from center as fraction of radius)
|
|
41
|
+
const DENSITY_ZONES = [
|
|
42
|
+
{ maxRadius: 0.3, minSpacing: 0.0008, maxSpacing: 0.0015, name: 'Very Dense Core' },
|
|
43
|
+
{ maxRadius: 0.5, minSpacing: 0.0015, maxSpacing: 0.003, name: 'Dense Inner' },
|
|
44
|
+
{ maxRadius: 0.7, minSpacing: 0.003, maxSpacing: 0.006, name: 'Medium' },
|
|
45
|
+
{ maxRadius: 0.85, minSpacing: 0.006, maxSpacing: 0.012, name: 'Sparse Suburban' },
|
|
46
|
+
{ maxRadius: 1.0, minSpacing: 0.012, maxSpacing: 0.025, name: 'Very Sparse Rural' }
|
|
47
|
+
];
|
|
48
|
+
// Cell tech-band definitions
|
|
49
|
+
const TECH_BANDS = [
|
|
50
|
+
{ tech: '2G', band: '900', fband: 'GSM900' },
|
|
51
|
+
{ tech: '2G', band: '1800', fband: 'GSM1800' },
|
|
52
|
+
{ tech: '4G', band: '700', fband: 'LTE700' },
|
|
53
|
+
{ tech: '4G', band: '800', fband: 'LTE800' },
|
|
54
|
+
{ tech: '4G', band: '900', fband: 'LTE900' },
|
|
55
|
+
{ tech: '4G', band: '1800', fband: 'LTE1800' },
|
|
56
|
+
{ tech: '4G', band: '2100', fband: 'LTE2100' },
|
|
57
|
+
{ tech: '4G', band: '2600', fband: 'LTE2600' },
|
|
58
|
+
{ tech: '5G', band: '700', fband: '5G-700' },
|
|
59
|
+
{ tech: '5G', band: '2100', fband: '5G-2100' },
|
|
60
|
+
{ tech: '5G', band: '3500', fband: '5G-3500' }
|
|
61
|
+
];
|
|
62
|
+
// Sector configurations
|
|
63
|
+
const SECTORS = [
|
|
64
|
+
{ azimuth: 0, sectorNum: 1 },
|
|
65
|
+
{ azimuth: 120, sectorNum: 2 },
|
|
66
|
+
{ azimuth: 240, sectorNum: 3 }
|
|
67
|
+
];
|
|
68
|
+
// Status distribution
|
|
69
|
+
const STATUSES = [
|
|
70
|
+
'On_Air', 'On_Air', 'On_Air', 'On_Air',
|
|
71
|
+
'On_Air_UNDER_CONSTRUCTION', 'On_Air_Locked',
|
|
72
|
+
'RF_Plan_Ready', 'RF_Plan_Ready',
|
|
73
|
+
'Re-Planned_RF_Plan_Ready', 'Tavlati_RF_Plan_Ready',
|
|
74
|
+
'On_Air', 'On_Air'
|
|
75
|
+
];
|
|
76
|
+
const BEAMWIDTH = 65;
|
|
77
|
+
/**
|
|
78
|
+
* Simple seeded random number generator
|
|
79
|
+
*/
|
|
80
|
+
function createRandom(seed) {
|
|
81
|
+
if (seed === undefined) {
|
|
82
|
+
return Math.random;
|
|
83
|
+
}
|
|
84
|
+
let s = seed;
|
|
85
|
+
return () => {
|
|
86
|
+
s = (s * 1103515245 + 12345) & 0x7fffffff;
|
|
87
|
+
return s / 0x7fffffff;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get density zone for a given normalized radius
|
|
92
|
+
*/
|
|
93
|
+
function getDensityZone(normalizedRadius) {
|
|
94
|
+
for (const zone of DENSITY_ZONES) {
|
|
95
|
+
if (normalizedRadius <= zone.maxRadius) {
|
|
96
|
+
return zone;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return DENSITY_ZONES[DENSITY_ZONES.length - 1];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Generate cell name from site, sector, and band index
|
|
103
|
+
* Format: SSSS S BB (e.g., "1000141")
|
|
104
|
+
*/
|
|
105
|
+
function generateCellName(siteNum, sectorNum, bandIndex) {
|
|
106
|
+
const siteId = String(siteNum + 1000).padStart(4, '0');
|
|
107
|
+
const cellSuffix = String(41 + bandIndex).padStart(2, '0');
|
|
108
|
+
return `${siteId}${sectorNum}${cellSuffix}`;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Generate demo cells with configurable parameters
|
|
112
|
+
*/
|
|
113
|
+
export function generateCells(config) {
|
|
114
|
+
const { numSites, centerLat, centerLng, radiusKm, seed } = config;
|
|
115
|
+
const random = createRandom(seed);
|
|
116
|
+
const radiusDegrees = radiusKm / 111;
|
|
117
|
+
const cells = [];
|
|
118
|
+
const usedPositions = [];
|
|
119
|
+
let cellCounter = 1;
|
|
120
|
+
let actualSiteIndex = 0;
|
|
121
|
+
/**
|
|
122
|
+
* Generate random point within circle
|
|
123
|
+
*/
|
|
124
|
+
function generateRandomPointInCircle() {
|
|
125
|
+
const r = Math.sqrt(random()) * radiusDegrees;
|
|
126
|
+
const theta = random() * 2 * Math.PI;
|
|
127
|
+
return {
|
|
128
|
+
lat: centerLat + r * Math.cos(theta),
|
|
129
|
+
lng: centerLng + r * Math.sin(theta),
|
|
130
|
+
normalizedRadius: r / radiusDegrees
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Check if position is too close to existing sites
|
|
135
|
+
*/
|
|
136
|
+
function isTooClose(lat, lng, minSpacing) {
|
|
137
|
+
for (const pos of usedPositions) {
|
|
138
|
+
const distance = Math.sqrt(Math.pow(lat - pos.lat, 2) + Math.pow(lng - pos.lng, 2));
|
|
139
|
+
const requiredSpacing = (minSpacing + pos.minSpacing) / 2;
|
|
140
|
+
if (distance < requiredSpacing) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Add jitter to coordinate
|
|
148
|
+
*/
|
|
149
|
+
function addJitter(value, maxJitter) {
|
|
150
|
+
return value + (random() - 0.5) * 2 * maxJitter;
|
|
151
|
+
}
|
|
152
|
+
// Generate sites
|
|
153
|
+
for (let attempt = 0; attempt < numSites * 3 && actualSiteIndex < numSites; attempt++) {
|
|
154
|
+
const { lat, lng, normalizedRadius } = generateRandomPointInCircle();
|
|
155
|
+
const zone = getDensityZone(normalizedRadius);
|
|
156
|
+
const minSpacing = zone.minSpacing + random() * (zone.maxSpacing - zone.minSpacing);
|
|
157
|
+
if (isTooClose(lat, lng, minSpacing)) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const jitterAmount = minSpacing * 0.3;
|
|
161
|
+
const siteLat = addJitter(lat, jitterAmount);
|
|
162
|
+
const siteLng = addJitter(lng, jitterAmount);
|
|
163
|
+
usedPositions.push({ lat: siteLat, lng: siteLng, minSpacing });
|
|
164
|
+
const siteNum = actualSiteIndex;
|
|
165
|
+
const siteId = String(siteNum + 1000).padStart(4, '0');
|
|
166
|
+
actualSiteIndex++;
|
|
167
|
+
// 10% chance of 1-2 sectors instead of 3
|
|
168
|
+
const numSectors = random() < 0.1 ? (random() < 0.5 ? 1 : 2) : 3;
|
|
169
|
+
const sectorsToGenerate = SECTORS.slice(0, numSectors);
|
|
170
|
+
for (const sector of sectorsToGenerate) {
|
|
171
|
+
for (let bandIndex = 0; bandIndex < TECH_BANDS.length; bandIndex++) {
|
|
172
|
+
const techBand = TECH_BANDS[bandIndex];
|
|
173
|
+
const cellName = generateCellName(siteNum, sector.sectorNum, bandIndex);
|
|
174
|
+
const status = STATUSES[bandIndex % STATUSES.length];
|
|
175
|
+
cells.push({
|
|
176
|
+
id: cellName,
|
|
177
|
+
txId: cellName,
|
|
178
|
+
cellID: cellName,
|
|
179
|
+
cellID2G: techBand.tech === '2G' ? cellName : '',
|
|
180
|
+
cellName: cellName,
|
|
181
|
+
siteId: siteId,
|
|
182
|
+
tech: techBand.tech,
|
|
183
|
+
fband: techBand.fband,
|
|
184
|
+
frq: techBand.band,
|
|
185
|
+
type: 'MACRO',
|
|
186
|
+
status: status,
|
|
187
|
+
onAirDate: '2024-01-15',
|
|
188
|
+
bcch: techBand.tech === '2G' ? 100 + bandIndex : 0,
|
|
189
|
+
ctrlid: techBand.tech === '2G' ? `CTRL-${cellName}` : '',
|
|
190
|
+
dlEarfn: techBand.tech === '4G' ? 6200 + bandIndex * 100 : 0,
|
|
191
|
+
antenna: 'DEMO-ANTENNA-MODEL',
|
|
192
|
+
azimuth: sector.azimuth,
|
|
193
|
+
height: 30,
|
|
194
|
+
electricalTilt: '3',
|
|
195
|
+
beamwidth: BEAMWIDTH,
|
|
196
|
+
latitude: siteLat,
|
|
197
|
+
longitude: siteLng,
|
|
198
|
+
dx: 0,
|
|
199
|
+
dy: 0,
|
|
200
|
+
siteLatitude: siteLat,
|
|
201
|
+
siteLongitude: siteLng,
|
|
202
|
+
comment: `Demo ${techBand.tech} ${techBand.band} cell at azimuth ${sector.azimuth}°`,
|
|
203
|
+
planner: 'Demo User',
|
|
204
|
+
atollETP: 43.0,
|
|
205
|
+
atollPW: 20.0,
|
|
206
|
+
atollRS: 500.0 + (techBand.band === '700' ? 200 : 0),
|
|
207
|
+
atollBW: parseFloat(techBand.band) / 100,
|
|
208
|
+
cellId3: `${cellName}-3G`,
|
|
209
|
+
nwtP1: 20,
|
|
210
|
+
nwtP2: 40,
|
|
211
|
+
pci1: cellCounter % 504,
|
|
212
|
+
nwtRS: 450.0,
|
|
213
|
+
nwtBW: 10.0,
|
|
214
|
+
other: {
|
|
215
|
+
demoCell: true,
|
|
216
|
+
siteNumber: actualSiteIndex,
|
|
217
|
+
sector: sector.sectorNum,
|
|
218
|
+
techBandKey: `${techBand.tech}_${techBand.band}`,
|
|
219
|
+
radius: normalizedRadius,
|
|
220
|
+
densityZone: zone.name
|
|
221
|
+
},
|
|
222
|
+
customSubgroup: `Sector-${sector.sectorNum}`
|
|
223
|
+
});
|
|
224
|
+
cellCounter++;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return cells;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Generate cells using a preset configuration
|
|
232
|
+
*/
|
|
233
|
+
export function generateCellsFromPreset(preset, seed) {
|
|
234
|
+
const config = { ...GENERATOR_PRESETS[preset], seed };
|
|
235
|
+
return generateCells(config);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get info about generated data
|
|
239
|
+
*/
|
|
240
|
+
export function getGeneratorInfo(cells) {
|
|
241
|
+
const siteIds = new Set(cells.map(c => c.siteId));
|
|
242
|
+
const techBreakdown = {};
|
|
243
|
+
const statusBreakdown = {};
|
|
244
|
+
for (const cell of cells) {
|
|
245
|
+
techBreakdown[cell.tech] = (techBreakdown[cell.tech] || 0) + 1;
|
|
246
|
+
statusBreakdown[cell.status] = (statusBreakdown[cell.status] || 0) + 1;
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
totalCells: cells.length,
|
|
250
|
+
totalSites: siteIds.size,
|
|
251
|
+
techBreakdown,
|
|
252
|
+
statusBreakdown
|
|
253
|
+
};
|
|
254
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Demo Data - Cell Types
|
|
3
|
+
*
|
|
4
|
+
* Core cell interface used across all components
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Cell data model - represents a radio cell/sector
|
|
8
|
+
*/
|
|
9
|
+
export interface Cell {
|
|
10
|
+
id: string;
|
|
11
|
+
txId: string;
|
|
12
|
+
cellID: string;
|
|
13
|
+
cellID2G: string;
|
|
14
|
+
cellName: string;
|
|
15
|
+
siteId: string;
|
|
16
|
+
tech: string;
|
|
17
|
+
fband: string;
|
|
18
|
+
frq: string;
|
|
19
|
+
type: string;
|
|
20
|
+
status: string;
|
|
21
|
+
onAirDate: string;
|
|
22
|
+
bcch: number;
|
|
23
|
+
ctrlid: string;
|
|
24
|
+
dlEarfn: number;
|
|
25
|
+
antenna: string;
|
|
26
|
+
azimuth: number;
|
|
27
|
+
height: number;
|
|
28
|
+
electricalTilt: string;
|
|
29
|
+
beamwidth: number;
|
|
30
|
+
latitude: number;
|
|
31
|
+
longitude: number;
|
|
32
|
+
dx: number;
|
|
33
|
+
dy: number;
|
|
34
|
+
siteLatitude: number;
|
|
35
|
+
siteLongitude: number;
|
|
36
|
+
comment: string;
|
|
37
|
+
planner: string;
|
|
38
|
+
atollETP: number;
|
|
39
|
+
atollPW: number;
|
|
40
|
+
atollRS: number;
|
|
41
|
+
atollBW: number;
|
|
42
|
+
cellId3: string;
|
|
43
|
+
nwtP1: number;
|
|
44
|
+
nwtP2: number;
|
|
45
|
+
pci1: number;
|
|
46
|
+
nwtRS: number;
|
|
47
|
+
nwtBW: number;
|
|
48
|
+
other?: Record<string, unknown>;
|
|
49
|
+
customSubgroup: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Supported cell sector status values
|
|
53
|
+
*/
|
|
54
|
+
export type CellStatus = 'On_Air' | 'On_Air_UNDER_CONSTRUCTION' | 'On_Air_Locked' | 'RF_Plan_Ready' | 'Re-Planned_RF_Plan_Ready' | 'Tavlati_RF_Plan_Ready';
|
|
55
|
+
/**
|
|
56
|
+
* Technology-Band combination key
|
|
57
|
+
*/
|
|
58
|
+
export type TechnologyBandKey = string;
|
|
59
|
+
/**
|
|
60
|
+
* Grouping fields for views
|
|
61
|
+
*/
|
|
62
|
+
export type CellGroupingField = 'tech' | 'fband' | 'frq' | 'status' | 'siteId' | 'none';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Demo Data Module
|
|
3
|
+
*
|
|
4
|
+
* Provides common demo data generators and types for use across components.
|
|
5
|
+
*/
|
|
6
|
+
export type { Cell, CellStatus, CellGroupingField } from './cell-types';
|
|
7
|
+
export { generateCells, generateCellsFromPreset, getGeneratorInfo, GENERATOR_PRESETS, type CellGeneratorConfig, type GeneratorPreset } from './cell-generator';
|
package/package.json
CHANGED