EARFCN par operateur. * * Cette version inclut la gestion des plages EARFCN par bande pour chaque * operateur, car les operateurs utilisent des valeurs differentes meme * sur la meme bande (ex: POST B20 != Tango B20). */ declare(strict_types=1); require_once __DIR__ . '/admin_auth.php'; require_once __DIR__ . '/../lib/db.php'; require_once __DIR__ . '/_nav.php'; require_admin_auth(); $db = pdo(); $user = current_admin_username(); $successMessage = null; $errorMessage = null; function nullable_float(array $input, string $key): ?float { $value = trim((string)($input[$key] ?? '')); if ($value === '') { return null; } if (!is_numeric($value)) { throw new InvalidArgumentException("$key doit etre un nombre."); } return (float)$value; } if ($_SERVER['REQUEST_METHOD'] === 'POST') { require_csrf_token(); $action = $_POST['action'] ?? ''; try { if ($action === 'save_country') { $countryId = (int)($_POST['country_id'] ?? 0); $mcc = trim((string)($_POST['mcc'] ?? '')); $iso2 = strtoupper(trim((string)($_POST['iso2'] ?? ''))); $name = trim((string)($_POST['name'] ?? '')); $lat = nullable_float($_POST, 'default_lat'); $lon = nullable_float($_POST, 'default_lon'); $zoomRaw = trim((string)($_POST['default_zoom'] ?? '')); $zoom = $zoomRaw === '' ? 9 : (int)$zoomRaw; $wmsUrl = trim((string)($_POST['cadastre_wms_url'] ?? '')) ?: null; $wmsLayer = trim((string)($_POST['cadastre_wms_layer'] ?? '')) ?: null; $attribution = trim((string)($_POST['cadastre_attribution_html'] ?? '')) ?: null; $srs = trim((string)($_POST['cadastre_srs'] ?? '')) ?: 'EPSG:3857'; if (!preg_match('/^\d{3}$/', $mcc)) { throw new InvalidArgumentException('MCC : 3 chiffres obligatoires.'); } if (!preg_match('/^[A-Z]{2}$/', $iso2)) { throw new InvalidArgumentException('ISO2 : 2 lettres obligatoires.'); } if ($name === '') { throw new InvalidArgumentException('Le nom du pays est obligatoire.'); } if (($lat === null) !== ($lon === null)) { throw new InvalidArgumentException('Saisissez latitude et longitude ensemble, ou laissez les deux champs vides.'); } if ($lat !== null && ($lat < -90 || $lat > 90)) { throw new InvalidArgumentException('La latitude doit etre comprise entre -90 et 90.'); } if ($lon !== null && ($lon < -180 || $lon > 180)) { throw new InvalidArgumentException('La longitude doit etre comprise entre -180 et 180.'); } if ($zoom < 1 || $zoom > 19) { throw new InvalidArgumentException('Le zoom doit etre compris entre 1 et 19.'); } if ($countryId > 0) { $stmt = $db->prepare( 'UPDATE countries SET mcc = ?, iso2 = ?, name = ?, default_lat = ?, default_lon = ?, default_zoom = ?, cadastre_wms_url = ?, cadastre_wms_layer = ?, cadastre_attribution_html = ?, cadastre_srs = ? WHERE id = ?' ); $stmt->execute([$mcc, $iso2, $name, $lat, $lon, $zoom, $wmsUrl, $wmsLayer, $attribution, $srs, $countryId]); if ($stmt->rowCount() === 0) { $exists = $db->prepare('SELECT 1 FROM countries WHERE id = ?'); $exists->execute([$countryId]); if (!$exists->fetchColumn()) { throw new RuntimeException('Pays introuvable.'); } } $successMessage = "Pays \"$name\" mis a jour."; } else { $stmt = $db->prepare( 'INSERT INTO countries (mcc, iso2, name, default_lat, default_lon, default_zoom, cadastre_wms_url, cadastre_wms_layer, cadastre_attribution_html, cadastre_srs) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' ); $stmt->execute([$mcc, $iso2, $name, $lat, $lon, $zoom, $wmsUrl, $wmsLayer, $attribution, $srs]); $successMessage = "Pays \"$name\" cree."; } } elseif ($action === 'delete_country') { $countryId = (int)($_POST['country_id'] ?? 0); if ($countryId <= 0) { throw new InvalidArgumentException('Pays invalide.'); } $check = $db->prepare('SELECT COUNT(*) FROM operators WHERE country_id = ?'); $check->execute([$countryId]); if ((int)$check->fetchColumn() > 0) { throw new RuntimeException('Des operateurs sont rattaches a ce pays : supprimez-les ou reaffectez-les d\'abord.'); } $db->prepare('DELETE FROM countries WHERE id = ?')->execute([$countryId]); $successMessage = 'Pays supprime.'; } elseif ($action === 'save_operator') { $operatorId = (int)($_POST['operator_id'] ?? 0); $countryId = (int)($_POST['country_id'] ?? 0); $mnc = str_pad(trim((string)($_POST['mnc'] ?? '')), 2, '0', STR_PAD_LEFT); $brand = trim((string)($_POST['brand'] ?? '')); $fullName = trim((string)($_POST['full_name'] ?? '')) ?: null; if ($countryId <= 0 || !preg_match('/^\d{2,3}$/', $mnc) || $brand === '') { throw new InvalidArgumentException('Pays, MNC (2 ou 3 chiffres) et nom commercial sont obligatoires.'); } $mccStmt = $db->prepare('SELECT mcc FROM countries WHERE id = ?'); $mccStmt->execute([$countryId]); $mcc = $mccStmt->fetchColumn(); if ($mcc === false) { throw new InvalidArgumentException('Pays invalide.'); } if ($operatorId > 0) { $stmt = $db->prepare('UPDATE operators SET country_id = ?, mcc = ?, mnc = ?, brand = ?, full_name = ? WHERE id = ?'); $stmt->execute([$countryId, $mcc, $mnc, $brand, $fullName, $operatorId]); $successMessage = "Operateur \"$brand\" mis a jour."; } else { $stmt = $db->prepare('INSERT INTO operators (country_id, mcc, mnc, brand, full_name) VALUES (?, ?, ?, ?, ?)'); $stmt->execute([$countryId, $mcc, $mnc, $brand, $fullName]); $operatorId = (int)$db->lastInsertId(); $successMessage = "Operateur \"$brand\" ajoute."; } } elseif ($action === 'delete_operator') { $operatorId = (int)($_POST['operator_id'] ?? 0); if ($operatorId <= 0) { throw new InvalidArgumentException('Operateur invalide.'); } $check = $db->prepare('SELECT COUNT(*) FROM sites WHERE operator_id = ?'); $check->execute([$operatorId]); if ((int)$check->fetchColumn() > 0) { throw new RuntimeException('Des sites valides existent pour cet operateur.'); } $db->prepare('DELETE FROM operators WHERE id = ?')->execute([$operatorId]); $successMessage = 'Operateur supprime.'; } elseif ($action === 'save_band_mapping') { $operatorId = (int)($_POST['operator_id'] ?? 0); $band = trim((string)($_POST['band'] ?? '')); $earfcnStart = (int)($_POST['earfcn_start'] ?? 0); $earfcnEnd = (int)($_POST['earfcn_end'] ?? 0); if ($operatorId <= 0 || !preg_match('/^B\d+[a-z]?$/i', $band)) { throw new InvalidArgumentException('Operateur et bande (ex: B20, B3, B7) obligatoires.'); } if ($earfcnStart <= 0 || $earfcnEnd < $earfcnStart) { throw new InvalidArgumentException('Plage EARFCN invalide (start > 0, end >= start).'); } $stmt = $db->prepare( 'INSERT INTO operator_band_mappings (operator_id, band, earfcn_start, earfcn_end) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE earfcn_start = VALUES(earfcn_start), earfcn_end = VALUES(earfcn_end)' ); $stmt->execute([$operatorId, strtoupper($band), $earfcnStart, $earfcnEnd]); $successMessage = "Mapping bande $band enregistre pour l'operateur."; } elseif ($action === 'delete_band_mapping') { $mappingId = (int)($_POST['mapping_id'] ?? 0); if ($mappingId <= 0) { throw new InvalidArgumentException('Mapping invalide.'); } $db->prepare('DELETE FROM operator_band_mappings WHERE id = ?')->execute([$mappingId]); $successMessage = 'Mapping supprime.'; } } catch (InvalidArgumentException|RuntimeException $e) { $errorMessage = $e->getMessage(); } catch (PDOException $e) { error_log('[countries_settings] PDOException: ' . $e->getMessage()); $errorMessage = str_contains($e->getMessage(), 'Duplicate') ? 'Ce couple MCC/MNC ou ce mapping bande existe deja.' : 'Une erreur est survenue. Consultez les journaux du serveur si le probleme persiste.'; } } $countries = $db->query('SELECT * FROM countries ORDER BY name')->fetchAll(PDO::FETCH_ASSOC); $operators = $db->query( 'SELECT o.*, c.name AS country_name FROM operators o JOIN countries c ON c.id = o.country_id ORDER BY c.name, o.brand' )->fetchAll(PDO::FETCH_ASSOC); $operatorsByCountry = []; foreach ($operators as $op) { $operatorsByCountry[$op['country_id']][] = $op; } // Charger les mappings bande/EARFCN pour chaque operateur $bandMappingsByOperator = []; $mappings = $db->query('SELECT * FROM operator_band_mappings ORDER BY operator_id, band')->fetchAll(PDO::FETCH_ASSOC); foreach ($mappings as $m) { $bandMappingsByOperator[$m['operator_id']][] = $m; } $editingCountry = null; $editCountryId = filter_input(INPUT_GET, 'edit_country', FILTER_VALIDATE_INT); if ($editCountryId) { foreach ($countries as $country) { if ((int)$country['id'] === $editCountryId) { $editingCountry = $country; break; } } } $editingOperator = null; $editOperatorId = filter_input(INPUT_GET, 'edit_operator', FILTER_VALIDATE_INT); if ($editOperatorId) { foreach ($operators as $operator) { if ((int)$operator['id'] === $editOperatorId) { $editingOperator = $operator; break; } } } function field_value(?array $source, string $key, string $fallback = ''): string { return htmlspecialchars((string)($source[$key] ?? $fallback), ENT_QUOTES, 'UTF-8'); } $countryFormTitle = $editingCountry ? 'Modifier le pays : ' . field_value($editingCountry, 'name') : 'Ajouter un pays'; $operatorFormTitle = $editingOperator ? 'Modifier l\'operateur : ' . field_value($editingOperator, 'brand') : 'Ajouter un operateur'; ?> NetMonster Admin - Pays & operateurs

Configuration des pays & operateurs

Configurez les pays, le centre de carte, les couches cadastre, les operateurs et les mappings bande/EARFCN.

Centre de la carte

Cadastre / WMS (optionnel)

Ou trouver une URL WMS ? Cherchez le portail geographique national et son service WMS public, generalement documente par une requete GetCapabilities. Laissez les champs WMS vides si aucune source ouverte n'est disponible : la carte restera utilisable avec OpenStreetMap.
Annuler l'edition

Pays configures ()

PaysMCCISO2Centre carteCadastreOperateursActions
configure non configure Modifier
Aucun pays configure.

Annuler l'edition

Operateurs configures ()

PaysBrandMCC-MNCRaison socialeActions
- Modifier
Aucun operateur configure.

Mappings bande 4G → EARFCN par operateur

Configurez les plages EARFCN utilisees par chaque operateur sur chaque bande 4G. Ces valeurs sont utilisees lors de l'import CellMapper pour deduire l'EARFCN a partir de la bande selectionnee.

(MCC / MNC )

BandeEARFCN debutEARFCN finActions

Aucun mapping configure pour cet operateur.

Aucun operateur configure. Ajoutez d'abord un operateur ci-dessus.