simple-admin/simpleadmin/www/index.html
sky 200a08e448 更新 simpleadmin/www/index.html
- Improved operator name detection logic with PLMN code priority
- Added fallback mechanism for hexadecimal encoded operator names
2024-12-07 20:14:28 +08:00

1978 lines
80 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en" data-bs-theme="light">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>模块管理</title>
<!-- <link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/> -->
<!-- Import all the bootstrap css files from css folder -->
<link rel="stylesheet" href="css/styles.css" />
<link rel="stylesheet" href="css/bootstrap.min.css" />
<!-- Logo -->
<link rel="simpleadmin-logo" href="favicon.ico" />
<!-- Import BootStrap Javascript -->
<script src="js/bootstrap.bundle.min.js"></script>
<script src="js/alpinejs.min.js" defer></script>
</head>
<body>
<main>
<div class="container my-4" x-data="processAllInfos()">
<nav class="navbar navbar-expand-lg mt-2">
<div class="container-fluid">
<a class="navbar-brand" href="/"
><span class="mb-0 h4 fw-bold">模块管理</span></a
>
<button
class="navbar-toggler"
type="button"
data-bs-toggle="collapse"
data-bs-target="#navbarText"
aria-controls="navbarText"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/">首页</a>
</li>
<li class="nav-item">
<a class="nav-link" href="network.html">网络</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/scanner.html">扫描</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings.html">设置</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/sms.html">短信</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/console">控制台</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/deviceinfo.html"
>设备信息</a
>
</li>
</ul>
<span class="navbar-text">
<button class="btn btn-link text-reset" id="darkModeToggle">
暗黑模式
</button>
</span>
</div>
</div>
</nav>
<div class="row align-items-center mt-5 gap-md-3 gap-2">
<div class="col align-self-center">
<div class="card">
<div
class="card-body d-flex flex-column align-items-center justify-content-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512"
height="85"
width="85"
>
<path
fill="#fdb53c"
d="M160 64c-26.5 0-48 21.5-48 48V276.5c0 17.3-7.1 31.9-15.3 42.5C86.2 332.6 80 349.5 80 368c0 44.2 35.8 80 80 80s80-35.8 80-80c0-18.5-6.2-35.4-16.7-48.9c-8.2-10.6-15.3-25.2-15.3-42.5V112c0-26.5-21.5-48-48-48zM48 112C48 50.2 98.1 0 160 0s112 50.1 112 112V276.5c0 .1 .1 .3 .2 .6c.2 .6 .8 1.6 1.7 2.8c18.9 24.4 30.1 55 30.1 88.1c0 79.5-64.5 144-144 144S16 447.5 16 368c0-33.2 11.2-63.8 30.1-88.1c.9-1.2 1.5-2.2 1.7-2.8c.1-.3 .2-.5 .2-.6V112zM208 368c0 26.5-21.5 48-48 48s-48-21.5-48-48c0-20.9 13.4-38.7 32-45.3V272c0-8.8 7.2-16 16-16s16 7.2 16 16v50.7c18.6 6.6 32 24.4 32 45.3z"
/>
</svg>
<h1
class="card-text mt-4"
x-text="temperature + ' &deg;C'"
></h1>
</div>
<div class="card-footer"
style="text-align:center;">
模块温度
</div>
</div>
</div>
<div class="col align-self-center">
<div class="card">
<div
class="card-body d-flex flex-column align-items-center justify-content-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512"
height="85"
width="85"
>
<path
fill="#6a46d8"
d="M64 0H242.7c17 0 33.3 6.7 45.3 18.7L365.3 96c12 12 18.7 28.3 18.7 45.3V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64C0 28.7 28.7 0 64 0zM96 192c-17.7 0-32 14.3-32 32v32h64V192H96zM64 352h80 96 80V288H240 144 64v64zM320 224c0-17.7-14.3-32-32-32H256v64h64V224zM160 192v64h64V192H160zM288 448c17.7 0 32-14.3 32-32V384H256v64h32zM160 384v64h64V384H160zM64 416c0 17.7 14.3 32 32 32h32V384H64v32z"
/>
</svg>
<h1 class="card-text mt-4" x-text="simStatus"></h1>
</div>
<div class="card-footer"
style="text-align:center;">
SIM卡状态
</div>
</div>
</div>
<div class="col align-self-center">
<div class="card">
<div
class="card-body d-flex flex-column align-items-center justify-content-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
height="85"
width="85"
>
<path
fill="#2fa7fb"
d="M576 0c17.7 0 32 14.3 32 32V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V32c0-17.7 14.3-32 32-32zM448 96c17.7 0 32 14.3 32 32V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V128c0-17.7 14.3-32 32-32zM352 224V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V224c0-17.7 14.3-32 32-32s32 14.3 32 32zM192 288c17.7 0 32 14.3 32 32V480c0 17.7-14.3 32-32 32s-32-14.3-32-32V320c0-17.7 14.3-32 32-32zM96 416v64c0 17.7-14.3 32-32 32s-32-14.3-32-32V416c0-17.7 14.3-32 32-32s32 14.3 32 32z"
/>
</svg>
<h1 class="card-text mt-4" x-text="signalPercentage + '%'"></h1>
</div>
<div class="card-footer"
style="text-align:center;">
信号强度
</div>
</div>
</div>
<div class="col align-self-center">
<div class="card">
<div
class="card-body d-flex flex-column align-items-center justify-content-center"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512"
height="85"
width="85"
>
<path
fill="#20c0ae"
d="M0 336c0 79.5 64.5 144 144 144H512c70.7 0 128-57.3 128-128c0-61.9-44-113.6-102.4-125.4c4.1-10.7 6.4-22.4 6.4-34.6c0-53-43-96-96-96c-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32C167.6 32 96 103.6 96 192c0 2.7 .1 5.4 .2 8.1C40.2 219.8 0 273.2 0 336z"
/>
</svg>
<h1
class="card-text mt-4"
x-text="internetConnectionStatus"
></h1>
</div>
<div class="card-footer"
style="text-align:center;">
网络状态
</div>
</div>
</div>
</div>
<div class="row mt-5 gap-3">
<div class="col">
<div class="card">
<div class="card-header">网络信息</div>
<div class="card-body">
<div class="card-text table-responsive-sm">
<table class="table">
<style>.table th,.table td { white-space: nowrap; /* 防止换行 */}</style>
<tbody>
<tr>
<th scope="row">SIM卡</th>
<td x-text="activeSim"></td>
</tr>
<tr>
<th scope="row">运营商</th>
<td x-text="networkProvider"></td>
</tr>
<tr>
<th scope="row">运营商代码</th>
<td x-text="mccmnc"></td>
</tr>
<tr>
<th scope="row">接入点APN</th>
<td x-text="apn"></td>
</tr>
<tr>
<th scope="row">网络模式</th>
<td x-text="networkMode"></td>
</tr>
<tr>
<th scope="row">Bands</th>
<td x-text="bands"></td>
</tr>
<tr>
<th scope="row">带宽</th>
<td x-text="bandwidth"></td>
</tr>
<tr>
<th scope="row">频点</th>
<td x-text="earfcns"></td>
</tr>
<tr>
<th scope="row">PCI</th>
<td
x-show="sccPCI != '-'"
x-text="pccPCI + ', ' + sccPCI"
></td>
<td x-show="sccPCI == '-'" x-text="pccPCI"></td>
</tr>
<tr>
<th scope="row">IPv<sup>4</sup></th>
<td x-text="ipv4"></td>
</tr>
<tr>
<th scope="row">IPv<sup>6</sup></th>
<td x-text="ipv6"></td>
</tr>
<tr>
<th scope="row">在线时长</th>
<td x-text="uptime"></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<div class="row row-cols-2">
<div class="col">
<p class="btn">刷新率(>3s)</p>
</div>
<div class="col">
<div class="input-group">
<input
x-model="newRefreshRate"
class="form-control"
type="number"
min="3"
max="60"
x-bind:placeholder="refreshRate + 's'"
/>
<button
@click="updateRefreshRate()"
class="btn btn-outline-secondary"
type="button"
>
设置
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col">
<div class="card">
<div class="card-header">信号</div>
<div class="card-body">
<div class="card-text table-responsive-sm">
<table class="table">
<tbody>
<tr>
<th scope="row">评估</th>
<td x-text="signalAssessment"></td>
</tr>
<tr>
<th scope="row">流量统计</th>
<td
x-text="downloadStat + ' 上传 / ' + uploadStat + ' 下载'"
></td>
</tr>
<tr x-show="csq != 'NR-SA Mode'">
<th scope="row">CSQ</th>
<td x-show="csq != '-'" x-text="csq"></td>
<td x-show="csq == '-'" class="fst-italic"></td>
</tr>
<tr>
<th scope="row">小区ID</th>
<td x-text="cellID"></td>
</tr>
<tr>
<th scope="row">eNB ID</th>
<td x-text="eNBID"></td>
</tr>
<tr>
<th scope="row">LAC ID</th>
<td x-text="tac"></td>
</tr>
<tr x-show="rsrqLTE != '-'">
<th scope="row">SS_RSRQ<sup>4G</sup></th>
<td
class="gap-4 align-items-center"
x-data="{ getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.rsrqLTEPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
} }"
>
<div
x-show="rsrqLTE != '-' && rsrqLTEPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="RSRQ BAR"
:aria-valuenow="rsrqLTEPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + rsrqLTEPercentage + '%'"
>
<span
x-text="rsrqLTE + ' / ' + rsrqLTEPercentage + '%'"
></span>
</div>
</div>
<span
x-show="rsrqLTEPercentage == '0'"
x-text="rsrqLTE"
></span>
<span x-show="rsrqLTE == '-'" class="fst-italic"></span>
</td>
</tr>
<tr x-show="rsrqNR != '-'">
<th scope="row">SS_RSRQ<sup>5G</sup></th>
<td
x-data="{ getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.rsrqNRPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
} }"
>
<div
x-show="rsrqNR != '-' && rsrqNRPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="RSRQ BAR"
:aria-valuenow="rsrqNRPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + rsrqNRPercentage + '%'"
>
<span
x-text="rsrqNR + ' / ' + rsrqNRPercentage + '%'"
></span>
</div>
</div>
<span
x-show="rsrqNRPercentage == '0'"
x-text="rsrqNR"
></span>
<span x-show="rsrqNR == '-'" class="fst-italic"></span>
</td>
</tr>
<tr x-show="rsrpLTE != '-'">
<th scope="row">RSRP<sup>4G</sup></th>
<td
class="gap-4 align-items-center"
x-data="{
getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.rsrpLTEPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
}
}"
>
<div
x-show="rsrpLTE != '-' && rsrpLTEPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="RSRP BAR"
:aria-valuenow="rsrpLTEPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + rsrpLTEPercentage + '%'"
>
<span
x-text="rsrpLTE + ' / ' + rsrpLTEPercentage + '%'"
></span>
</div>
</div>
<span
x-show="rsrpLTEPercentage == '0'"
x-text="rsrpLTE"
></span>
<span x-show="rsrpLTE == '-'" class="fst-italic"></span>
</td>
</tr>
<tr x-show="rsrpNR != '-'">
<th scope="row">SS_RSRP<sup>5G</sup></th>
<td
class="gap-4 align-items-center"
x-data="{
getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.rsrpNRPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
}
}"
>
<div
x-show="rsrpNR != '-' && rsrpNRPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="RSRP BAR"
:aria-valuenow="rsrpNRPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + rsrpNRPercentage + '%'"
>
<span
x-text="rsrpNR + ' / ' + rsrpNRPercentage + '%'"
></span>
</div>
</div>
<span
x-show="rsrpNRPercentage == '0'"
x-text="rsrpNR"
></span>
<span x-show="rsrpNR == '-'" class="fst-italic"></span>
</td>
</tr>
<tr x-show="sinrLTE != '-'">
<th scope="row">SINR<sup>4G</sup></th>
<td
class="gap-4 align-items-center"
x-data="{
getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.sinrLTEPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
}
}"
>
<div
x-show="sinrLTE != '-' && sinrLTEPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="SINR BAR"
:aria-valuenow="sinrLTEPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + sinrLTEPercentage +'%'"
>
<span
x-text="sinrLTE + ' / ' + sinrLTEPercentage +'%'"
></span>
</div>
</div>
<span
x-text="sinrLTE"
x-show="sinrLTEPercentage == '0'"
></span>
<span x-show="sinrLTE == '-'" class="fst-italic"></span>
</td>
</tr>
<tr x-show="sinrNR != '-'">
<th scope="row">SINR<sup>5G</sup></th>
<td
class="gap-4 align-items-center"
x-data="{
getProgressBarClass: function() {
// Remove the percentage sign and convert to integer
var percentage = parseInt(this.sinrNRPercentage);
if (percentage >= 60) {
return 'progress-bar bg-success is-medium';
} else if (percentage >= 40) {
return 'progress-bar bg-warning is-warning is-medium';
} else {
return 'progress-bar bg-danger is-medium';
}
}
}"
>
<div
x-show="sinrNR != '-' && sinrNRPercentage != '0'"
class="progress w-100"
role="progressbar"
aria-label="SINR BAR"
:aria-valuenow="sinrNRPercentage"
aria-valuemin="0"
aria-valuemax="100"
style="height: 18px"
>
<div
:class="getProgressBarClass()"
:style="'width: ' + sinrNRPercentage +'%'"
>
<span
x-text="sinrNR + ' / ' + sinrNRPercentage +'%'"
></span>
</div>
</div>
<span
x-text="sinrNR"
x-show="sinrNRPercentage == '0'"
></span>
<span x-show="sinrNR == '-'" class="fst-italic"></span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<div class="card-text">
<div class="row">
<div class="col">
<div>
<p>更新时间</p>
</div>
</div>
<div class="col">
<p x-text="lastUpdate"></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<script src="js/dark-mode.js"></script>
<script>
function processAllInfos() {
return {
atcmd: "",
internetConnectionStatus: "已断开",
temperature: "0",
simStatus: "无SIM卡",
activeSim: "无SIM卡",
networkProvider: "未知",
mccmnc: "00000",
apn: "未知",
networkMode: "已断开",
bands: "未知频段",
bandwidth: "未知带宽",
earfcns: "000",
pccPCI: "0",
sccPCI: "-",
ipv4: "000.000.000.000",
ipv6: "0000:0000:0000:0000:0000:0000:0000:0000",
cellID: "未知",
eNBID: "未知",
tac: "未知",
csq: "-",
rsrpLTE: "-",
rsrpNR: "-",
rsrpLTEPercentage: "0%",
rsrpNRPercentage: "0%",
rsrqLTE: "-",
rsrqNR: "-",
rsrqLTEPercentage: "0%",
rsrqNRPercentage: "0%",
sinrLTE: "-",
sinrNR: "-",
sinrLTEPercentage: "0%",
sinrNRPercentage: "0%",
signalPercentage: "0",
signalAssessment: "未知",
uptime: "未知时间",
lastUpdate: new Date().toLocaleString(),
newRefreshRate: null,
refreshRate: 3,
nrDownload: "0",
nrUpload: "0",
nonNrDownload: "0",
nonNrUpload: "0",
downloadStat: "0",
uploadStat: "0",
fetchAllInfo() {
this.atcmd =
'AT+QTEMP;+QUIMSLOT?;+QSPN;+CGCONTRDP=1;+QMAP="WWANIP";+QENG="servingcell";+QCAINFO;+QSIMSTAT?;+CSQ;+QGDNRCNT?;+QGDCNT?';
// 定义运营商映射表 | Define operator mapping table
const operatorMap = {
// 中国大陆 (460) | Mainland China (460)
"46000": "中国移动 (CMCC)",
"46001": "中国联通 (CU)",
"46002": "中国移动 (CMCC)",
"46003": "中国电信 (CT)",
"46004": "中国移动 (CMCC)",
"46005": "中国电信 (CT)",
"46006": "中国联通 (CU)",
"46007": "中国移动 (CMCC)",
"46008": "中国移动 (CMCC)",
"46009": "中国联通 (CU)",
"46011": "中国电信 (CT)",
// 中国香港 (454) | Hong Kong, China (454)
"45400": "香港移动通讯 (CSL)",
"45401": "香港移动通讯 (CSL)",
"45402": "香港移动通讯 (CSL)",
"45403": "和记电讯 (3 HK)",
"45404": "和记电讯 (3 HK)",
"45405": "和记电讯 (3 HK)",
"45406": "数码通 (SmarTone)",
"45412": "中国移动香港 (CMHK)",
"45413": "中国移动香港 (CMHK)",
"45415": "数码通 (SmarTone)",
"45416": "香港电讯 (PCCW)",
"45417": "数码通 (SmarTone)",
"45419": "香港电讯 (PCCW)",
"45420": "香港电讯 (PCCW)",
// 中国澳门 (455) | Macau, China (455)
"45501": "澳门电讯 (CTM)",
"45502": "中国电信澳门 (CT Macau)",
"45503": "和记澳门 (3 Macau)",
"45505": "和记澳门 (3 Macau)",
"45506": "数码通澳门 (SmarTone Macau)",
// 美国 (310-316) | United States (310-316)
"310030": "AT&T Mobility",
"310070": "AT&T Mobility",
"310150": "AT&T Mobility",
"310170": "AT&T Mobility",
"310280": "AT&T Mobility",
"310410": "AT&T Mobility",
"310980": "AT&T Mobility",
"311180": "AT&T Mobility",
"310120": "Sprint",
"310260": "T-Mobile USA",
"310160": "T-Mobile USA",
"311480": "Verizon Wireless",
"310004": "Verizon Wireless",
"310012": "Verizon Wireless",
// 加拿大 (302) | Canada (302)
"302220": "Telus Mobility",
"302221": "Telus Mobility",
"302222": "Telus Mobility",
"302270": "EastLink",
"302320": "Rogers Wireless",
"302370": "Rogers Wireless",
"302720": "Rogers Wireless",
"302490": "Freedom Mobile",
"302500": "Videotron",
"302610": "Bell Mobility",
"302640": "Bell Mobility",
"302690": "Bell Mobility",
// 西班牙 (214) | Spain (214)
"21401": "Vodafone España",
"21403": "Orange España",
"21404": "Yoigo",
"21405": "Movistar España",
"21406": "Vodafone España",
"21407": "Movistar España",
"21408": "Euskaltel",
"21409": "Orange España",
// 日本 (440-441) | Japan (440-441)
"44000": "Y!mobile",
"44001": "NTT DOCOMO",
"44002": "NTT DOCOMO",
"44003": "NTT DOCOMO",
"44004": "SoftBank",
"44006": "SoftBank",
"44007": "KDDI (au)",
"44008": "KDDI (au)",
"44009": "NTT DOCOMO",
"44010": "NTT DOCOMO",
"44020": "SoftBank",
"44021": "NTT DOCOMO",
"44051": "KDDI (au)",
"44052": "KDDI (au)",
"44053": "KDDI (au)",
"44054": "KDDI (au)",
"44070": "KDDI (au)",
"44071": "KDDI (au)",
"44072": "KDDI (au)",
"44073": "KDDI (au)",
"44074": "KDDI (au)",
"44075": "KDDI (au)",
"44076": "KDDI (au)",
// 澳大利亚 (505) | Australia (505)
"50501": "Telstra",
"50502": "Optus",
"50503": "Vodafone Australia",
"50506": "Three Australia",
"50507": "Vodafone Australia",
"50508": "One.Tel",
"50512": "Three Australia",
"50571": "Telstra",
"50572": "Telstra",
"50590": "Optus"
};
fetch(
"/cgi-bin/get_atcommand?" +
new URLSearchParams({
atcmd: this.atcmd,
})
).then((response) => {
response.text().then((data) => {
// content here
if (data.includes("ERROR")) {
console.log("Error fetching data");
this.activeSim = "出现错误";
return;
} else {
const rawdata = data;
const lines = rawdata.split("\n");
console.log(lines);
// --- Temperature ---
// find this example value from lines "+QTEMP:"cpuss-0-usr","50"
try {
this.temperature = lines
.find((line) => line.includes('+QTEMP:"cpuss-0-usr"'))
.split(",")[1]
.replace(/"/g, "");
} catch (error) {
this.temperature = lines
.find((line) => line.includes('+QTEMP:"cpu0-a7-usr"'))
.split(",")[1]
.replace(/"/g, "");
}
// --- SIM Status ---
// find this example value from lines "+QSIMSTAT: 0,1"
// Only get the last digit of 0,1
const sim_status = lines
.find((line) => line.includes("+QSIMSTAT:"))
.split(" ")[1]
.replace(/"/g, "")
.split(",")[1];
if (sim_status == 1) {
this.simStatus = "已激活";
} else if (sim_status == 0) {
this.simStatus = "无SIM卡";
}
// --- Active SIM ---
// find this example value from lines "+QUIMSLOT: 1"
const current_sim = lines
.find((line) => line.includes("+QUIMSLOT:"))
.split(" ")[1]
.replace(/"/g, "");
if (current_sim == 1) {
this.activeSim = "SIM 1";
} else if (current_sim == 2) {
this.activeSim = "SIM 2";
} else {
this.activeSim = "无SIM卡";
}
// --- Network Provider ---
// 获取 PLMN 码 | Get PLMN code
const plmn = lines
.find((line) => line.includes("+QSPN:"))
.split(",")[4]
.replace(/"/g, "");
// 获取原始运营商名称 | Get original operator name
const network_provider = lines
.find((line) => line.includes("+QSPN:"))
.split(",")[0]
.replace("+QSPN: ", "")
.replace(/"/g, "")
.replace(/ /g, "");
// 处理运营商名称显示 | Process operator name display
if (operatorMap[plmn]) {
// 如果在映射表中找到对应的运营商名称,直接使用
// If found in mapping table, use the mapped operator name
this.networkProvider = operatorMap[plmn];
} else if (network_provider.length === 24 && /^[0-9A-F]+$/i.test(network_provider)) {
// 如果是24位十六进制编码进行解码
// If it's a 24-bit hexadecimal encoding, decode it
this.networkProvider = hexToAscii(network_provider);
} else if (network_provider.match(/^[0-9]+$/) != null) {
// 如果是纯数字,使用 plmn_name
// If it's pure numbers, use plmn_name
this.networkProvider = lines
.find((line) => line.includes("+QSPN:"))
.split(",")[2]
.replace(/"/g, "");
} else {
// 其他情况直接使用原始名称
// For other cases, use the original name
this.networkProvider = network_provider;
}
// 十六进制解码函数 | Hexadecimal decoding function
function hexToAscii(hex) {
let ascii = '';
for (let i = 0; i < hex.length; i += 2) {
ascii += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
}
return ascii;
}
// --- MCCMNC ---
this.mccmnc = lines
.find((line) => line.includes("+QSPN:"))
.split(",")[4]
.replace(/"/g, "");
// --- APN ---
// find this example value from lines "+CGCONTRDP: 1,0,\"internet.dito.ph\",\"100.65.141.236\",\"36.5.141.64.76.204.39.68.23.210.251.16.49.239.42.149\", \"254.128.0.0.0.0.0.0.0.0.0.0.0.0.0.1\",\"131.226.72.19\",\"131.226.73.19\"\r"
this.apn = lines
.find((line) => line.includes("+CGCONTRDP:"))
.split(",")[2]
.replace(/"/g, "");
// --- Network Mode ---
// find this example value from lines "+QENG: \"servingcell\",\"NOCONN\",\"NR5G-SA\",\"TDD\",515,66,7000C4001,475,702000,620640,78,12,-83,-3,16,1,-\r"
try {
const network_mode = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[2]
.replace(/"/g, "");
const duplex_mode = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[3]
.replace(/"/g, "");
if (network_mode == "NR5G-SA") {
if (duplex_mode == "TDD") {
this.networkMode = "5G SA TDD";
} else if (duplex_mode == "FDD") {
this.networkMode = "5G SA FDD";
}
}
if (network_mode == "LTE") {
// get the FDD | TDD value
const is_tdd = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[3]
.replace(/"/g, "");
if (is_tdd == "TDD") {
this.networkMode = "4G LTE TDD";
} else if (is_tdd == "FDD") {
this.networkMode = "4G LTE FDD";
}
}
} catch (error) {
// find this example value from lines "+QENG: \"LTE\",\"FDD\",515,03,22AE76D,398,1350,3,4,4,BF82,-110,-13,-78,10,6,200,-\r"
const network_mode_lte = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[0]
.replace("+QENG: ", "")
.replace(/"/g, "");
try {
// find this example value from lines "+QENG: \"NR5G-NSA\",515,03,843,-95,20,-11,528030,41,8,1\r"
const network_mode_5g = lines
.find((line) => line.includes('+QENG: "NR5G-NSA"'))
.split(",")[0]
.replace("+QENG: ", "")
.replace(/"/g, "");
this.networkMode = "5G NSA";
} catch (error) {
if (network_mode_lte == "FDD") {
this.networkMode = "4G LTE FDD";
} else if (network_mode_lte == "TDD") {
this.networkMode = "4G LTE TDD";
}
}
}
// --- Bands ---
// Get all the values with LTE BAND n (for example, LTE BAND 3, LTE BAND 1) and then store them in an array
const bands = lines.filter((line) =>
line.includes("LTE BAND")
);
// since it includes the whole line, we need to extract the band part only
for (let i = 0; i < bands.length; i++) {
bands[i] = bands[i].split(",")[3].replace(/"/g, "");
}
// Get all the values with NR BAND n (for example, NR BAND 3, NR BAND 1) and then store them in an array
const bands_5g = lines.filter((line) =>
line.includes("NR5G BAND")
);
// since it includes the whole line, we need to extract the band number only
for (let i = 0; i < bands_5g.length; i++) {
bands_5g[i] = bands_5g[i].split(",")[3].replace(/"/g, "");
}
// Combine the bands and bands_5g arrays seperated by a comma. however, bands or bands_5g can be empty
if (bands.length > 0 && bands_5g.length > 0) {
this.bands = bands.join(", ") + ", " + bands_5g.join(", ");
} else if (bands.length > 0) {
this.bands = bands.join(", ");
} else if (bands_5g.length > 0) {
this.bands = bands_5g.join(", ");
} else {
this.bands = "No Bands";
}
// --- Bandwidth ---
if (
this.networkMode == "5G SA TDD" ||
this.networkMode == "5G SA FDD"
) {
// find this example value from lines "+QENG: \"servingcell\"
const bandwidth_line = lines.find((line) =>
line.includes('+QENG: "servingcell"')
);
const nr_bw = bandwidth_line.split(",")[11];
const calculated_bandwidth = this.calculate_nr_bw(nr_bw);
this.bandwidth = "NR " + calculated_bandwidth + " MHz";
} else if (
this.networkMode == "4G LTE FDD" ||
this.networkMode == "4G LTE TDD"
) {
// find this example value from lines "+QENG: \"servingcell\"
const bandwidth_line = lines.find((line) =>
line.includes('+QENG: "servingcell"')
);
const lte_bw_ul = bandwidth_line.split(",")[10];
const lte_bw_dl = bandwidth_line.split(",")[11];
const calculated_bandwidth_ul =
this.calculate_lte_bw(lte_bw_ul);
const calculated_bandwidth_dl =
this.calculate_lte_bw(lte_bw_dl);
this.bandwidth =
calculated_bandwidth_ul +
" UL / " +
calculated_bandwidth_dl +
" DL MHz";
} else if (this.networkMode == "5G NSA") {
// find this example value from lines "+QENG: \"LTE\" for LTE
const lte_bandwidth_line = lines.find((line) =>
line.includes('+QENG: "LTE"')
);
const lte_bw_ul = lte_bandwidth_line.split(",")[8];
const lte_bw_dl = lte_bandwidth_line.split(",")[9];
const calculated_bandwidth_ul =
this.calculate_lte_bw(lte_bw_ul);
const calculated_bandwidth_dl =
this.calculate_lte_bw(lte_bw_dl);
// find this example value from lines "+QENG: \"NR5G-NSA\" for NR5G
const nr_bandwidth_line = lines.find((line) =>
line.includes('+QENG: "NR5G-NSA"')
);
const nr_bw = nr_bandwidth_line.split(",")[9];
const calculated_bandwidth = this.calculate_nr_bw(nr_bw);
// combine the bandwidths
this.bandwidth =
calculated_bandwidth_ul +
" UL / " +
calculated_bandwidth_dl +
" DL MHz" +
" / NR " +
calculated_bandwidth +
" MHz";
} else {
this.bandwidth = "Unknown Bandwidth";
}
// --- E/ARFCN ---
if (
this.networkMode == "5G SA TDD" ||
this.networkMode == "5G SA FDD"
) {
// find this value from lines "+QCAINFO: \"PCC\"
const nr_pcc_arfcn = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[1];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const nr_scc_arfcn = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// if empty, then proceed to error block
if (nr_scc_arfcn.length == 0) {
throw "No SCC ARFCN";
}
// process all the values in the array and extract the ARFCN part only
for (let i = 0; i < nr_scc_arfcn.length; i++) {
nr_scc_arfcn[i] = nr_scc_arfcn[i].split(",")[1];
}
// combine the PCC and SCC ARFCN values
this.earfcns =
nr_pcc_arfcn + ", " + nr_scc_arfcn.join(", ");
} catch (error) {
this.earfcns = nr_pcc_arfcn.replace(/,/g, "");
}
} else if (
this.networkMode == "4G LTE FDD" ||
this.networkMode == "4G LTE TDD"
) {
// find this value from lines "+QCAINFO: \"PCC\"
const lte_pcc_arfcn = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[1];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const lte_scc_arfcn = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// if empty, then proceed to error block
if (lte_scc_arfcn.length == 0) {
throw "No SCC ARFCN";
}
// process all the values in the array and extract the ARFCN part only
for (let i = 0; i < lte_scc_arfcn.length; i++) {
lte_scc_arfcn[i] = lte_scc_arfcn[i].split(",")[1];
}
// combine the PCC and SCC ARFCN values
this.earfcns =
lte_pcc_arfcn + ", " + lte_scc_arfcn.join(", ");
} catch (error) {
this.earfcns = lte_pcc_arfcn.replace(/,/g, "");
}
} else if (this.networkMode == "5G NSA") {
// find this value from lines "+QCAINFO: \"PCC\"
const lte_pcc_arfcn = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[5];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const lte_scc_arfcn = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// If empty, then proceed to error block
if (lte_scc_arfcn.length == 0) {
throw "No SCC ARFCN";
}
// process all the values in the array and extract the ARFCN part only
for (let i = 0; i < lte_scc_arfcn.length; i++) {
lte_scc_arfcn[i] = lte_scc_arfcn[i].split(",")[1];
}
// combine the PCC and SCC ARFCN values
this.earfcns =
lte_pcc_arfcn + ", " + lte_scc_arfcn.join(", ");
} catch (error) {
this.earfcns = lte_pcc_arfcn.replace(/,/g, "");
}
} else {
this.earfcns = "Unknown E/ARFCN";
}
// --- PCI ---
if (
this.networkMode == "5G SA TDD" ||
this.networkMode == "5G SA FDD"
) {
const nr_pcc_pci = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[4];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const nr_scc_pci = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// if empty, then proceed to error block
if (nr_scc_pci.length == 0) {
throw "No SCC PCI";
}
// process all the values in the array and extract the PCI part only
for (let i = 0; i < nr_scc_pci.length; i++) {
nr_scc_pci[i] = nr_scc_pci[i].split(",")[5];
}
// combine the PCC and SCC PCI values
this.pccPCI = nr_pcc_pci;
this.sccPCI = nr_scc_pci.join(", ");
} catch (error) {
// remove comma if only one value
this.pccPCI = nr_pcc_pci.replace(/,/g, "");
this.sccPCI = "-";
}
} else if (
this.networkMode == "4G LTE FDD" ||
this.networkMode == "4G LTE TDD"
) {
// find this value from lines "+QCAINFO: \"PCC\"
const lte_pcc_pci = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[5];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const lte_scc_pci = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// if empty, then proceed to error block
if (lte_scc_pci.length == 0) {
throw "No SCC PCI";
}
// process all the values in the array and extract the PCI part only
for (let i = 0; i < lte_scc_pci.length; i++) {
lte_scc_pci[i] = lte_scc_pci[i].split(",")[5];
}
// combine the PCC and SCC PCI values
this.pccPCI = lte_pcc_pci;
this.sccPCI = lte_scc_pci.join(", ");
} catch (error) {
this.pccPCI = lte_pcc_pci;
this.sccPCI = "-";
}
} else if (this.networkMode == "5G NSA") {
// find this value from lines "+QCAINFO: \"PCC\"
const lte_pcc_pci = lines
.find((line) => line.includes('+QCAINFO: "PCC"'))
.split(",")[5];
try {
// Look for all the lines with this value "+QCAINFO: \"SCC\" and store them in an array
const lte_scc_pci = lines.filter((line) =>
line.includes('+QCAINFO: "SCC"')
);
// If empty, then proceed to error block
if (lte_scc_pci.length == 0) {
throw "No SCC PCI";
}
// process all the values in the array and extract the PCI part only
for (let i = 0; i < lte_scc_pci.length; i++) {
// if line contains LTE BAND then do this process
if (lte_scc_pci[i].includes("LTE BAND")) {
lte_scc_pci[i] = lte_scc_pci[i].split(",")[5];
} else {
lte_scc_pci[i] = lte_scc_pci[i].split(",")[4];
}
}
// combine the PCC and SCC PCI values
this.pccPCI = lte_pcc_pci;
this.sccPCI = lte_scc_pci.join(", ");
} catch (error) {
this.pccPCI = lte_pcc_pci.replace(/,/g, "");
this.sccPCI = "-";
}
} else {
this.pccPCI = "0";
this.sccPCI = "-";
}
// --- IPv4 and IPv6 ---
// find the value from line "IPV4"
this.ipv4 = lines
.find((line) => line.includes("IPV4"))
.split(",")[4]
.replace(/"/g, "");
// find the value from line "IPV6"
this.ipv6 = lines
.find((line) => line.includes("IPV6"))
.split(",")[4]
.replace(/"/g, "");
// Traffic Stats
// for NR traffic stats: +QGDNRCNT: 3263753367,109876105
this.nrDownload = lines
.find((line) => line.includes("+QGDNRCNT:"))
.split(",")[0]
// remove the +QGDNRCNT: part
.replace("+QGDNRCNT: ", "");
this.nrUpload = lines
.find((line) => line.includes("+QGDNRCNT:"))
.split(",")[1];
// for non-NR traffic stats: +QGDCNT: 247357510,6864571506
this.nonNrDownload = lines
.find((line) => line.includes("+QGDCNT:"))
.split(",")[1];
this.nonNrUpload = lines
.find((line) => line.includes("+QGDCNT:"))
.split(",")[0]
// remove the +QGDCNT: part
.replace("+QGDCNT: ", "");
// Add the nrDownload and nonNrDownload together
this.downloadStat =
parseInt(this.nrDownload) + parseInt(this.nonNrDownload);
// Add the nrUpload and nonNrUpload together
this.uploadStat =
parseInt(this.nrUpload) + parseInt(this.nonNrUpload);
// Convert the downloadStat and uploadStat bytes to readable size
this.downloadStat = this.bytesToSize(this.downloadStat);
this.uploadStat = this.bytesToSize(this.uploadStat);
console.log(this.downloadStat);
console.log(this.uploadStat);
// Signal Informations
const currentNetworkMode = this.networkMode;
if (
currentNetworkMode == "5G SA TDD" ||
currentNetworkMode == "5G SA FDD" ||
currentNetworkMode == "4G LTE FDD" ||
currentNetworkMode == "4G LTE TDD"
) {
// find the value from line "+QENG: \"servingcell\""
// CellID
const longCID = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[6]
.replace(/"/g, "");
// Get the eNBID. Its just Cell ID minus the last 2 characters
this.eNBID = longCID.substring(0, longCID.length - 2);
// Get the short Cell ID (Last 2 characters of the Cell ID)
const shortCID = longCID.substring(longCID.length - 2);
if (
currentNetworkMode == "5G SA TDD" ||
currentNetworkMode == "5G SA FDD"
) {
// TAC
this.tac = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[8]
.replace(/"/g, "");
// CSQ
this.csq = "NR-SA Mode";
// RSRP
this.rsrpNR = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[12]
.replace(/"/g, "");
// RSRQ
this.rsrqNR = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[13]
.replace(/"/g, "");
// SINR
this.sinrNR = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[14]
.replace(/"/g, "");
// Calculate the RSRP Percentage
this.rsrpNRPercentage = this.calculateRSRPPercentage(
parseInt(this.rsrpNR)
);
// Calculate the RSRQ Percentage
this.rsrqNRPercentage = this.calculateRSRQPercentage(
parseInt(this.rsrqNR)
);
// Calculate the SINR Percentage
this.sinrNRPercentage = this.calculateSINRPercentage(
parseInt(this.sinrNR)
);
// Calculate the Signal Percentage
this.signalPercentage = this.calculateSignalPercentage(
this.rsrpNRPercentage,
this.sinrNRPercentage
);
// Calculate the Signal Assessment
this.signalAssessment = this.signalQuality(
this.signalPercentage
);
} else {
// LTE Only
// TAC
this.tac = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[12]
.replace(/"/g, "");
// CSQ
this.csq = lines
.find((line) => line.includes("+CSQ:"))
.split(" ")[1]
.replace("+CSQ: ", "")
.replace(/"/g, "");
// RSRP
this.rsrpLTE = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[13]
.replace(/"/g, "");
// RSRQ
this.rsrqLTE = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[14]
.replace(/"/g, "");
// // RSSI
// this.rssi = lines
// .find((line) => line.includes('+QENG: "servingcell"'))
// .split(",")[15]
// .replace(/"/g, "");
// SINR
this.sinrLTE = lines
.find((line) => line.includes('+QENG: "servingcell"'))
.split(",")[16]
.replace(/"/g, "");
// Calculate the RSRP Percentage
this.rsrpLTEPercentage = this.calculateRSRPPercentage(
parseInt(this.rsrpLTE)
);
// Calculate the RSRQ Percentage
this.rsrqLTEPercentage = this.calculateRSRQPercentage(
parseInt(this.rsrqLTE)
);
// Calculate the SINR Percentage
this.sinrLTEPercentage = this.calculateSINRPercentage(
parseInt(this.sinrLTE)
);
// Calculate the Signal Percentage
this.signalPercentage = this.calculateSignalPercentage(
this.rsrpLTEPercentage,
this.sinrLTEPercentage
);
// Calculate the Signal Assessment
this.signalAssessment = this.signalQuality(
this.signalPercentage
);
}
this.cellID =
"Short " +
shortCID +
"(" +
parseInt(shortCID, 16) +
")" +
", " +
"Long " +
longCID +
"(" +
parseInt(longCID, 16) +
")";
} else if (currentNetworkMode == "5G NSA") {
// find the value from line "+QENG: \"LTE\" for LTE
// LongCID
const longCID = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[4]
.replace(/"/g, "");
// Get the eNBID. Its just Cell ID minus the last 2 characters
this.eNBID = longCID.substring(0, longCID.length - 2);
// Get the short Cell ID (Last 2 characters of the Cell ID)
const shortCID = longCID.substring(longCID.length - 2);
// cellID
this.cellID =
"Short " +
shortCID +
"(" +
parseInt(shortCID, 16) +
")" +
", " +
"Long " +
longCID +
"(" +
parseInt(longCID, 16) +
")";
// TAC
this.tac = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[10]
.replace(/"/g, "");
// CSQ
this.csq = lines
.find((line) => line.includes("+CSQ:"))
.split(" ")[1]
.replace("+CSQ: ", "")
.replace(/"/g, "");
// RSRP LTE
this.rsrpLTE = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[11]
.replace(/"/g, "");
// RSRQ LTE
this.rsrqLTE = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[12]
.replace(/"/g, "");
// // RSSI LTE
// this.rssi = lines
// .find((line) => line.includes('+QENG: "LTE"'))
// .split(",")[13]
// .replace(/"/g, "");
// SINR LTE
this.sinrLTE = lines
.find((line) => line.includes('+QENG: "LTE"'))
.split(",")[14]
.replace(/"/g, "");
// Calculate the RSRP LTE Percentage
this.rsrpLTEPercentage = this.calculateRSRPPercentage(
parseInt(this.rsrpLTE)
);
// Calculate the RSRQ LTE Percentage
this.rsrqLTEPercentage = this.calculateRSRQPercentage(
parseInt(this.rsrqLTE)
);
// Calculate the SINR LTE Percentage
this.sinrLTEPercentage = this.calculateSINRPercentage(
parseInt(this.sinrLTE)
);
// Calculate the Signal Percentage
const lte_signal_percentage =
this.calculateSignalPercentage(
this.rsrpLTEPercentage,
this.sinrLTEPercentage
);
// find the value from line "+QENG: \"NR5G-NSA\" for NR5G
// RSRP NR
this.rsrpNR = lines
.find((line) => line.includes('+QENG: "NR5G-NSA"'))
.split(",")[4]
.replace(/"/g, "");
// SINR NR
this.sinrNR = lines
.find((line) => line.includes('+QENG: "NR5G-NSA"'))
.split(",")[5]
.replace(/"/g, "");
// RSRQ NR
this.rsrqNR = lines
.find((line) => line.includes('+QENG: "NR5G-NSA"'))
.split(",")[6]
.replace(/"/g, "");
// Calculate the RSRP NR Percentage
this.rsrpNRPercentage = this.calculateRSRPPercentage(
parseInt(this.rsrpNR)
);
// Calculate the RSRQ NR Percentage
this.rsrqNRPercentage = this.calculateRSRQPercentage(
parseInt(this.rsrqNR)
);
// Calculate the SINR NR Percentage
this.sinrNRPercentage = this.calculateSINRPercentage(
parseInt(this.sinrNR)
);
// Calculate the Signal Percentage
const nr_signal_percentage = this.calculateSignalPercentage(
this.rsrpNRPercentage,
this.sinrNRPercentage
);
// Average the LTE and NR Signal Percentages
this.signalPercentage =
(lte_signal_percentage + nr_signal_percentage) / 2;
// Calculate the Signal Assessment
this.signalAssessment = this.signalQuality(
this.signalPercentage
);
} else {
this.signalAssessment = "No Signal";
}
}
});
});
},
bytesToSize(bytes) {
const sizes = ["Bytes", "KB", "MB", "GB", "TB"];
if (bytes == 0) return "0 Byte";
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.round(bytes / Math.pow(1024, i), 2) + " " + sizes[i];
},
requestPing() {
return fetch("/cgi-bin/get_ping")
.then((response) => response.text())
.then((data) => {
return data;
})
.catch((error) => {
console.error("Error:", error);
// Throw the error again to ensure it's propagated
throw error;
});
},
calculate_lte_bw(lte_bw) {
switch (true) {
case 0:
return 1.4;
case 1:
return 3;
// Now case 2 - 5
case lte_bw >= 2 && lte_bw <= 5:
return (lte_bw - 1) * 5;
default:
return "Unknown";
}
},
calculate_nr_bw(nr_bw) {
switch (true) {
case nr_bw >= 0 && nr_bw <= 5:
return (nr_bw + 1) * 5;
case nr_bw >= 6 && nr_bw <= 12:
return (nr_bw - 2) * 10;
case nr_bw === 13:
return "200";
case nr_bw === 14:
return "400";
default:
return "Unknown";
}
},
calculateRSRPPercentage(rsrp) {
let RSRP_min = -135;
let RSRP_max = -65;
// If rsrp is null, return 0%
if (isNaN(rsrp) || rsrp < -140) {
return 0;
}
let percentage = ((rsrp - RSRP_min) / (RSRP_max - RSRP_min)) * 100;
if (percentage > 100) {
percentage = 100;
}
// if percentage is less than 15%, make it 15%
if (percentage < 15) {
percentage = 15;
}
return Math.round(percentage);
},
calculateRSRQPercentage(rsrq) {
let RSRQ_min = -20;
let RSRQ_max = -8;
// If rsrq is null, return 0%
if (isNaN(rsrq) || rsrq < -20) {
return 0;
}
let percentage = ((rsrq - RSRQ_min) / (RSRQ_max - RSRQ_min)) * 100;
if (percentage > 100) {
percentage = 100;
}
// if percentage is less than 15%, make it 15%
if (percentage < 15) {
percentage = 15;
}
return Math.round(percentage);
},
calculateSINRPercentage(sinr) {
let SINR_min = -10; // Changed from 0
let SINR_max = 35;
// If sinr is null, return 0%
if (isNaN(sinr) || sinr < -10) {
return 0;
}
let percentage = ((sinr - SINR_min) / (SINR_max - SINR_min)) * 100;
if (percentage > 100) {
percentage = 100;
}
// if percentage is less than 15%, make it 15%
if (percentage < 15) {
percentage = 15;
}
return Math.round(percentage);
},
// Calculate the overall signal assessment
calculateSignalPercentage(rsrpNRPercentage, sinrNRPercentage) {
// Get the average of the RSRP Percentage and SINR Percentage
let average = (rsrpNRPercentage + sinrNRPercentage) / 2;
return Math.round(average);
},
signalQuality(percentage) {
if (percentage >= 80) {
return "优秀";
} else if (percentage >= 60) {
return "良好";
} else if (percentage >= 40) {
return "一般";
} else if (percentage >= 0) {
return "差";
} else {
return "无信号";
}
},
fetchUpTime() {
// Content-Type: text/plain
//
// 1 hour 44, minute
fetch("/cgi-bin/get_uptime")
.then((response) => response.text())
.then((data) => {
// Example result
// 01:17:02 up 3 days, 2:41, load average: 0.65, 0.66, 0.60
// Look for xx days in the result
const days = data.match(/(\d+) day/);
// Do the same for hours
const hours = data.match(/(\d+) hour/);
// Do the same for minutes
const minutes = data.match(/(\d+) min/);
// 2:41
const hoursAndMinutes = data.match(/(\d+):(\d+),/);
if (hoursAndMinutes != null) {
if (days != null) {
if (days[1] === "1") {
if (hoursAndMinutes[1] === "1") {
this.uptime =
days[1] +
" day, " +
hoursAndMinutes[1] +
" hour " +
hoursAndMinutes[2] +
" minutes";
} else if (hoursAndMinutes[2] === 1) {
this.uptime =
days[1] +
" day, " +
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minute";
} else {
this.uptime =
days[1] +
" day, " +
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minutes";
}
} else {
if (hoursAndMinutes[1] === "1") {
this.uptime =
days[1] +
" days, " +
hoursAndMinutes[1] +
" hour " +
hoursAndMinutes[2] +
" minutes";
} else if (hoursAndMinutes[2] === 1) {
this.uptime =
days[1] +
" days, " +
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minute";
} else {
this.uptime =
days[1] +
" days, " +
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minutes";
}
}
} else {
if (hoursAndMinutes[1] === "1") {
this.uptime =
hoursAndMinutes[1] +
" hour " +
hoursAndMinutes[2] +
" minutes";
} else if (hoursAndMinutes[2] === 1) {
this.uptime =
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minute";
} else {
this.uptime =
hoursAndMinutes[1] +
" hours " +
hoursAndMinutes[2] +
" minutes";
}
}
} else if (days != null) {
if (hours != null) {
if (days[1] === "1") {
if (hours[1] === "1") {
this.uptime = days[1] + " day, " + hours[1] + " hour";
} else {
this.uptime = days[1] + " day, " + hours[1] + " hours";
}
} else {
if (hours[1] === "1") {
this.uptime = days[1] + " days, " + hours[1] + " hour";
} else {
this.uptime = days[1] + " days, " + hours[1] + " hours";
}
}
} else if (minutes != null) {
if (days[1] === "1") {
if (minutes[1] === "1") {
this.uptime =
days[1] + " day, " + minutes[1] + " minute";
} else {
this.uptime =
days[1] + " day, " + minutes[1] + " minutes";
}
} else {
if (minutes[1] === "1") {
this.uptime =
days[1] + " days, " + minutes[1] + " minute";
} else {
this.uptime =
days[1] + " days, " + minutes[1] + " minutes";
}
}
} else {
if (days[1] === "1") {
this.uptime = days[1] + " day";
} else {
this.uptime = days[1] + " days";
}
}
} else if (hours != null) {
if (hours[1] === "1") {
this.uptime = hours[1] + " hour";
} else {
this.uptime = hours[1] + " hours";
}
} else if (minutes != null) {
if (minutes[1] === "1") {
this.uptime = minutes[1] + " minute";
} else {
this.uptime = minutes[1] + " minutes";
}
} else {
this.uptime = "未知时间";
}
});
},
updateRefreshRate() {
// Check if the refresh rate is less than 3
if (this.newRefreshRate < 3) {
this.newRefreshRate = 3;
}
// Clear the old interval
clearInterval(this.intervalId);
// Set the refresh rate
this.refreshRate = this.newRefreshRate;
console.log("Refresh Rate Updated to " + this.refreshRate);
// Store the refresh rate in local storage or session storage
localStorage.setItem("refreshRate", this.refreshRate);
// Initialize with the new refresh rate
this.init();
},
init() {
// Fetch uptime
this.fetchUpTime();
// Retrieve the refresh rate from local storage or session storage
const storedRefreshRate = localStorage.getItem("refreshRate");
// If a refresh rate is stored, use it; otherwise, use a default value
this.refreshRate = storedRefreshRate
? parseInt(storedRefreshRate)
: 3; // Change 3 to your desired default value
this.fetchAllInfo();
this.requestPing()
.then((data) => {
const response = data.trim();
// Trim any leading/trailing spaces
if (response === "OK") {
this.internetConnectionStatus = "已连接";
} else {
this.internetConnectionStatus = "已断开";
}
})
.catch((error) => {
console.error("Error:", error);
this.internetConnectionStatus = "已断开";
});
this.lastUpdate = new Date().toLocaleString();
console.log("Initialized");
// Set the refresh rate for interval
this.intervalId = setInterval(() => {
this.fetchUpTime();
this.fetchAllInfo();
this.requestPing()
.then((data) => {
const response = data.trim();
// Trim any leading/trailing spaces
if (response === "OK") {
this.internetConnectionStatus = "已连接";
} else {
this.internetConnectionStatus = "已断开";
}
})
.catch((error) => {
console.error("Error:", error);
this.internetConnectionStatus = "已断开";
});
this.lastUpdate = new Date().toLocaleString();
console.log("Refreshed");
}, this.refreshRate * 1000);
},
};
}
</script>
</body>
</html>