A:增加详细计算解释
This commit is contained in:
1 parent
e643d92fcb
commit
055a122bf8
1 file changed
+2363
-1986
+2363
-1986
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@@ -10,40 +10,43 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background: rgba(255, 255, 255, 0.95);
|
background: rgba(255, 255, 255, 0.98);
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.08);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
background: linear-gradient(135deg, #2c3e50, #3498db);
|
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
font-size: 2.5em;
|
font-size: 2.5em;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header p {
|
.header p {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
opacity: 0.9;
|
opacity: 0.95;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 语言和Github按钮样式 */
|
/* 语言和Github按钮样式 */
|
||||||
@@ -57,14 +60,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lang-btn, .github-btn {
|
.lang-btn, .github-btn {
|
||||||
background: rgba(255, 255, 255, 0.2);
|
background: rgba(255, 255, 255, 0.25);
|
||||||
color: white;
|
color: white;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||||
border-radius: 25px;
|
border-radius: 25px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -74,124 +77,126 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lang-btn:hover, .github-btn:hover {
|
.lang-btn:hover, .github-btn:hover {
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.35);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-btn.active {
|
.lang-btn.active {
|
||||||
background: rgba(255, 255, 255, 0.3);
|
background: rgba(255, 255, 255, 0.35);
|
||||||
border-color: rgba(255, 255, 255, 0.5);
|
border-color: rgba(255, 255, 255, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.github-btn {
|
.github-btn {
|
||||||
background: rgba(36, 41, 46, 0.8);
|
background: rgba(36, 41, 46, 0.7);
|
||||||
border-color: rgba(255, 255, 255, 0.2);
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.github-btn:hover {
|
.github-btn:hover {
|
||||||
background: rgba(36, 41, 46, 0.9);
|
background: rgba(36, 41, 46, 0.85);
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-section, .control-section {
|
.input-section, .control-section {
|
||||||
background: white;
|
background: #f8fafb;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
|
||||||
border: 1px solid #e0e6ed;
|
border: 1px solid #e8f0f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
font-size: 1.4em;
|
font-size: 1.4em;
|
||||||
color: #2c3e50;
|
color: #4682b4;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
border-bottom: 2px solid #3498db;
|
border-bottom: 2px solid #87ceeb;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #34495e;
|
color: #5a7c8a;
|
||||||
}
|
}
|
||||||
|
|
||||||
input, select, textarea {
|
input, select, textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border: 2px solid #e1e8ed;
|
border: 2px solid #e0ecf1;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus, select:focus, textarea:focus {
|
input:focus, select:focus, textarea:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: #3498db;
|
border-color: #87ceeb;
|
||||||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
|
box-shadow: 0 0 0 3px rgba(135, 206, 235, 0.15);
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
background: linear-gradient(135deg, #3498db, #2980b9);
|
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 12px 24px;
|
padding: 12px 24px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
.btn:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
box-shadow: 0 8px 25px rgba(52, 152, 219, 0.3);
|
box-shadow: 0 8px 20px rgba(70, 130, 180, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
background: linear-gradient(135deg, #95a5a6, #7f8c8d);
|
background: linear-gradient(135deg, #b0c4de, #9abdd6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success {
|
.btn-success {
|
||||||
background: linear-gradient(135deg, #27ae60, #229954);
|
background: linear-gradient(135deg, #66cdaa, #4db393);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-smart {
|
.btn-smart {
|
||||||
background: linear-gradient(135deg, #e74c3c, #c0392b);
|
background: linear-gradient(135deg, #ff7f50, #ff6347);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 智能检测面板样式 - 紧凑版 */
|
/* 智能检测面板样式 - 清新版 */
|
||||||
.smart-detection {
|
.smart-detection {
|
||||||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
background: linear-gradient(135deg, #f0f9ff, #e6f7ff);
|
||||||
border: 2px solid #17a2b8;
|
border: 2px solid #87ceeb;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smart-detection::before {
|
.smart-detection::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -199,43 +204,43 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background: linear-gradient(90deg, #17a2b8, #138496);
|
background: linear-gradient(90deg, #87ceeb, #5f9ea0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-header {
|
.detection-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #2c3e50;
|
color: #4682b4;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-results {
|
.detection-results {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-results::-webkit-scrollbar {
|
.detection-results::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-results::-webkit-scrollbar-track {
|
.detection-results::-webkit-scrollbar-track {
|
||||||
background: #f1f1f1;
|
background: #f0f9ff;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-results::-webkit-scrollbar-thumb {
|
.detection-results::-webkit-scrollbar-thumb {
|
||||||
background: #888;
|
background: #b0c4de;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-item {
|
.detection-item {
|
||||||
background: white;
|
background: white;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #daeaf2;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -243,32 +248,32 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detection-category {
|
.detection-category {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #495057;
|
color: #5a7c8a;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.confidence-high {
|
.confidence-high {
|
||||||
border-left: 3px solid #28a745;
|
border-left: 3px solid #66cdaa;
|
||||||
background: linear-gradient(90deg, rgba(40, 167, 69, 0.08), white);
|
background: linear-gradient(90deg, rgba(102, 205, 170, 0.08), white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confidence-medium {
|
.confidence-medium {
|
||||||
border-left: 3px solid #ffc107;
|
border-left: 3px solid #ffd700;
|
||||||
background: linear-gradient(90deg, rgba(255, 193, 7, 0.08), white);
|
background: linear-gradient(90deg, rgba(255, 215, 0, 0.08), white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confidence-low {
|
.confidence-low {
|
||||||
border-left: 3px solid #dc3545;
|
border-left: 3px solid #ff7f50;
|
||||||
background: linear-gradient(90deg, rgba(220, 53, 69, 0.08), white);
|
background: linear-gradient(90deg, rgba(255, 127, 80, 0.08), white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.confidence-badge {
|
.confidence-badge {
|
||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
@@ -276,107 +281,107 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-high {
|
.badge-high {
|
||||||
background: #28a745;
|
background: #66cdaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-medium {
|
.badge-medium {
|
||||||
background: #ffc107;
|
background: #ffd700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-low {
|
.badge-low {
|
||||||
background: #dc3545;
|
background: #ff7f50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recommendation-panel {
|
.recommendation-panel {
|
||||||
background: linear-gradient(135deg, #e8f5e8, #f0f8f0);
|
background: linear-gradient(135deg, #f0fdf4, #dcfce7);
|
||||||
border: 2px solid #28a745;
|
border: 2px solid #66cdaa;
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recommendation-header {
|
.recommendation-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #155724;
|
color: #2e7d32;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recommended-item {
|
.recommended-item {
|
||||||
background: white;
|
background: white;
|
||||||
border: 1px solid #c3e6cb;
|
border: 1px solid #a7f3d0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.recommended-badge {
|
.recommended-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -6px;
|
top: -6px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
background: #28a745;
|
background: #66cdaa;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-section {
|
.results-section {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
background: white;
|
background: #f8fafb;
|
||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
padding: 25px;
|
padding: 25px;
|
||||||
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.06);
|
||||||
border: 1px solid #e0e6ed;
|
border: 1px solid #e8f0f5;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-tabs {
|
.results-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-bottom: 2px solid #ecf0f1;
|
border-bottom: 2px solid #e8f0f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-btn {
|
.tab-btn {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 3px solid transparent;
|
border-bottom: 3px solid transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #7f8c8d;
|
color: #8fa9b3;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-btn.active {
|
.tab-btn.active {
|
||||||
color: #3498db;
|
color: #4682b4;
|
||||||
border-bottom-color: #3498db;
|
border-bottom-color: #4682b4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-btn.smart {
|
.tab-btn.smart {
|
||||||
color: #e74c3c;
|
color: #ff7f50;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-btn.smart.active {
|
.tab-btn.smart.active {
|
||||||
border-bottom-color: #e74c3c;
|
border-bottom-color: #ff7f50;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tab计数器样式 */
|
/* Tab计数器样式 */
|
||||||
.tab-counter {
|
.tab-counter {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -5px;
|
top: -5px;
|
||||||
right: -5px;
|
right: -5px;
|
||||||
background: #e74c3c;
|
background: #ff7f50;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
@@ -388,100 +393,104 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content {
|
.tab-content {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-content.active {
|
.tab-content.active {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-grid {
|
.result-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item {
|
.result-item {
|
||||||
background: #f8f9fa;
|
background: white;
|
||||||
border: 1px solid #e9ecef;
|
border: 1px solid #e0ecf1;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item:hover {
|
.result-item:hover {
|
||||||
background: #e3f2fd;
|
background: #f0f9ff;
|
||||||
border-color: #3498db;
|
border-color: #87ceeb;
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(135, 206, 235, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item.recommended {
|
.result-item.recommended {
|
||||||
border: 2px solid #28a745;
|
border: 2px solid #66cdaa;
|
||||||
background: linear-gradient(135deg, rgba(40, 167, 69, 0.1), #f8f9fa);
|
background: linear-gradient(135deg, rgba(102, 205, 170, 0.05), white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-item.failed {
|
.result-item.failed {
|
||||||
opacity: 0.5;
|
opacity: 0.6;
|
||||||
border-color: #dc3545;
|
border-color: #ffb6c1;
|
||||||
background: linear-gradient(135deg, rgba(220, 53, 69, 0.1), #f8f9fa);
|
background: linear-gradient(135deg, rgba(255, 182, 193, 0.1), white);
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-header {
|
.result-header {
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #2c3e50;
|
color: #4682b4;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-header-sub{
|
.result-header-sub {
|
||||||
/*max-width: 210px;*/
|
white-space: nowrap;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-value {
|
.result-value {
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
background: white;
|
background: #f0f9ff;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #daeaf2;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
background: #17a2b8;
|
background: #5f9ea0;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn:hover {
|
.copy-btn:hover {
|
||||||
background: #138496;
|
background: #4a8a8c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-btn {
|
.details-btn {
|
||||||
background: #28a745;
|
background: #66cdaa;
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-btn:hover {
|
.details-btn:hover {
|
||||||
background: #1e7e34;
|
background: #4db393;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-indicator {
|
.success-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: 8px;
|
||||||
@@ -490,20 +499,20 @@
|
|||||||
height: 12px;
|
height: 12px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-indicator.success {
|
.success-indicator.success {
|
||||||
background: #28a745;
|
background: #66cdaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-indicator.warning {
|
.success-indicator.warning {
|
||||||
background: #ffc107;
|
background: #ffd700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.success-indicator.error {
|
.success-indicator.error {
|
||||||
background: #dc3545;
|
background: #ff7f50;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 详情模态框样式 - 增大尺寸 */
|
/* 详情模态框样式 - 清新版 */
|
||||||
.details-modal {
|
.details-modal {
|
||||||
display: none;
|
display: none;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -512,10 +521,10 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
backdrop-filter: blur(5px);
|
backdrop-filter: blur(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
margin: 2% auto;
|
margin: 2% auto;
|
||||||
@@ -525,10 +534,10 @@
|
|||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
max-height: 95vh;
|
max-height: 95vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
|
||||||
animation: modalSlideIn 0.3s ease-out;
|
animation: modalSlideIn 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes modalSlideIn {
|
@keyframes modalSlideIn {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -539,9 +548,9 @@
|
|||||||
transform: translateY(0) scale(1);
|
transform: translateY(0) scale(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
background: linear-gradient(135deg, #2c3e50, #3498db);
|
background: linear-gradient(135deg, #5f9ea0, #4682b4);
|
||||||
color: white;
|
color: white;
|
||||||
padding: 20px 30px;
|
padding: 20px 30px;
|
||||||
border-radius: 15px 15px 0 0;
|
border-radius: 15px 15px 0 0;
|
||||||
@@ -549,13 +558,13 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-title {
|
.modal-title {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: 600;
|
font-weight: 400;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-btn {
|
.close-btn {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
@@ -564,263 +573,323 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-btn:hover {
|
.close-btn:hover {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
opacity: 0.7;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
padding: 25px 30px;
|
padding: 25px 30px;
|
||||||
max-height: 75vh;
|
max-height: 75vh;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body::-webkit-scrollbar {
|
.modal-body::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body::-webkit-scrollbar-track {
|
.modal-body::-webkit-scrollbar-track {
|
||||||
background: #f1f1f1;
|
background: #f0f9ff;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body::-webkit-scrollbar-thumb {
|
.modal-body::-webkit-scrollbar-thumb {
|
||||||
background: #888;
|
background: #b0c4de;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 详情内容样式 */
|
/* 详情内容样式 - 清新版 */
|
||||||
.detail-section {
|
.detail-section {
|
||||||
margin-bottom: 25px;
|
margin-bottom: 25px;
|
||||||
background: #f8f9fa;
|
background: #f8fafb;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-left: 4px solid #3498db;
|
border-left: 4px solid #87ceeb;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-title {
|
.detail-title {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #2c3e50;
|
color: #4682b4;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 新增:步骤展示样式 */
|
||||||
|
.conversion-steps {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #e0ecf1;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item {
|
||||||
|
position: relative;
|
||||||
|
padding: 15px 0;
|
||||||
|
padding-left: 40px;
|
||||||
|
border-bottom: 1px solid #f0f9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-number {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 15px;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
background: linear-gradient(135deg, #87ceeb, #5f9ea0);
|
||||||
|
color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4682b4;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-description {
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 13px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-data {
|
||||||
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
|
background: #f0f9ff;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #daeaf2;
|
||||||
|
color: #1e40af;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 8px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-arrow {
|
||||||
|
text-align: center;
|
||||||
|
color: #87ceeb;
|
||||||
|
font-size: 20px;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.byteorder-test {
|
.byteorder-test {
|
||||||
background: white;
|
background: white;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #daeaf2;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.byteorder-item {
|
.byteorder-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
border-bottom: 1px solid #eee;
|
border-bottom: 1px solid #f0f9ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.byteorder-item:last-child {
|
.byteorder-item:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.byteorder-label {
|
.byteorder-label {
|
||||||
font-weight: 600;
|
font-weight: 500;
|
||||||
color: #495057;
|
color: #5a7c8a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.byteorder-value {
|
.byteorder-value {
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
background: #f1f3f4;
|
background: #f0f9ff;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.byteorder-status {
|
.byteorder-status {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.principle-box {
|
.principle-box {
|
||||||
background: #e8f5e8;
|
background: #f0fdf4;
|
||||||
border: 1px solid #c3e6cb;
|
border: 1px solid #d1fae5;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.principle-item {
|
.principle-item {
|
||||||
margin: 8px 0;
|
margin: 8px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.principle-item strong {
|
.principle-item strong {
|
||||||
min-width: 100px;
|
min-width: 100px;
|
||||||
color: #155724;
|
color: #2e7d32;
|
||||||
}
|
}
|
||||||
|
|
||||||
.memory-layout {
|
.memory-layout {
|
||||||
background: #fff3cd;
|
background: #fffbeb;
|
||||||
border: 1px solid #ffeaa7;
|
border: 1px solid #fde68a;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.memory-step {
|
.memory-step {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.memory-bytes {
|
.memory-bytes {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #ddd;
|
border: 1px solid #fbbf24;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 原始数据展示样式 */
|
/* 原始数据展示样式 */
|
||||||
.process-step {
|
.process-step {
|
||||||
background: #f8f9fa;
|
background: white;
|
||||||
border: 1px solid #e9ecef;
|
border: 1px solid #e0ecf1;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.process-step:hover {
|
.process-step:hover {
|
||||||
background: #e3f2fd;
|
background: #f0f9ff;
|
||||||
border-color: #3498db;
|
border-color: #87ceeb;
|
||||||
transform: translateX(5px);
|
transform: translateX(5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-title {
|
|
||||||
font-weight: 600;
|
|
||||||
color: #2c3e50;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.step-content {
|
.step-content {
|
||||||
font-family: 'Consolas', 'Monaco', monospace;
|
font-family: 'Consolas', 'Monaco', monospace;
|
||||||
background: white;
|
background: #f8fafb;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #e0ecf1;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
color: #495057;
|
color: #4b5563;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: #e74c3c;
|
color: #dc2626;
|
||||||
background: #fadbd8;
|
background: #fee2e2;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.statistics-panel {
|
.statistics-panel {
|
||||||
background: #fff3cd;
|
background: #fffbeb;
|
||||||
border: 1px solid #ffc107;
|
border: 1px solid #fbbf24;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 15px 0;
|
margin: 15px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats-grid {
|
.stats-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-item {
|
.stat-item {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid #ffecb5;
|
border: 1px solid #fde68a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-value {
|
.stat-value {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
font-weight: 700;
|
font-weight: 600;
|
||||||
color: #856404;
|
color: #d97706;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #6c757d;
|
color: #6b7280;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 自动选择提示 */
|
/* 自动选择提示 */
|
||||||
.auto-select-notice {
|
.auto-select-notice {
|
||||||
background: #d1ecf1;
|
background: #dbeafe;
|
||||||
border: 1px solid #bee5eb;
|
border: 1px solid #93c5fd;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #0c5460;
|
color: #1e40af;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auto-select-notice::before {
|
.auto-select-notice::before {
|
||||||
content: 'ℹ️';
|
content: 'ℹ️';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 禁用body滚动 */
|
/* 禁用body滚动 */
|
||||||
.modal-open {
|
.modal-open {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.main-content {
|
.main-content {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header h1 {
|
.header h1 {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-grid {
|
.result-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
margin: 1% auto;
|
margin: 1% auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
padding: 15px 20px;
|
padding: 15px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-body {
|
.modal-body {
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@@ -834,875 +903,906 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<!-- 语言切换和Github按钮 -->
|
<!-- 语言切换和Github按钮 -->
|
||||||
<div class="header-buttons">
|
<div class="header-buttons">
|
||||||
<button class="lang-btn active" onclick="switchLanguage('zh')" data-lang="zh">
|
<button class="lang-btn active" onclick="switchLanguage('zh')" data-lang="zh">
|
||||||
🇨🇳 中文
|
🇨🇳 中文
|
||||||
</button>
|
</button>
|
||||||
<button class="lang-btn" onclick="switchLanguage('en')" data-lang="en">
|
<button class="lang-btn" onclick="switchLanguage('en')" data-lang="en">
|
||||||
🇺🇸 English
|
🇺🇸 English
|
||||||
</button>
|
</button>
|
||||||
<a href="https://github.com/duyl328/PLC-Data-Lab" target="_blank" class="github-btn">
|
<a href="https://github.com/duyl328/PLC-Data-Lab" target="_blank" class="github-btn">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
|
||||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<span data-i18n="github">Github</span>
|
<span data-i18n="github">Github</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h1 data-i18n="title">PLC数据格式转换与调试工具</h1>
|
<h1 data-i18n="title">PLC数据格式转换与调试工具</h1>
|
||||||
<p data-i18n="subtitle">数据格式检测、转换与智能推荐系统</p>
|
<p data-i18n="subtitle">数据格式检测、转换与智能推荐系统</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<!-- 数据输入区 -->
|
<!-- 数据输入区 -->
|
||||||
<div class="input-section">
|
<div class="input-section">
|
||||||
<div class="section-title">
|
|
||||||
📥 <span data-i18n="data_input">数据输入区</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label data-i18n="input_format">输入格式选择:</label>
|
|
||||||
<select id="inputFormat">
|
|
||||||
<option value="byte" data-i18n="byte_format">byte[] (0-255)</option>
|
|
||||||
<option value="ushort" selected data-i18n="ushort_format">ushort[] (0-65535)</option>
|
|
||||||
<option value="short" data-i18n="short_format">short[] (-32768 to 32767)</option>
|
|
||||||
<option value="int" data-i18n="int_format">int[] (-2147483648 to 2147483647)</option>
|
|
||||||
<option value="uint" data-i18n="uint_format">uint[] (0 to 4294967295)</option>
|
|
||||||
<option value="hex" data-i18n="hex_format">Hex 字符串</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label data-i18n="raw_data_input">原始数据输入:</label>
|
|
||||||
<textarea id="rawData" data-i18n-placeholder="raw_data_placeholder"></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<button class="btn btn-smart" onclick="intelligentAnalysis()">🧠 <span data-i18n="intelligent_analysis">智能分析</span></button>
|
|
||||||
<button class="btn" onclick="parseInput()">📊 <span data-i18n="parse_data">解析数据</span></button>
|
|
||||||
<button class="btn btn-secondary" onclick="clearInput()">🗑️ <span data-i18n="clear">清空</span></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="inputValidation"></div>
|
|
||||||
|
|
||||||
<!-- 智能检测结果面板 - 移到输入区下方 -->
|
|
||||||
<div id="smartDetection" class="smart-detection" style="display: none;">
|
|
||||||
<div class="detection-header">
|
|
||||||
🔍 <span data-i18n="smart_detection_result">智能检测结果</span>
|
|
||||||
</div>
|
|
||||||
<div id="detectionResults" class="detection-results"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 控制选项区 -->
|
|
||||||
<div class="control-section">
|
|
||||||
<div class="section-title">
|
|
||||||
⚙️ <span data-i18n="conversion_control">转换控制</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 自动选择提示 -->
|
|
||||||
<div id="autoSelectNotice" class="auto-select-notice" style="display: none;">
|
|
||||||
<span data-i18n="auto_select_notice">已根据智能检测结果自动选择最优配置</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label data-i18n="target_data_type">目标数据类型:</label>
|
|
||||||
<select id="targetType" onchange="filterResults()">
|
|
||||||
<option value="all" data-i18n="show_all_types">🔄 显示所有类型</option>
|
|
||||||
<option value="bool" data-i18n="bool_type">bool (布尔)</option>
|
|
||||||
<option value="byte" data-i18n="byte_type">byte (无符号8位)</option>
|
|
||||||
<option value="sbyte" data-i18n="sbyte_type">sbyte (有符号8位)</option>
|
|
||||||
<option value="short" data-i18n="short_type">short (有符号16位)</option>
|
|
||||||
<option value="ushort" data-i18n="ushort_type">ushort (无符号16位)</option>
|
|
||||||
<option value="int" data-i18n="int_type">int (有符号32位)</option>
|
|
||||||
<option value="uint" data-i18n="uint_type">uint (无符号32位)</option>
|
|
||||||
<option value="long" data-i18n="long_type">long (有符号64位)</option>
|
|
||||||
<option value="ulong" data-i18n="ulong_type">ulong (无符号64位)</option>
|
|
||||||
<option value="float" data-i18n="float_type">float (32位浮点)</option>
|
|
||||||
<option value="double" data-i18n="double_type">double (64位浮点)</option>
|
|
||||||
<option value="ascii" data-i18n="ascii_type">ASCII 字符串</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label data-i18n="byte_order_selection">字节序选择:</label>
|
|
||||||
<select id="byteOrder" onchange="filterResults()">
|
|
||||||
<option value="all" data-i18n="show_all_byte_order">🔄 显示所有字节序</option>
|
|
||||||
<option value="ABCD" data-i18n="abcd_big_endian">ABCD (大端序)</option>
|
|
||||||
<option value="DCBA" data-i18n="dcba_little_endian">DCBA (小端序)</option>
|
|
||||||
<option value="CDAB" data-i18n="cdab_siemens">CDAB (西门子常见)</option>
|
|
||||||
<option value="BADC" data-i18n="badc_omron">BADC (欧姆龙常见)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label data-i18n="encoding_format">编码格式:</label>
|
|
||||||
<select id="encoding" onchange="filterResults()">
|
|
||||||
<option value="all" data-i18n="show_all_encoding">🔄 显示所有编码</option>
|
|
||||||
<option value="DEC" data-i18n="dec_encoding">DEC (十进制编码)</option>
|
|
||||||
<option value="BCD" data-i18n="bcd_encoding">BCD (二进制编码十进制)</option>
|
|
||||||
<option value="HEX" data-i18n="hex_encoding">HEX (十六进制编码)</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<button class="btn btn-success" onclick="convertData()">🚀 <span data-i18n="convert">转换</span></button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 推荐配置面板 -->
|
|
||||||
<div id="recommendationPanel" class="recommendation-panel" style="display: none;">
|
|
||||||
<div class="recommendation-header">
|
|
||||||
⭐ <span data-i18n="smart_recommendation">智能推荐配置</span>
|
|
||||||
</div>
|
|
||||||
<div id="recommendedConfigs"></div>
|
|
||||||
<button class="btn btn-success" onclick="applyRecommendation()">✅ <span data-i18n="apply_recommendation">应用推荐配置</span></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 结果展示区 -->
|
|
||||||
<div class="results-section" id="resultsSection" style="display: none;">
|
|
||||||
<div class="section-title">
|
<div class="section-title">
|
||||||
📊 <span data-i18n="conversion_results">转换结果</span>
|
📥 <span data-i18n="data_input">数据输入区</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 统计信息面板 -->
|
<div class="form-group">
|
||||||
<div id="statisticsPanel" class="statistics-panel" style="display: none;">
|
<label data-i18n="input_format">输入格式选择:</label>
|
||||||
<h4>📈 <span data-i18n="conversion_statistics">转换统计</span></h4>
|
<select id="inputFormat">
|
||||||
<div id="statisticsGrid" class="stats-grid"></div>
|
<option value="byte" data-i18n="byte_format">byte[] (0-255)</option>
|
||||||
|
<option value="ushort" selected data-i18n="ushort_format">ushort[] (0-65535)</option>
|
||||||
|
<option value="short" data-i18n="short_format">short[] (-32768 to 32767)</option>
|
||||||
|
<option value="int" data-i18n="int_format">int[] (-2147483648 to 2147483647)</option>
|
||||||
|
<option value="uint" data-i18n="uint_format">uint[] (0 to 4294967295)</option>
|
||||||
|
<option value="hex" data-i18n="hex_format">Hex 字符串</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="results-tabs">
|
<div class="form-group">
|
||||||
<button class="tab-btn smart active" onclick="switchTab('smart-results')">🧠 <span data-i18n="intelligent_recommendation">智能推荐</span></button>
|
<label data-i18n="raw_data_input">原始数据输入:</label>
|
||||||
<button class="tab-btn" onclick="switchTab('all-results')">
|
<textarea id="rawData" data-i18n-placeholder="raw_data_placeholder"></textarea>
|
||||||
<span data-i18n="conversion_results_tab">转换结果</span>
|
</div>
|
||||||
<span class="tab-counter" id="allResultsCounter" style="display: none;">0</span>
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-smart" onclick="intelligentAnalysis()">🧠 <span data-i18n="intelligent_analysis">智能分析</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="tab-btn" onclick="switchTab('raw-data')"><span data-i18n="raw_data">原始数据</span></button>
|
<button class="btn" onclick="parseInput()">📊 <span data-i18n="parse_data">解析数据</span></button>
|
||||||
|
<button class="btn btn-secondary" onclick="clearInput()">🗑️ <span data-i18n="clear">清空</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="smart-results" class="tab-content active">
|
<div id="inputValidation"></div>
|
||||||
<div id="smartResultsGrid" class="result-grid"></div>
|
|
||||||
|
<!-- 智能检测结果面板 - 移到输入区下方 -->
|
||||||
|
<div id="smartDetection" class="smart-detection" style="display: none;">
|
||||||
|
<div class="detection-header">
|
||||||
|
🔍 <span data-i18n="smart_detection_result">智能检测结果</span>
|
||||||
|
</div>
|
||||||
|
<div id="detectionResults" class="detection-results"></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="all-results" class="tab-content">
|
|
||||||
<div id="allResultsGrid" class="result-grid"></div>
|
<!-- 控制选项区 -->
|
||||||
|
<div class="control-section">
|
||||||
|
<div class="section-title">
|
||||||
|
⚙️ <span data-i18n="conversion_control">转换控制</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="raw-data" class="tab-content">
|
<!-- 自动选择提示 -->
|
||||||
<div id="rawDataDisplay"></div>
|
<div id="autoSelectNotice" class="auto-select-notice" style="display: none;">
|
||||||
|
<span data-i18n="auto_select_notice">已根据智能检测结果自动选择最优配置</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label data-i18n="target_data_type">目标数据类型:</label>
|
||||||
|
<select id="targetType" onchange="filterResults()">
|
||||||
|
<option value="all" data-i18n="show_all_types">🔄 显示所有类型</option>
|
||||||
|
<option value="bool" data-i18n="bool_type">bool (布尔)</option>
|
||||||
|
<option value="byte" data-i18n="byte_type">byte (无符号8位)</option>
|
||||||
|
<option value="sbyte" data-i18n="sbyte_type">sbyte (有符号8位)</option>
|
||||||
|
<option value="short" data-i18n="short_type">short (有符号16位)</option>
|
||||||
|
<option value="ushort" data-i18n="ushort_type">ushort (无符号16位)</option>
|
||||||
|
<option value="int" data-i18n="int_type">int (有符号32位)</option>
|
||||||
|
<option value="uint" data-i18n="uint_type">uint (无符号32位)</option>
|
||||||
|
<option value="long" data-i18n="long_type">long (有符号64位)</option>
|
||||||
|
<option value="ulong" data-i18n="ulong_type">ulong (无符号64位)</option>
|
||||||
|
<option value="float" data-i18n="float_type">float (32位浮点)</option>
|
||||||
|
<option value="double" data-i18n="double_type">double (64位浮点)</option>
|
||||||
|
<option value="ascii" data-i18n="ascii_type">ASCII 字符串</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label data-i18n="byte_order_selection">字节序选择:</label>
|
||||||
|
<select id="byteOrder" onchange="filterResults()">
|
||||||
|
<option value="all" data-i18n="show_all_byte_order">🔄 显示所有字节序</option>
|
||||||
|
<option value="ABCD" data-i18n="abcd_big_endian">ABCD (大端序)</option>
|
||||||
|
<option value="DCBA" data-i18n="dcba_little_endian">DCBA (小端序)</option>
|
||||||
|
<option value="CDAB" data-i18n="cdab_siemens">CDAB (西门子常见)</option>
|
||||||
|
<option value="BADC" data-i18n="badc_omron">BADC (欧姆龙常见)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label data-i18n="encoding_format">编码格式:</label>
|
||||||
|
<select id="encoding" onchange="filterResults()">
|
||||||
|
<option value="all" data-i18n="show_all_encoding">🔄 显示所有编码</option>
|
||||||
|
<option value="DEC" data-i18n="dec_encoding">DEC (十进制编码)</option>
|
||||||
|
<option value="BCD" data-i18n="bcd_encoding">BCD (二进制编码十进制)</option>
|
||||||
|
<option value="HEX" data-i18n="hex_encoding">HEX (十六进制编码)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-success" onclick="convertData()">🚀 <span data-i18n="convert">转换</span></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 推荐配置面板 -->
|
||||||
|
<div id="recommendationPanel" class="recommendation-panel" style="display: none;">
|
||||||
|
<div class="recommendation-header">
|
||||||
|
⭐ <span data-i18n="smart_recommendation">智能推荐配置</span>
|
||||||
|
</div>
|
||||||
|
<div id="recommendedConfigs"></div>
|
||||||
|
<button class="btn btn-success" onclick="applyRecommendation()">✅ <span
|
||||||
|
data-i18n="apply_recommendation">应用推荐配置</span></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 详情模态框 -->
|
<!-- 结果展示区 -->
|
||||||
<div id="detailsModal" class="details-modal">
|
<div class="results-section" id="resultsSection" style="display: none;">
|
||||||
<div class="modal-content">
|
<div class="section-title">
|
||||||
<div class="modal-header">
|
📊 <span data-i18n="conversion_results">转换结果</span>
|
||||||
<h2 class="modal-title" id="modalTitle" data-i18n="conversion_details">转换详情</h2>
|
</div>
|
||||||
<button class="close-btn" onclick="closeDetailsModal()">×</button>
|
|
||||||
</div>
|
<!-- 统计信息面板 -->
|
||||||
<div class="modal-body" id="modalBody">
|
<div id="statisticsPanel" class="statistics-panel" style="display: none;">
|
||||||
<!-- 详情内容将在这里动态生成 -->
|
<h4>📈 <span data-i18n="conversion_statistics">转换统计</span></h4>
|
||||||
</div>
|
<div id="statisticsGrid" class="stats-grid"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="results-tabs">
|
||||||
|
<button class="tab-btn smart active" onclick="switchTab('smart-results')">🧠 <span
|
||||||
|
data-i18n="intelligent_recommendation">智能推荐</span></button>
|
||||||
|
<button class="tab-btn" onclick="switchTab('all-results')">
|
||||||
|
<span data-i18n="conversion_results_tab">转换结果</span>
|
||||||
|
<span class="tab-counter" id="allResultsCounter" style="display: none;">0</span>
|
||||||
|
</button>
|
||||||
|
<button class="tab-btn" onclick="switchTab('raw-data')"><span data-i18n="raw_data">原始数据</span></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="smart-results" class="tab-content active">
|
||||||
|
<div id="smartResultsGrid" class="result-grid"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="all-results" class="tab-content">
|
||||||
|
<div id="allResultsGrid" class="result-grid"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="raw-data" class="tab-content">
|
||||||
|
<div id="rawDataDisplay"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script>
|
<!-- 详情模态框 -->
|
||||||
// 国际化语言包
|
<div id="detailsModal" class="details-modal">
|
||||||
const i18n = {
|
<div class="modal-content">
|
||||||
zh: {
|
<div class="modal-header">
|
||||||
title: 'PLC数据格式转换与调试工具',
|
<h2 class="modal-title" id="modalTitle" data-i18n="conversion_details">转换详情</h2>
|
||||||
subtitle: '数据格式检测、转换与智能推荐系统',
|
<button class="close-btn" onclick="closeDetailsModal()">×</button>
|
||||||
github: 'Github',
|
</div>
|
||||||
data_input: '数据输入区',
|
<div class="modal-body" id="modalBody">
|
||||||
input_format: '输入格式选择:',
|
<!-- 详情内容将在这里动态生成 -->
|
||||||
byte_format: 'byte[] (0-255)',
|
</div>
|
||||||
ushort_format: 'ushort[] (0-65535)',
|
</div>
|
||||||
short_format: 'short[] (-32768 to 32767)',
|
</div>
|
||||||
int_format: 'int[] (-2147483648 to 2147483647)',
|
|
||||||
uint_format: 'uint[] (0 to 4294967295)',
|
|
||||||
hex_format: 'Hex 字符串',
|
|
||||||
raw_data_input: '原始数据输入:',
|
|
||||||
raw_data_placeholder: '示例:[13312,0,16128,0,13312,0,8704,0,5888,0]\n或者:0x3400,0x0000,0x3F00,0x0000\n支持逗号、空格、换行分隔',
|
|
||||||
intelligent_analysis: '智能分析',
|
|
||||||
parse_data: '解析数据',
|
|
||||||
clear: '清空',
|
|
||||||
smart_detection_result: '智能检测结果',
|
|
||||||
conversion_control: '转换控制',
|
|
||||||
auto_select_notice: '已根据智能检测结果自动选择最优配置',
|
|
||||||
target_data_type: '目标数据类型:',
|
|
||||||
show_all_types: '🔄 显示所有类型',
|
|
||||||
bool_type: 'bool (布尔)',
|
|
||||||
byte_type: 'byte (无符号8位)',
|
|
||||||
sbyte_type: 'sbyte (有符号8位)',
|
|
||||||
short_type: 'short (有符号16位)',
|
|
||||||
ushort_type: 'ushort (无符号16位)',
|
|
||||||
int_type: 'int (有符号32位)',
|
|
||||||
uint_type: 'uint (无符号32位)',
|
|
||||||
long_type: 'long (有符号64位)',
|
|
||||||
ulong_type: 'ulong (无符号64位)',
|
|
||||||
float_type: 'float (32位浮点)',
|
|
||||||
double_type: 'double (64位浮点)',
|
|
||||||
ascii_type: 'ASCII 字符串',
|
|
||||||
byte_order_selection: '字节序选择:',
|
|
||||||
show_all_byte_order: '🔄 显示所有字节序',
|
|
||||||
abcd_big_endian: 'ABCD (大端序)',
|
|
||||||
dcba_little_endian: 'DCBA (小端序)',
|
|
||||||
cdab_siemens: 'CDAB (西门子常见)',
|
|
||||||
badc_omron: 'BADC (欧姆龙常见)',
|
|
||||||
encoding_format: '编码格式:',
|
|
||||||
show_all_encoding: '🔄 显示所有编码',
|
|
||||||
dec_encoding: 'DEC (十进制编码)',
|
|
||||||
bcd_encoding: 'BCD (二进制编码十进制)',
|
|
||||||
hex_encoding: 'HEX (十六进制编码)',
|
|
||||||
convert: '转换',
|
|
||||||
smart_recommendation: '智能推荐配置',
|
|
||||||
apply_recommendation: '应用推荐配置',
|
|
||||||
conversion_results: '转换结果',
|
|
||||||
conversion_statistics: '转换统计',
|
|
||||||
intelligent_recommendation: '智能推荐',
|
|
||||||
conversion_results_tab: '转换结果',
|
|
||||||
raw_data: '原始数据',
|
|
||||||
conversion_details: '转换详情',
|
|
||||||
// 统计相关
|
|
||||||
total_conversions: '总转换数',
|
|
||||||
successful_conversions: '成功转换',
|
|
||||||
failed_conversions: '转换失败',
|
|
||||||
smart_recommendations: '智能推荐',
|
|
||||||
success_rate: '成功率',
|
|
||||||
recommendation_rate: '推荐率',
|
|
||||||
// 智能检测相关
|
|
||||||
encoding_format_detection: '编码格式',
|
|
||||||
byte_order_detection: '字节序',
|
|
||||||
strongly_recommended: '强烈推荐',
|
|
||||||
recommended: '推荐',
|
|
||||||
// 错误消息
|
|
||||||
no_data_input: '请输入数据',
|
|
||||||
invalid_hex_format: '无效的十六进制格式',
|
|
||||||
invalid_number: '无效的数值',
|
|
||||||
data_parsed_successfully: '成功解析',
|
|
||||||
items: '个数据项',
|
|
||||||
value_out_of_range: '数值超出范围',
|
|
||||||
no_data_to_convert: '请先输入并解析数据',
|
|
||||||
no_recommendations: '没有推荐配置可应用',
|
|
||||||
// 智能分析相关
|
|
||||||
bcd_format_match: '字节符合BCD格式',
|
|
||||||
non_bcd_bytes: '存在非BCD格式字节',
|
|
||||||
printable_characters: '字节为可打印字符',
|
|
||||||
ascii_characters: '字节为ASCII字符',
|
|
||||||
low_ascii_ratio: 'ASCII字符比例较低',
|
|
||||||
decimal_always_valid: '十进制编码始终有效',
|
|
||||||
reasonable_floats: '个浮点数转换合理',
|
|
||||||
valid_floats: '个浮点数转换有效',
|
|
||||||
poor_float_conversion: '浮点数转换效果不佳',
|
|
||||||
insufficient_data_length: '数据长度不足以进行浮点数分析',
|
|
||||||
reasonable_16bit_values: '个16位值合理',
|
|
||||||
reasonable_byte_values: '个字节值合理',
|
|
||||||
high_confidence_ascii: 'ASCII字符检测置信度高,推荐字符串解析',
|
|
||||||
high_confidence_bcd: 'BCD编码检测置信度高,推荐无符号整数',
|
|
||||||
best_byteorder_float: '字节序浮点数转换效果最佳',
|
|
||||||
good_16bit_parsing: '16位无符号整数解析效果较好',
|
|
||||||
no_high_confidence: '未发现高置信度的格式特征',
|
|
||||||
// 详情页面相关
|
|
||||||
conversion_result_title: '转换结果',
|
|
||||||
current_config: '当前配置:',
|
|
||||||
data_type: '数据类型',
|
|
||||||
byte_order: '字节序',
|
|
||||||
encoding_format_detail: '编码格式',
|
|
||||||
conversion_result: '转换结果',
|
|
||||||
all_byte_order_tests: '所有字节序格式测试结果',
|
|
||||||
format: '格式',
|
|
||||||
conversion_failed: '转换失败',
|
|
||||||
conversion_principle: '转换原理说明',
|
|
||||||
problem_analysis: '问题分析:',
|
|
||||||
data_analysis: '数据分析:',
|
|
||||||
byte_order_explanation: '字节序说明:',
|
|
||||||
big_endian_desc: '大端序:高字节在前,低字节在后,如 0x12345678 存储为 [12][34][56][78]',
|
|
||||||
little_endian_desc: '小端序:低字节在前,高字节在后,如 0x12345678 存储为 [78][56][34][12]',
|
|
||||||
middle_endian_ab_desc: '中端序:字对交换的大端序,西门子PLC常见',
|
|
||||||
middle_endian_dc_desc: '中端序:字对交换的小端序,欧姆龙PLC常见',
|
|
||||||
memory_layout_analysis: '内存布局分析',
|
|
||||||
original_data: '原始数据',
|
|
||||||
byte_representation: '字节表示',
|
|
||||||
float_conversion_example: '浮点数转换示例',
|
|
||||||
order: '顺序',
|
|
||||||
// 原始数据显示
|
|
||||||
input_data: '输入数据',
|
|
||||||
convert_to_byte_array: '转换为字节数组',
|
|
||||||
hex_representation: '十六进制表示',
|
|
||||||
binary_representation: '二进制表示',
|
|
||||||
// 数据分析文本
|
|
||||||
ushort_to_int_analysis: '每2个16位ushort值被组合成1个32位int值,这可能是因为原始数据是32位整数被拆分读取造成的。',
|
|
||||||
int_to_ushort_analysis: '每个32位int被拆分成2个16位ushort读取,如果读取结果不符合预期,可能存在字节序转换问题。',
|
|
||||||
you_input: '你输入了',
|
|
||||||
values: '值',
|
|
||||||
convert_to: ',转换为',
|
|
||||||
type: '类型。'
|
|
||||||
},
|
|
||||||
en: {
|
|
||||||
title: 'Smart PLC Data Format Converter & Debugger',
|
|
||||||
subtitle: 'Data Format Detection, Conversion & Smart Recommendation System',
|
|
||||||
github: 'Github',
|
|
||||||
data_input: 'Data Input',
|
|
||||||
input_format: 'Input Format:',
|
|
||||||
byte_format: 'byte[] (0-255)',
|
|
||||||
ushort_format: 'ushort[] (0-65535)',
|
|
||||||
short_format: 'short[] (-32768 to 32767)',
|
|
||||||
int_format: 'int[] (-2147483648 to 2147483647)',
|
|
||||||
uint_format: 'uint[] (0 to 4294967295)',
|
|
||||||
hex_format: 'Hex String',
|
|
||||||
raw_data_input: 'Raw Data Input:',
|
|
||||||
raw_data_placeholder: 'Example: [13312,0,16128,0,13312,0,8704,0,5888,0]\nOr: 0x3400,0x0000,0x3F00,0x0000\nSupports comma, space, newline separation',
|
|
||||||
intelligent_analysis: 'Smart Analysis',
|
|
||||||
parse_data: 'Parse Data',
|
|
||||||
clear: 'Clear',
|
|
||||||
smart_detection_result: 'Smart Detection Result',
|
|
||||||
conversion_control: 'Conversion Control',
|
|
||||||
auto_select_notice: 'Optimal configuration automatically selected based on smart detection results',
|
|
||||||
target_data_type: 'Target Data Type:',
|
|
||||||
show_all_types: '🔄 Show All Types',
|
|
||||||
bool_type: 'bool (Boolean)',
|
|
||||||
byte_type: 'byte (Unsigned 8-bit)',
|
|
||||||
sbyte_type: 'sbyte (Signed 8-bit)',
|
|
||||||
short_type: 'short (Signed 16-bit)',
|
|
||||||
ushort_type: 'ushort (Unsigned 16-bit)',
|
|
||||||
int_type: 'int (Signed 32-bit)',
|
|
||||||
uint_type: 'uint (Unsigned 32-bit)',
|
|
||||||
long_type: 'long (Signed 64-bit)',
|
|
||||||
ulong_type: 'ulong (Unsigned 64-bit)',
|
|
||||||
float_type: 'float (32-bit Float)',
|
|
||||||
double_type: 'double (64-bit Double)',
|
|
||||||
ascii_type: 'ASCII String',
|
|
||||||
byte_order_selection: 'Byte Order:',
|
|
||||||
show_all_byte_order: '🔄 Show All Byte Orders',
|
|
||||||
abcd_big_endian: 'ABCD (Big Endian)',
|
|
||||||
dcba_little_endian: 'DCBA (Little Endian)',
|
|
||||||
cdab_siemens: 'CDAB (Siemens Common)',
|
|
||||||
badc_omron: 'BADC (Omron Common)',
|
|
||||||
encoding_format: 'Encoding Format:',
|
|
||||||
show_all_encoding: '🔄 Show All Encodings',
|
|
||||||
dec_encoding: 'DEC (Decimal Encoding)',
|
|
||||||
bcd_encoding: 'BCD (Binary Coded Decimal)',
|
|
||||||
hex_encoding: 'HEX (Hexadecimal Encoding)',
|
|
||||||
convert: 'Convert',
|
|
||||||
smart_recommendation: 'Smart Recommendations',
|
|
||||||
apply_recommendation: 'Apply Recommendation',
|
|
||||||
conversion_results: 'Conversion Results',
|
|
||||||
conversion_statistics: 'Conversion Statistics',
|
|
||||||
intelligent_recommendation: 'Smart Recommendations',
|
|
||||||
conversion_results_tab: 'Results',
|
|
||||||
raw_data: 'Raw Data',
|
|
||||||
conversion_details: 'Conversion Details',
|
|
||||||
// 统计相关
|
|
||||||
total_conversions: 'Total',
|
|
||||||
successful_conversions: 'Success',
|
|
||||||
failed_conversions: 'Failed',
|
|
||||||
smart_recommendations: 'Recommended',
|
|
||||||
success_rate: 'Success Rate',
|
|
||||||
recommendation_rate: 'Rec. Rate',
|
|
||||||
// 智能检测相关
|
|
||||||
encoding_format_detection: 'Encoding',
|
|
||||||
byte_order_detection: 'Byte Order',
|
|
||||||
strongly_recommended: 'Strongly Recommended',
|
|
||||||
recommended: 'Recommended',
|
|
||||||
// 错误消息
|
|
||||||
no_data_input: 'Please input data',
|
|
||||||
invalid_hex_format: 'Invalid hexadecimal format',
|
|
||||||
invalid_number: 'Invalid number',
|
|
||||||
data_parsed_successfully: 'Successfully parsed',
|
|
||||||
items: 'data items',
|
|
||||||
value_out_of_range: 'Value out of range',
|
|
||||||
no_data_to_convert: 'Please input and parse data first',
|
|
||||||
no_recommendations: 'No recommendation available',
|
|
||||||
// 智能分析相关
|
|
||||||
bcd_format_match: 'bytes match BCD format',
|
|
||||||
non_bcd_bytes: 'Non-BCD format bytes exist',
|
|
||||||
printable_characters: 'bytes are printable characters',
|
|
||||||
ascii_characters: 'bytes are ASCII characters',
|
|
||||||
low_ascii_ratio: 'Low ASCII character ratio',
|
|
||||||
decimal_always_valid: 'Decimal encoding is always valid',
|
|
||||||
reasonable_floats: 'float conversions are reasonable',
|
|
||||||
valid_floats: 'float conversions are valid',
|
|
||||||
poor_float_conversion: 'Poor float conversion performance',
|
|
||||||
insufficient_data_length: 'Insufficient data length for float analysis',
|
|
||||||
reasonable_16bit_values: '16-bit values are reasonable',
|
|
||||||
reasonable_byte_values: 'byte values are reasonable',
|
|
||||||
high_confidence_ascii: 'High confidence ASCII detection, recommend string parsing',
|
|
||||||
high_confidence_bcd: 'High confidence BCD detection, recommend unsigned integer',
|
|
||||||
best_byteorder_float: 'byte order has best float conversion',
|
|
||||||
good_16bit_parsing: '16-bit unsigned integer parsing works well',
|
|
||||||
no_high_confidence: 'No high-confidence format features found',
|
|
||||||
// 详情页面相关
|
|
||||||
conversion_result_title: 'Conversion Result',
|
|
||||||
current_config: 'Current Configuration:',
|
|
||||||
data_type: 'Data Type',
|
|
||||||
byte_order: 'Byte Order',
|
|
||||||
encoding_format_detail: 'Encoding',
|
|
||||||
conversion_result: 'Result',
|
|
||||||
all_byte_order_tests: 'All Byte Order Test Results',
|
|
||||||
format: 'Format',
|
|
||||||
conversion_failed: 'Conversion Failed',
|
|
||||||
conversion_principle: 'Conversion Principles',
|
|
||||||
problem_analysis: 'Analysis:',
|
|
||||||
data_analysis: 'Data Analysis:',
|
|
||||||
byte_order_explanation: 'Byte Order Explanation:',
|
|
||||||
big_endian_desc: 'Big Endian: High byte first, e.g., 0x12345678 stored as [12][34][56][78]',
|
|
||||||
little_endian_desc: 'Little Endian: Low byte first, e.g., 0x12345678 stored as [78][56][34][12]',
|
|
||||||
middle_endian_ab_desc: 'Middle Endian: Word-swapped big endian, common in Siemens PLCs',
|
|
||||||
middle_endian_dc_desc: 'Middle Endian: Word-swapped little endian, common in Omron PLCs',
|
|
||||||
memory_layout_analysis: 'Memory Layout Analysis',
|
|
||||||
original_data: 'Original Data',
|
|
||||||
byte_representation: 'Byte Representation',
|
|
||||||
float_conversion_example: 'Float Conversion Example',
|
|
||||||
order: 'Order',
|
|
||||||
// 原始数据显示
|
|
||||||
input_data: 'Input Data',
|
|
||||||
convert_to_byte_array: 'Convert to Byte Array',
|
|
||||||
hex_representation: 'Hex Representation',
|
|
||||||
binary_representation: 'Binary Representation',
|
|
||||||
// 数据分析文本
|
|
||||||
ushort_to_int_analysis: 'Every 2 16-bit ushort values are combined into one 32-bit int value, possibly due to 32-bit integers being split during reading.',
|
|
||||||
int_to_ushort_analysis: 'Each 32-bit int is split into 2 16-bit ushorts. If results don\'t meet expectations, byte order issues may exist.',
|
|
||||||
you_input: 'You input ',
|
|
||||||
values: ' values: [',
|
|
||||||
convert_to: '], converting to ',
|
|
||||||
type: ' type.'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 当前语言
|
<script>
|
||||||
let currentLanguage = 'zh';
|
// 国际化语言包
|
||||||
|
const i18n = {
|
||||||
// 切换语言
|
zh: {
|
||||||
function switchLanguage(lang) {
|
title: 'PLC数据格式转换与调试工具',
|
||||||
currentLanguage = lang;
|
subtitle: '数据格式检测、转换与智能推荐系统',
|
||||||
|
github: 'Github',
|
||||||
// 更新按钮状态
|
data_input: '数据输入区',
|
||||||
document.querySelectorAll('.lang-btn').forEach(btn => {
|
input_format: '输入格式选择:',
|
||||||
btn.classList.remove('active');
|
byte_format: 'byte[] (0-255)',
|
||||||
if (btn.getAttribute('data-lang') === lang) {
|
ushort_format: 'ushort[] (0-65535)',
|
||||||
btn.classList.add('active');
|
short_format: 'short[] (-32768 to 32767)',
|
||||||
}
|
int_format: 'int[] (-2147483648 to 2147483647)',
|
||||||
});
|
uint_format: 'uint[] (0 to 4294967295)',
|
||||||
|
hex_format: 'Hex 字符串',
|
||||||
// 更新页面语言属性
|
raw_data_input: '原始数据输入:',
|
||||||
document.documentElement.lang = lang === 'zh' ? 'zh-CN' : 'en';
|
raw_data_placeholder: '示例:[13312,0,16128,0,13312,0,8704,0,5888,0]\n或者:0x3400,0x0000,0x3F00,0x0000\n支持逗号、空格、换行分隔',
|
||||||
|
intelligent_analysis: '智能分析',
|
||||||
// 更新页面标题
|
parse_data: '解析数据',
|
||||||
document.title = i18n[lang].title;
|
clear: '清空',
|
||||||
|
smart_detection_result: '智能检测结果',
|
||||||
// 更新所有国际化文本
|
conversion_control: '转换控制',
|
||||||
updateI18nTexts();
|
auto_select_notice: '已根据智能检测结果自动选择最优配置',
|
||||||
|
target_data_type: '目标数据类型:',
|
||||||
// 保存语言设置
|
show_all_types: '🔄 显示所有类型',
|
||||||
try {
|
bool_type: 'bool (布尔)',
|
||||||
localStorage.setItem('plc-tool-lang', lang);
|
byte_type: 'byte (无符号8位)',
|
||||||
} catch (e) {
|
sbyte_type: 'sbyte (有符号8位)',
|
||||||
// 忽略localStorage错误
|
short_type: 'short (有符号16位)',
|
||||||
}
|
ushort_type: 'ushort (无符号16位)',
|
||||||
|
int_type: 'int (有符号32位)',
|
||||||
// 如果有检测结果,重新显示(因为需要更新语言)
|
uint_type: 'uint (无符号32位)',
|
||||||
if (Object.keys(smartAnalysisResults).length > 0) {
|
long_type: 'long (有符号64位)',
|
||||||
displaySmartDetection();
|
ulong_type: 'ulong (无符号64位)',
|
||||||
displayRecommendations();
|
float_type: 'float (32位浮点)',
|
||||||
}
|
double_type: 'double (64位浮点)',
|
||||||
|
ascii_type: 'ASCII 字符串',
|
||||||
// 如果有转换结果,重新显示统计信息
|
byte_order_selection: '字节序选择:',
|
||||||
if (allResults.length > 0) {
|
show_all_byte_order: '🔄 显示所有字节序',
|
||||||
const statistics = calculateStatistics();
|
abcd_big_endian: 'ABCD (大端序)',
|
||||||
displayStatistics(statistics);
|
dcba_little_endian: 'DCBA (小端序)',
|
||||||
}
|
cdab_siemens: 'CDAB (西门子常见)',
|
||||||
|
badc_omron: 'BADC (欧姆龙常见)',
|
||||||
|
encoding_format: '编码格式:',
|
||||||
|
show_all_encoding: '🔄 显示所有编码',
|
||||||
|
dec_encoding: 'DEC (十进制编码)',
|
||||||
|
bcd_encoding: 'BCD (二进制编码十进制)',
|
||||||
|
hex_encoding: 'HEX (十六进制编码)',
|
||||||
|
convert: '转换',
|
||||||
|
smart_recommendation: '智能推荐配置',
|
||||||
|
apply_recommendation: '应用推荐配置',
|
||||||
|
conversion_results: '转换结果',
|
||||||
|
conversion_statistics: '转换统计',
|
||||||
|
intelligent_recommendation: '智能推荐',
|
||||||
|
conversion_results_tab: '转换结果',
|
||||||
|
raw_data: '原始数据',
|
||||||
|
conversion_details: '转换详情',
|
||||||
|
// 统计相关
|
||||||
|
total_conversions: '总转换数',
|
||||||
|
successful_conversions: '成功转换',
|
||||||
|
failed_conversions: '转换失败',
|
||||||
|
smart_recommendations: '智能推荐',
|
||||||
|
success_rate: '成功率',
|
||||||
|
recommendation_rate: '推荐率',
|
||||||
|
// 智能检测相关
|
||||||
|
encoding_format_detection: '编码格式',
|
||||||
|
byte_order_detection: '字节序',
|
||||||
|
strongly_recommended: '强烈推荐',
|
||||||
|
recommended: '推荐',
|
||||||
|
// 错误消息
|
||||||
|
no_data_input: '请输入数据',
|
||||||
|
invalid_hex_format: '无效的十六进制格式',
|
||||||
|
invalid_number: '无效的数值',
|
||||||
|
data_parsed_successfully: '成功解析',
|
||||||
|
items: '个数据项',
|
||||||
|
value_out_of_range: '数值超出范围',
|
||||||
|
no_data_to_convert: '请先输入并解析数据',
|
||||||
|
no_recommendations: '没有推荐配置可应用',
|
||||||
|
// 智能分析相关
|
||||||
|
bcd_format_match: '字节符合BCD格式',
|
||||||
|
non_bcd_bytes: '存在非BCD格式字节',
|
||||||
|
printable_characters: '字节为可打印字符',
|
||||||
|
ascii_characters: '字节为ASCII字符',
|
||||||
|
low_ascii_ratio: 'ASCII字符比例较低',
|
||||||
|
decimal_always_valid: '十进制编码始终有效',
|
||||||
|
reasonable_floats: '个浮点数转换合理',
|
||||||
|
valid_floats: '个浮点数转换有效',
|
||||||
|
poor_float_conversion: '浮点数转换效果不佳',
|
||||||
|
insufficient_data_length: '数据长度不足以进行浮点数分析',
|
||||||
|
reasonable_16bit_values: '个16位值合理',
|
||||||
|
reasonable_byte_values: '个字节值合理',
|
||||||
|
high_confidence_ascii: 'ASCII字符检测置信度高,推荐字符串解析',
|
||||||
|
high_confidence_bcd: 'BCD编码检测置信度高,推荐无符号整数',
|
||||||
|
best_byteorder_float: '字节序浮点数转换效果最佳',
|
||||||
|
good_16bit_parsing: '16位无符号整数解析效果较好',
|
||||||
|
no_high_confidence: '未发现高置信度的格式特征',
|
||||||
|
// 详情页面相关
|
||||||
|
conversion_result_title: '转换结果',
|
||||||
|
current_config: '当前配置:',
|
||||||
|
data_type: '数据类型',
|
||||||
|
byte_order: '字节序',
|
||||||
|
encoding_format_detail: '编码格式',
|
||||||
|
conversion_result: '转换结果',
|
||||||
|
all_byte_order_tests: '所有字节序格式测试结果',
|
||||||
|
format: '格式',
|
||||||
|
conversion_failed: '转换失败',
|
||||||
|
conversion_principle: '转换原理说明',
|
||||||
|
problem_analysis: '问题分析:',
|
||||||
|
data_analysis: '数据分析:',
|
||||||
|
byte_order_explanation: '字节序说明:',
|
||||||
|
big_endian_desc: '大端序:高字节在前,低字节在后,如 0x12345678 存储为 [12][34][56][78]',
|
||||||
|
little_endian_desc: '小端序:低字节在前,高字节在后,如 0x12345678 存储为 [78][56][34][12]',
|
||||||
|
middle_endian_ab_desc: '中端序:字对交换的大端序,西门子PLC常见',
|
||||||
|
middle_endian_dc_desc: '中端序:字对交换的小端序,欧姆龙PLC常见',
|
||||||
|
memory_layout_analysis: '内存布局分析',
|
||||||
|
original_data: '原始数据',
|
||||||
|
byte_representation: '字节表示',
|
||||||
|
float_conversion_example: '浮点数转换示例',
|
||||||
|
order: '顺序',
|
||||||
|
// 原始数据显示
|
||||||
|
input_data: '输入数据',
|
||||||
|
convert_to_byte_array: '转换为字节数组',
|
||||||
|
hex_representation: '十六进制表示',
|
||||||
|
binary_representation: '二进制表示',
|
||||||
|
// 数据分析文本
|
||||||
|
ushort_to_int_analysis: '每2个16位ushort值被组合成1个32位int值,这可能是因为原始数据是32位整数被拆分读取造成的。',
|
||||||
|
int_to_ushort_analysis: '每个32位int被拆分成2个16位ushort读取,如果读取结果不符合预期,可能存在字节序转换问题。',
|
||||||
|
you_input: '你输入了',
|
||||||
|
values: '值',
|
||||||
|
convert_to: ',转换为',
|
||||||
|
type: '类型。',
|
||||||
|
// 步骤相关
|
||||||
|
step_convert_process: '详细转换步骤',
|
||||||
|
step_parse_input: '解析输入数据',
|
||||||
|
step_convert_bytes: '转换为字节数组',
|
||||||
|
step_apply_encoding: '应用编码格式',
|
||||||
|
step_apply_byteorder: '应用字节序',
|
||||||
|
step_parse_datatype: '按数据类型解析',
|
||||||
|
step_final_result: '最终结果',
|
||||||
|
encoding_dec_desc: '十进制编码,直接使用原始字节值',
|
||||||
|
encoding_bcd_desc: 'BCD编码,每个字节表示两位十进制数',
|
||||||
|
encoding_hex_desc: '十六进制编码,字节值按16进制解释',
|
||||||
|
parse_as: '解析为',
|
||||||
|
bytes_combined: '个字节组合',
|
||||||
|
value_calculation: '数值计算'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'Smart PLC Data Format Converter & Debugger',
|
||||||
|
subtitle: 'Data Format Detection, Conversion & Smart Recommendation System',
|
||||||
|
github: 'Github',
|
||||||
|
data_input: 'Data Input',
|
||||||
|
input_format: 'Input Format:',
|
||||||
|
byte_format: 'byte[] (0-255)',
|
||||||
|
ushort_format: 'ushort[] (0-65535)',
|
||||||
|
short_format: 'short[] (-32768 to 32767)',
|
||||||
|
int_format: 'int[] (-2147483648 to 2147483647)',
|
||||||
|
uint_format: 'uint[] (0 to 4294967295)',
|
||||||
|
hex_format: 'Hex String',
|
||||||
|
raw_data_input: 'Raw Data Input:',
|
||||||
|
raw_data_placeholder: 'Example: [13312,0,16128,0,13312,0,8704,0,5888,0]\nOr: 0x3400,0x0000,0x3F00,0x0000\nSupports comma, space, newline separation',
|
||||||
|
intelligent_analysis: 'Smart Analysis',
|
||||||
|
parse_data: 'Parse Data',
|
||||||
|
clear: 'Clear',
|
||||||
|
smart_detection_result: 'Smart Detection Result',
|
||||||
|
conversion_control: 'Conversion Control',
|
||||||
|
auto_select_notice: 'Optimal configuration automatically selected based on smart detection results',
|
||||||
|
target_data_type: 'Target Data Type:',
|
||||||
|
show_all_types: '🔄 Show All Types',
|
||||||
|
bool_type: 'bool (Boolean)',
|
||||||
|
byte_type: 'byte (Unsigned 8-bit)',
|
||||||
|
sbyte_type: 'sbyte (Signed 8-bit)',
|
||||||
|
short_type: 'short (Signed 16-bit)',
|
||||||
|
ushort_type: 'ushort (Unsigned 16-bit)',
|
||||||
|
int_type: 'int (Signed 32-bit)',
|
||||||
|
uint_type: 'uint (Unsigned 32-bit)',
|
||||||
|
long_type: 'long (Signed 64-bit)',
|
||||||
|
ulong_type: 'ulong (Unsigned 64-bit)',
|
||||||
|
float_type: 'float (32-bit Float)',
|
||||||
|
double_type: 'double (64-bit Double)',
|
||||||
|
ascii_type: 'ASCII String',
|
||||||
|
byte_order_selection: 'Byte Order:',
|
||||||
|
show_all_byte_order: '🔄 Show All Byte Orders',
|
||||||
|
abcd_big_endian: 'ABCD (Big Endian)',
|
||||||
|
dcba_little_endian: 'DCBA (Little Endian)',
|
||||||
|
cdab_siemens: 'CDAB (Siemens Common)',
|
||||||
|
badc_omron: 'BADC (Omron Common)',
|
||||||
|
encoding_format: 'Encoding Format:',
|
||||||
|
show_all_encoding: '🔄 Show All Encodings',
|
||||||
|
dec_encoding: 'DEC (Decimal Encoding)',
|
||||||
|
bcd_encoding: 'BCD (Binary Coded Decimal)',
|
||||||
|
hex_encoding: 'HEX (Hexadecimal Encoding)',
|
||||||
|
convert: 'Convert',
|
||||||
|
smart_recommendation: 'Smart Recommendations',
|
||||||
|
apply_recommendation: 'Apply Recommendation',
|
||||||
|
conversion_results: 'Conversion Results',
|
||||||
|
conversion_statistics: 'Conversion Statistics',
|
||||||
|
intelligent_recommendation: 'Smart Recommendations',
|
||||||
|
conversion_results_tab: 'Results',
|
||||||
|
raw_data: 'Raw Data',
|
||||||
|
conversion_details: 'Conversion Details',
|
||||||
|
// 统计相关
|
||||||
|
total_conversions: 'Total',
|
||||||
|
successful_conversions: 'Success',
|
||||||
|
failed_conversions: 'Failed',
|
||||||
|
smart_recommendations: 'Recommended',
|
||||||
|
success_rate: 'Success Rate',
|
||||||
|
recommendation_rate: 'Rec. Rate',
|
||||||
|
// 智能检测相关
|
||||||
|
encoding_format_detection: 'Encoding',
|
||||||
|
byte_order_detection: 'Byte Order',
|
||||||
|
strongly_recommended: 'Strongly Recommended',
|
||||||
|
recommended: 'Recommended',
|
||||||
|
// 错误消息
|
||||||
|
no_data_input: 'Please input data',
|
||||||
|
invalid_hex_format: 'Invalid hexadecimal format',
|
||||||
|
invalid_number: 'Invalid number',
|
||||||
|
data_parsed_successfully: 'Successfully parsed',
|
||||||
|
items: 'data items',
|
||||||
|
value_out_of_range: 'Value out of range',
|
||||||
|
no_data_to_convert: 'Please input and parse data first',
|
||||||
|
no_recommendations: 'No recommendation available',
|
||||||
|
// 智能分析相关
|
||||||
|
bcd_format_match: 'bytes match BCD format',
|
||||||
|
non_bcd_bytes: 'Non-BCD format bytes exist',
|
||||||
|
printable_characters: 'bytes are printable characters',
|
||||||
|
ascii_characters: 'bytes are ASCII characters',
|
||||||
|
low_ascii_ratio: 'Low ASCII character ratio',
|
||||||
|
decimal_always_valid: 'Decimal encoding is always valid',
|
||||||
|
reasonable_floats: 'float conversions are reasonable',
|
||||||
|
valid_floats: 'float conversions are valid',
|
||||||
|
poor_float_conversion: 'Poor float conversion performance',
|
||||||
|
insufficient_data_length: 'Insufficient data length for float analysis',
|
||||||
|
reasonable_16bit_values: '16-bit values are reasonable',
|
||||||
|
reasonable_byte_values: 'byte values are reasonable',
|
||||||
|
high_confidence_ascii: 'High confidence ASCII detection, recommend string parsing',
|
||||||
|
high_confidence_bcd: 'High confidence BCD detection, recommend unsigned integer',
|
||||||
|
best_byteorder_float: 'byte order has best float conversion',
|
||||||
|
good_16bit_parsing: '16-bit unsigned integer parsing works well',
|
||||||
|
no_high_confidence: 'No high-confidence format features found',
|
||||||
|
// 详情页面相关
|
||||||
|
conversion_result_title: 'Conversion Result',
|
||||||
|
current_config: 'Current Configuration:',
|
||||||
|
data_type: 'Data Type',
|
||||||
|
byte_order: 'Byte Order',
|
||||||
|
encoding_format_detail: 'Encoding',
|
||||||
|
conversion_result: 'Result',
|
||||||
|
all_byte_order_tests: 'All Byte Order Test Results',
|
||||||
|
format: 'Format',
|
||||||
|
conversion_failed: 'Conversion Failed',
|
||||||
|
conversion_principle: 'Conversion Principles',
|
||||||
|
problem_analysis: 'Analysis:',
|
||||||
|
data_analysis: 'Data Analysis:',
|
||||||
|
byte_order_explanation: 'Byte Order Explanation:',
|
||||||
|
big_endian_desc: 'Big Endian: High byte first, e.g., 0x12345678 stored as [12][34][56][78]',
|
||||||
|
little_endian_desc: 'Little Endian: Low byte first, e.g., 0x12345678 stored as [78][56][34][12]',
|
||||||
|
middle_endian_ab_desc: 'Middle Endian: Word-swapped big endian, common in Siemens PLCs',
|
||||||
|
middle_endian_dc_desc: 'Middle Endian: Word-swapped little endian, common in Omron PLCs',
|
||||||
|
memory_layout_analysis: 'Memory Layout Analysis',
|
||||||
|
original_data: 'Original Data',
|
||||||
|
byte_representation: 'Byte Representation',
|
||||||
|
float_conversion_example: 'Float Conversion Example',
|
||||||
|
order: 'Order',
|
||||||
|
// 原始数据显示
|
||||||
|
input_data: 'Input Data',
|
||||||
|
convert_to_byte_array: 'Convert to Byte Array',
|
||||||
|
hex_representation: 'Hex Representation',
|
||||||
|
binary_representation: 'Binary Representation',
|
||||||
|
// 数据分析文本
|
||||||
|
ushort_to_int_analysis: 'Every 2 16-bit ushort values are combined into one 32-bit int value, possibly due to 32-bit integers being split during reading.',
|
||||||
|
int_to_ushort_analysis: 'Each 32-bit int is split into 2 16-bit ushorts. If results don\'t meet expectations, byte order issues may exist.',
|
||||||
|
you_input: 'You input ',
|
||||||
|
values: ' values: [',
|
||||||
|
convert_to: '], converting to ',
|
||||||
|
type: ' type.',
|
||||||
|
// 步骤相关
|
||||||
|
step_convert_process: 'Detailed Conversion Steps',
|
||||||
|
step_parse_input: 'Parse Input Data',
|
||||||
|
step_convert_bytes: 'Convert to Byte Array',
|
||||||
|
step_apply_encoding: 'Apply Encoding Format',
|
||||||
|
step_apply_byteorder: 'Apply Byte Order',
|
||||||
|
step_parse_datatype: 'Parse by Data Type',
|
||||||
|
step_final_result: 'Final Result',
|
||||||
|
encoding_dec_desc: 'Decimal encoding, using raw byte values directly',
|
||||||
|
encoding_bcd_desc: 'BCD encoding, each byte represents two decimal digits',
|
||||||
|
encoding_hex_desc: 'Hexadecimal encoding, bytes interpreted as hex',
|
||||||
|
parse_as: 'Parse as',
|
||||||
|
bytes_combined: 'bytes combined',
|
||||||
|
value_calculation: 'Value calculation'
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 更新国际化文本
|
// 当前语言
|
||||||
function updateI18nTexts() {
|
let currentLanguage = 'zh';
|
||||||
const texts = i18n[currentLanguage];
|
|
||||||
|
|
||||||
// 更新所有带有 data-i18n 属性的元素
|
|
||||||
document.querySelectorAll('[data-i18n]').forEach(element => {
|
|
||||||
const key = element.getAttribute('data-i18n');
|
|
||||||
if (texts[key]) {
|
|
||||||
element.textContent = texts[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新带有 data-i18n-placeholder 属性的元素
|
|
||||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
|
|
||||||
const key = element.getAttribute('data-i18n-placeholder');
|
|
||||||
if (texts[key]) {
|
|
||||||
element.placeholder = texts[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取国际化文本
|
// 切换语言
|
||||||
function t(key, ...args) {
|
function switchLanguage(lang) {
|
||||||
let text = i18n[currentLanguage][key] || key;
|
currentLanguage = lang;
|
||||||
|
|
||||||
// 如果有参数,进行字符串替换
|
// 更新按钮状态
|
||||||
if (args.length > 0) {
|
document.querySelectorAll('.lang-btn').forEach(btn => {
|
||||||
args.forEach((arg, index) => {
|
btn.classList.remove('active');
|
||||||
text = text.replace(`{${index}}`, arg);
|
if (btn.getAttribute('data-lang') === lang) {
|
||||||
});
|
btn.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面加载时初始化语言
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// 尝试从localStorage加载语言设置
|
|
||||||
let savedLang = 'zh';
|
|
||||||
try {
|
|
||||||
savedLang = localStorage.getItem('plc-tool-lang') || 'zh';
|
|
||||||
} catch (e) {
|
|
||||||
// 忽略localStorage错误
|
|
||||||
}
|
|
||||||
|
|
||||||
switchLanguage(savedLang);
|
|
||||||
|
|
||||||
// 设置默认数据
|
|
||||||
document.getElementById('rawData').value = '[13312,0,16128,0,13312,0,8704,0,5888,0]';
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 原有的所有JavaScript代码保持不变,只需要修改显示文本的部分
|
// 更新页面语言属性
|
||||||
let currentData = [];
|
document.documentElement.lang = lang === 'zh' ? 'zh-CN' : 'en';
|
||||||
let currentInputFormat = 'ushort';
|
|
||||||
let allResults = [];
|
// 更新页面标题
|
||||||
let smartAnalysisResults = {};
|
document.title = i18n[lang].title;
|
||||||
let recommendedConfigs = [];
|
|
||||||
|
// 更新所有国际化文本
|
||||||
// 智能分析函数
|
updateI18nTexts();
|
||||||
function intelligentAnalysis() {
|
|
||||||
if (!parseInput(false)) {
|
// 保存语言设置
|
||||||
return;
|
try {
|
||||||
}
|
localStorage.setItem('plc-tool-lang', lang);
|
||||||
|
} catch (e) {
|
||||||
// 清空之前的结果
|
// 忽略localStorage错误
|
||||||
clearPreviousResults();
|
}
|
||||||
|
|
||||||
// 执行智能检测
|
// 如果有检测结果,重新显示(因为需要更新语言)
|
||||||
smartAnalysisResults = performSmartDetection();
|
if (Object.keys(smartAnalysisResults).length > 0) {
|
||||||
|
|
||||||
// 显示检测结果
|
|
||||||
displaySmartDetection();
|
displaySmartDetection();
|
||||||
|
displayRecommendations();
|
||||||
// 生成推荐配置
|
|
||||||
generateRecommendations();
|
|
||||||
|
|
||||||
// 自动应用最佳配置
|
|
||||||
autoApplyBestConfig();
|
|
||||||
|
|
||||||
// 自动转换并显示结果
|
|
||||||
convertData(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动应用最佳配置
|
// 如果有转换结果,重新显示统计信息
|
||||||
function autoApplyBestConfig() {
|
if (allResults.length > 0) {
|
||||||
let bestEncoding = null;
|
const statistics = calculateStatistics();
|
||||||
let bestEncodingScore = 0;
|
displayStatistics(statistics);
|
||||||
let bestByteOrder = null;
|
|
||||||
let bestByteOrderScore = 0;
|
|
||||||
let bestDataType = null;
|
|
||||||
let autoSelected = false;
|
|
||||||
|
|
||||||
// 找出最佳编码格式(只选择高置信度的)
|
|
||||||
for (let [format, result] of Object.entries(smartAnalysisResults.encoding)) {
|
|
||||||
if (result.confidence === 'high' && result.score > bestEncodingScore) {
|
|
||||||
bestEncoding = format;
|
|
||||||
bestEncodingScore = result.score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找出最佳字节序(只选择高置信度的)
|
|
||||||
for (let [order, result] of Object.entries(smartAnalysisResults.byteOrder)) {
|
|
||||||
if (result.confidence === 'high' && result.score > bestByteOrderScore) {
|
|
||||||
bestByteOrder = order;
|
|
||||||
bestByteOrderScore = result.score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据推荐配置确定最佳数据类型
|
|
||||||
if (recommendedConfigs.length > 0 && recommendedConfigs[0].confidence === 'high') {
|
|
||||||
bestDataType = recommendedConfigs[0].dataType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 应用高置信度的配置
|
|
||||||
if (bestEncoding && bestEncodingScore > 80) {
|
|
||||||
document.getElementById('encoding').value = bestEncoding;
|
|
||||||
autoSelected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestByteOrder && bestByteOrderScore > 70) {
|
|
||||||
document.getElementById('byteOrder').value = bestByteOrder;
|
|
||||||
autoSelected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestDataType) {
|
|
||||||
document.getElementById('targetType').value = bestDataType;
|
|
||||||
autoSelected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示自动选择提示
|
|
||||||
if (autoSelected) {
|
|
||||||
document.getElementById('autoSelectNotice').style.display = 'flex';
|
|
||||||
setTimeout(() => {
|
|
||||||
document.getElementById('autoSelectNotice').style.display = 'none';
|
|
||||||
}, 5000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新国际化文本
|
||||||
|
function updateI18nTexts() {
|
||||||
|
const texts = i18n[currentLanguage];
|
||||||
|
|
||||||
|
// 更新所有带有 data-i18n 属性的元素
|
||||||
|
document.querySelectorAll('[data-i18n]').forEach(element => {
|
||||||
|
const key = element.getAttribute('data-i18n');
|
||||||
|
if (texts[key]) {
|
||||||
|
element.textContent = texts[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新带有 data-i18n-placeholder 属性的元素
|
||||||
|
document.querySelectorAll('[data-i18n-placeholder]').forEach(element => {
|
||||||
|
const key = element.getAttribute('data-i18n-placeholder');
|
||||||
|
if (texts[key]) {
|
||||||
|
element.placeholder = texts[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取国际化文本
|
||||||
|
function t(key, ...args) {
|
||||||
|
let text = i18n[currentLanguage][key] || key;
|
||||||
|
|
||||||
|
// 如果有参数,进行字符串替换
|
||||||
|
if (args.length > 0) {
|
||||||
|
args.forEach((arg, index) => {
|
||||||
|
text = text.replace(`{${index}}`, arg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载时初始化语言
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
// 尝试从localStorage加载语言设置
|
||||||
|
let savedLang = 'zh';
|
||||||
|
try {
|
||||||
|
savedLang = localStorage.getItem('plc-tool-lang') || 'zh';
|
||||||
|
} catch (e) {
|
||||||
|
// 忽略localStorage错误
|
||||||
|
}
|
||||||
|
|
||||||
|
switchLanguage(savedLang);
|
||||||
|
|
||||||
|
// 设置默认数据
|
||||||
|
document.getElementById('rawData').value = '[13312,0,16128,0,13312,0,8704,0,5888,0]';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 原有的所有JavaScript代码保持不变,只需要修改显示文本的部分
|
||||||
|
let currentData = [];
|
||||||
|
let currentInputFormat = 'ushort';
|
||||||
|
let allResults = [];
|
||||||
|
let smartAnalysisResults = {};
|
||||||
|
let recommendedConfigs = [];
|
||||||
|
|
||||||
|
// 智能分析函数
|
||||||
|
function intelligentAnalysis() {
|
||||||
|
if (!parseInput(false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空之前的结果
|
||||||
|
clearPreviousResults();
|
||||||
|
|
||||||
// 执行智能检测
|
// 执行智能检测
|
||||||
function performSmartDetection() {
|
smartAnalysisResults = performSmartDetection();
|
||||||
const bytes = convertToBytes(currentData, currentInputFormat);
|
|
||||||
const analysis = {
|
// 显示检测结果
|
||||||
encoding: analyzeEncoding(bytes),
|
displaySmartDetection();
|
||||||
byteOrder: analyzeByteOrder(bytes),
|
|
||||||
dataType: analyzeDataType(bytes),
|
// 生成推荐配置
|
||||||
patterns: analyzePatterns(bytes)
|
generateRecommendations();
|
||||||
};
|
|
||||||
|
// 自动应用最佳配置
|
||||||
return analysis;
|
autoApplyBestConfig();
|
||||||
|
|
||||||
|
// 自动转换并显示结果
|
||||||
|
convertData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动应用最佳配置
|
||||||
|
function autoApplyBestConfig() {
|
||||||
|
let bestEncoding = null;
|
||||||
|
let bestEncodingScore = 0;
|
||||||
|
let bestByteOrder = null;
|
||||||
|
let bestByteOrderScore = 0;
|
||||||
|
let bestDataType = null;
|
||||||
|
let autoSelected = false;
|
||||||
|
|
||||||
|
// 找出最佳编码格式(只选择高置信度的)
|
||||||
|
for (let [format, result] of Object.entries(smartAnalysisResults.encoding)) {
|
||||||
|
if (result.confidence === 'high' && result.score > bestEncodingScore) {
|
||||||
|
bestEncoding = format;
|
||||||
|
bestEncodingScore = result.score;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分析编码格式
|
// 找出最佳字节序(只选择高置信度的)
|
||||||
function analyzeEncoding(bytes) {
|
for (let [order, result] of Object.entries(smartAnalysisResults.byteOrder)) {
|
||||||
const results = {};
|
if (result.confidence === 'high' && result.score > bestByteOrderScore) {
|
||||||
|
bestByteOrder = order;
|
||||||
// BCD检测
|
bestByteOrderScore = result.score;
|
||||||
let bcdValid = true;
|
}
|
||||||
let bcdScore = 0;
|
}
|
||||||
for (let byte of bytes) {
|
|
||||||
const high = (byte >> 4) & 0xF;
|
// 根据推荐配置确定最佳数据类型
|
||||||
const low = byte & 0xF;
|
if (recommendedConfigs.length > 0 && recommendedConfigs[0].confidence === 'high') {
|
||||||
if (high <= 9 && low <= 9) {
|
bestDataType = recommendedConfigs[0].dataType;
|
||||||
bcdScore++;
|
}
|
||||||
} else {
|
|
||||||
bcdValid = false;
|
// 应用高置信度的配置
|
||||||
break;
|
if (bestEncoding && bestEncodingScore > 80) {
|
||||||
|
document.getElementById('encoding').value = bestEncoding;
|
||||||
|
autoSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestByteOrder && bestByteOrderScore > 70) {
|
||||||
|
document.getElementById('byteOrder').value = bestByteOrder;
|
||||||
|
autoSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestDataType) {
|
||||||
|
document.getElementById('targetType').value = bestDataType;
|
||||||
|
autoSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示自动选择提示
|
||||||
|
if (autoSelected) {
|
||||||
|
document.getElementById('autoSelectNotice').style.display = 'flex';
|
||||||
|
setTimeout(() => {
|
||||||
|
document.getElementById('autoSelectNotice').style.display = 'none';
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行智能检测
|
||||||
|
function performSmartDetection() {
|
||||||
|
const bytes = convertToBytes(currentData, currentInputFormat);
|
||||||
|
const analysis = {
|
||||||
|
encoding: analyzeEncoding(bytes),
|
||||||
|
byteOrder: analyzeByteOrder(bytes),
|
||||||
|
dataType: analyzeDataType(bytes),
|
||||||
|
patterns: analyzePatterns(bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
return analysis;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析编码格式
|
||||||
|
function analyzeEncoding(bytes) {
|
||||||
|
const results = {};
|
||||||
|
|
||||||
|
// BCD检测
|
||||||
|
let bcdValid = true;
|
||||||
|
let bcdScore = 0;
|
||||||
|
for (let byte of bytes) {
|
||||||
|
const high = (byte >> 4) & 0xF;
|
||||||
|
const low = byte & 0xF;
|
||||||
|
if (high <= 9 && low <= 9) {
|
||||||
|
bcdScore++;
|
||||||
|
} else {
|
||||||
|
bcdValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bcdValid && bytes.length > 0) {
|
||||||
|
results.BCD = {
|
||||||
|
confidence: bcdScore === bytes.length ? 'high' : 'medium',
|
||||||
|
score: (bcdScore / bytes.length) * 100,
|
||||||
|
reason: `${bcdScore}/${bytes.length} ${t('bcd_format_match')}`
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
results.BCD = {
|
||||||
|
confidence: 'low',
|
||||||
|
score: 0,
|
||||||
|
reason: t('non_bcd_bytes')
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASCII检测
|
||||||
|
let asciiCount = 0;
|
||||||
|
let printableCount = 0;
|
||||||
|
for (let byte of bytes) {
|
||||||
|
if (byte >= 0 && byte <= 127) {
|
||||||
|
asciiCount++;
|
||||||
|
if (byte >= 32 && byte <= 126) {
|
||||||
|
printableCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (bcdValid && bytes.length > 0) {
|
|
||||||
results.BCD = {
|
const asciiRatio = asciiCount / bytes.length;
|
||||||
confidence: bcdScore === bytes.length ? 'high' : 'medium',
|
const printableRatio = printableCount / bytes.length;
|
||||||
score: (bcdScore / bytes.length) * 100,
|
|
||||||
reason: `${bcdScore}/${bytes.length} ${t('bcd_format_match')}`
|
if (printableRatio > 0.7) {
|
||||||
};
|
results.ASCII = {
|
||||||
} else {
|
|
||||||
results.BCD = {
|
|
||||||
confidence: 'low',
|
|
||||||
score: 0,
|
|
||||||
reason: t('non_bcd_bytes')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ASCII检测
|
|
||||||
let asciiCount = 0;
|
|
||||||
let printableCount = 0;
|
|
||||||
for (let byte of bytes) {
|
|
||||||
if (byte >= 0 && byte <= 127) {
|
|
||||||
asciiCount++;
|
|
||||||
if (byte >= 32 && byte <= 126) {
|
|
||||||
printableCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const asciiRatio = asciiCount / bytes.length;
|
|
||||||
const printableRatio = printableCount / bytes.length;
|
|
||||||
|
|
||||||
if (printableRatio > 0.7) {
|
|
||||||
results.ASCII = {
|
|
||||||
confidence: 'high',
|
|
||||||
score: printableRatio * 100,
|
|
||||||
reason: `${printableCount}/${bytes.length} ${t('printable_characters')}`
|
|
||||||
};
|
|
||||||
} else if (asciiRatio > 0.8) {
|
|
||||||
results.ASCII = {
|
|
||||||
confidence: 'medium',
|
|
||||||
score: asciiRatio * 100,
|
|
||||||
reason: `${asciiCount}/${bytes.length} ${t('ascii_characters')}`
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
results.ASCII = {
|
|
||||||
confidence: 'low',
|
|
||||||
score: asciiRatio * 100,
|
|
||||||
reason: t('low_ascii_ratio')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// DEC检测(默认总是可能的)
|
|
||||||
results.DEC = {
|
|
||||||
confidence: 'high',
|
confidence: 'high',
|
||||||
score: 100,
|
score: printableRatio * 100,
|
||||||
reason: t('decimal_always_valid')
|
reason: `${printableCount}/${bytes.length} ${t('printable_characters')}`
|
||||||
|
};
|
||||||
|
} else if (asciiRatio > 0.8) {
|
||||||
|
results.ASCII = {
|
||||||
|
confidence: 'medium',
|
||||||
|
score: asciiRatio * 100,
|
||||||
|
reason: `${asciiCount}/${bytes.length} ${t('ascii_characters')}`
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
results.ASCII = {
|
||||||
|
confidence: 'low',
|
||||||
|
score: asciiRatio * 100,
|
||||||
|
reason: t('low_ascii_ratio')
|
||||||
};
|
};
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分析字节序
|
// DEC检测(默认总是可能的)
|
||||||
function analyzeByteOrder(bytes) {
|
results.DEC = {
|
||||||
const results = {};
|
confidence: 'high',
|
||||||
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
score: 100,
|
||||||
|
reason: t('decimal_always_valid')
|
||||||
// 尝试将字节转换为浮点数,看哪种字节序产生最合理的结果
|
};
|
||||||
for (let byteOrder of byteOrders) {
|
|
||||||
let validFloatCount = 0;
|
return results;
|
||||||
let reasonableFloatCount = 0;
|
}
|
||||||
let totalAttempts = 0;
|
|
||||||
|
// 分析字节序
|
||||||
for (let i = 0; i < bytes.length - 3; i += 4) {
|
function analyzeByteOrder(bytes) {
|
||||||
try {
|
const results = {};
|
||||||
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
||||||
const buffer = new ArrayBuffer(4);
|
|
||||||
const view = new DataView(buffer);
|
// 尝试将字节转换为浮点数,看哪种字节序产生最合理的结果
|
||||||
for (let j = 0; j < 4; j++) {
|
for (let byteOrder of byteOrders) {
|
||||||
view.setUint8(j, orderedBytes[j]);
|
let validFloatCount = 0;
|
||||||
}
|
let reasonableFloatCount = 0;
|
||||||
const floatValue = view.getFloat32(0, true);
|
let totalAttempts = 0;
|
||||||
totalAttempts++;
|
|
||||||
|
for (let i = 0; i < bytes.length - 3; i += 4) {
|
||||||
if (isFinite(floatValue) && !isNaN(floatValue)) {
|
try {
|
||||||
validFloatCount++;
|
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
||||||
// 检查是否为合理的浮点数(不是极大或极小的数)
|
const buffer = new ArrayBuffer(4);
|
||||||
if (Math.abs(floatValue) > 0.0001 && Math.abs(floatValue) < 1000000) {
|
const view = new DataView(buffer);
|
||||||
reasonableFloatCount++;
|
for (let j = 0; j < 4; j++) {
|
||||||
}
|
view.setUint8(j, orderedBytes[j]);
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// 忽略转换错误
|
|
||||||
}
|
}
|
||||||
|
const floatValue = view.getFloat32(0, true);
|
||||||
|
totalAttempts++;
|
||||||
|
|
||||||
|
if (isFinite(floatValue) && !isNaN(floatValue)) {
|
||||||
|
validFloatCount++;
|
||||||
|
// 检查是否为合理的浮点数(不是极大或极小的数)
|
||||||
|
if (Math.abs(floatValue) > 0.0001 && Math.abs(floatValue) < 1000000) {
|
||||||
|
reasonableFloatCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 忽略转换错误
|
||||||
}
|
}
|
||||||
|
}
|
||||||
let confidence = 'low';
|
|
||||||
let score = 0;
|
let confidence = 'low';
|
||||||
let reason = '';
|
let score = 0;
|
||||||
|
let reason = '';
|
||||||
if (totalAttempts > 0) {
|
|
||||||
const validRatio = validFloatCount / totalAttempts;
|
if (totalAttempts > 0) {
|
||||||
const reasonableRatio = reasonableFloatCount / totalAttempts;
|
const validRatio = validFloatCount / totalAttempts;
|
||||||
score = reasonableRatio * 100;
|
const reasonableRatio = reasonableFloatCount / totalAttempts;
|
||||||
|
score = reasonableRatio * 100;
|
||||||
if (reasonableRatio > 0.7) {
|
|
||||||
confidence = 'high';
|
if (reasonableRatio > 0.7) {
|
||||||
reason = `${reasonableFloatCount}/${totalAttempts} ${t('reasonable_floats')}`;
|
confidence = 'high';
|
||||||
} else if (validRatio > 0.5) {
|
reason = `${reasonableFloatCount}/${totalAttempts} ${t('reasonable_floats')}`;
|
||||||
confidence = 'medium';
|
} else if (validRatio > 0.5) {
|
||||||
reason = `${validFloatCount}/${totalAttempts} ${t('valid_floats')}`;
|
confidence = 'medium';
|
||||||
} else {
|
reason = `${validFloatCount}/${totalAttempts} ${t('valid_floats')}`;
|
||||||
reason = t('poor_float_conversion');
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
reason = t('insufficient_data_length');
|
reason = t('poor_float_conversion');
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
results[byteOrder] = {
|
reason = t('insufficient_data_length');
|
||||||
confidence: confidence,
|
|
||||||
score: score,
|
|
||||||
reason: reason
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
results[byteOrder] = {
|
||||||
}
|
confidence: confidence,
|
||||||
|
score: score,
|
||||||
// 分析数据类型
|
reason: reason
|
||||||
function analyzeDataType(bytes) {
|
|
||||||
const results = {};
|
|
||||||
|
|
||||||
// 检查16位整数的合理性
|
|
||||||
let reasonableShortCount = 0;
|
|
||||||
let totalShorts = 0;
|
|
||||||
for (let i = 0; i < bytes.length - 1; i += 2) {
|
|
||||||
const value = bytes[i] | (bytes[i + 1] << 8);
|
|
||||||
totalShorts++;
|
|
||||||
// 检查是否为合理的16位数值范围
|
|
||||||
if (value < 10000 && value > 0) {
|
|
||||||
reasonableShortCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalShorts > 0) {
|
|
||||||
const shortRatio = reasonableShortCount / totalShorts;
|
|
||||||
results.short = {
|
|
||||||
confidence: shortRatio > 0.6 ? 'high' : shortRatio > 0.3 ? 'medium' : 'low',
|
|
||||||
score: shortRatio * 100,
|
|
||||||
reason: `${reasonableShortCount}/${totalShorts} ${t('reasonable_16bit_values')}`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查字节级数据
|
|
||||||
let reasonableByteCount = 0;
|
|
||||||
for (let byte of bytes) {
|
|
||||||
if (byte > 0 && byte < 250) {
|
|
||||||
reasonableByteCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const byteRatio = reasonableByteCount / bytes.length;
|
|
||||||
results.byte = {
|
|
||||||
confidence: byteRatio > 0.8 ? 'high' : byteRatio > 0.5 ? 'medium' : 'low',
|
|
||||||
score: byteRatio * 100,
|
|
||||||
reason: `${reasonableByteCount}/${bytes.length} ${t('reasonable_byte_values')}`
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分析数据模式
|
return results;
|
||||||
function analyzePatterns(bytes) {
|
}
|
||||||
const patterns = {};
|
|
||||||
|
// 分析数据类型
|
||||||
// 检查重复模式
|
function analyzeDataType(bytes) {
|
||||||
const counts = {};
|
const results = {};
|
||||||
for (let byte of bytes) {
|
|
||||||
counts[byte] = (counts[byte] || 0) + 1;
|
// 检查16位整数的合理性
|
||||||
|
let reasonableShortCount = 0;
|
||||||
|
let totalShorts = 0;
|
||||||
|
for (let i = 0; i < bytes.length - 1; i += 2) {
|
||||||
|
const value = bytes[i] | (bytes[i + 1] << 8);
|
||||||
|
totalShorts++;
|
||||||
|
// 检查是否为合理的16位数值范围
|
||||||
|
if (value < 10000 && value > 0) {
|
||||||
|
reasonableShortCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueValues = Object.keys(counts).length;
|
|
||||||
const maxRepeats = Math.max(...Object.values(counts));
|
|
||||||
|
|
||||||
patterns.repetition = {
|
|
||||||
uniqueValues: uniqueValues,
|
|
||||||
maxRepeats: maxRepeats,
|
|
||||||
entropy: uniqueValues / bytes.length
|
|
||||||
};
|
|
||||||
|
|
||||||
// 检查零值模式
|
|
||||||
const zeroCount = counts[0] || 0;
|
|
||||||
patterns.zeros = {
|
|
||||||
count: zeroCount,
|
|
||||||
ratio: zeroCount / bytes.length
|
|
||||||
};
|
|
||||||
|
|
||||||
return patterns;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示智能检测结果 - 紧凑版
|
if (totalShorts > 0) {
|
||||||
function displaySmartDetection() {
|
const shortRatio = reasonableShortCount / totalShorts;
|
||||||
const panel = document.getElementById('smartDetection');
|
results.short = {
|
||||||
const results = document.getElementById('detectionResults');
|
confidence: shortRatio > 0.6 ? 'high' : shortRatio > 0.3 ? 'medium' : 'low',
|
||||||
|
score: shortRatio * 100,
|
||||||
let html = '';
|
reason: `${reasonableShortCount}/${totalShorts} ${t('reasonable_16bit_values')}`
|
||||||
|
};
|
||||||
// 编码格式检测结果 - 只显示高置信度的
|
}
|
||||||
const highConfidenceEncodings = Object.entries(smartAnalysisResults.encoding)
|
|
||||||
.filter(([_, result]) => result.confidence !== 'low')
|
// 检查字节级数据
|
||||||
.sort((a, b) => b[1].score - a[1].score);
|
let reasonableByteCount = 0;
|
||||||
|
for (let byte of bytes) {
|
||||||
if (highConfidenceEncodings.length > 0) {
|
if (byte > 0 && byte < 250) {
|
||||||
html += `<div class="detection-category">${t('encoding_format_detection')}</div>`;
|
reasonableByteCount++;
|
||||||
for (let [format, result] of highConfidenceEncodings.slice(0, 3)) {
|
}
|
||||||
const confidenceClass = `confidence-${result.confidence}`;
|
}
|
||||||
const badgeClass = `badge-${result.confidence}`;
|
|
||||||
|
const byteRatio = reasonableByteCount / bytes.length;
|
||||||
html += `
|
results.byte = {
|
||||||
|
confidence: byteRatio > 0.8 ? 'high' : byteRatio > 0.5 ? 'medium' : 'low',
|
||||||
|
score: byteRatio * 100,
|
||||||
|
reason: `${reasonableByteCount}/${bytes.length} ${t('reasonable_byte_values')}`
|
||||||
|
};
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析数据模式
|
||||||
|
function analyzePatterns(bytes) {
|
||||||
|
const patterns = {};
|
||||||
|
|
||||||
|
// 检查重复模式
|
||||||
|
const counts = {};
|
||||||
|
for (let byte of bytes) {
|
||||||
|
counts[byte] = (counts[byte] || 0) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueValues = Object.keys(counts).length;
|
||||||
|
const maxRepeats = Math.max(...Object.values(counts));
|
||||||
|
|
||||||
|
patterns.repetition = {
|
||||||
|
uniqueValues: uniqueValues,
|
||||||
|
maxRepeats: maxRepeats,
|
||||||
|
entropy: uniqueValues / bytes.length
|
||||||
|
};
|
||||||
|
|
||||||
|
// 检查零值模式
|
||||||
|
const zeroCount = counts[0] || 0;
|
||||||
|
patterns.zeros = {
|
||||||
|
count: zeroCount,
|
||||||
|
ratio: zeroCount / bytes.length
|
||||||
|
};
|
||||||
|
|
||||||
|
return patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示智能检测结果 - 紧凑版
|
||||||
|
function displaySmartDetection() {
|
||||||
|
const panel = document.getElementById('smartDetection');
|
||||||
|
const results = document.getElementById('detectionResults');
|
||||||
|
|
||||||
|
let html = '';
|
||||||
|
|
||||||
|
// 编码格式检测结果 - 只显示高置信度的
|
||||||
|
const highConfidenceEncodings = Object.entries(smartAnalysisResults.encoding)
|
||||||
|
.filter(([_, result]) => result.confidence !== 'low')
|
||||||
|
.sort((a, b) => b[1].score - a[1].score);
|
||||||
|
|
||||||
|
if (highConfidenceEncodings.length > 0) {
|
||||||
|
html += `<div class="detection-category">${t('encoding_format_detection')}</div>`;
|
||||||
|
for (let [format, result] of highConfidenceEncodings.slice(0, 3)) {
|
||||||
|
const confidenceClass = `confidence-${result.confidence}`;
|
||||||
|
const badgeClass = `badge-${result.confidence}`;
|
||||||
|
|
||||||
|
html += `
|
||||||
<div class="detection-item ${confidenceClass}">
|
<div class="detection-item ${confidenceClass}">
|
||||||
<div>
|
<div>
|
||||||
<strong>${format}</strong> - ${result.reason}
|
<strong>${format}</strong> - ${result.reason}
|
||||||
@@ -1712,21 +1812,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 字节序检测结果 - 只显示高置信度的
|
|
||||||
const highConfidenceByteOrders = Object.entries(smartAnalysisResults.byteOrder)
|
// 字节序检测结果 - 只显示高置信度的
|
||||||
.filter(([_, result]) => result.confidence !== 'low')
|
const highConfidenceByteOrders = Object.entries(smartAnalysisResults.byteOrder)
|
||||||
.sort((a, b) => b[1].score - a[1].score);
|
.filter(([_, result]) => result.confidence !== 'low')
|
||||||
|
.sort((a, b) => b[1].score - a[1].score);
|
||||||
if (highConfidenceByteOrders.length > 0) {
|
|
||||||
html += `<div class="detection-category">${t('byte_order_detection')}</div>`;
|
if (highConfidenceByteOrders.length > 0) {
|
||||||
for (let [order, result] of highConfidenceByteOrders.slice(0, 3)) {
|
html += `<div class="detection-category">${t('byte_order_detection')}</div>`;
|
||||||
const confidenceClass = `confidence-${result.confidence}`;
|
for (let [order, result] of highConfidenceByteOrders.slice(0, 3)) {
|
||||||
const badgeClass = `badge-${result.confidence}`;
|
const confidenceClass = `confidence-${result.confidence}`;
|
||||||
|
const badgeClass = `badge-${result.confidence}`;
|
||||||
html += `
|
|
||||||
|
html += `
|
||||||
<div class="detection-item ${confidenceClass}">
|
<div class="detection-item ${confidenceClass}">
|
||||||
<div>
|
<div>
|
||||||
<strong>${order}</strong> - ${result.reason}
|
<strong>${order}</strong> - ${result.reason}
|
||||||
@@ -1736,99 +1836,99 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
results.innerHTML = html || `<div class="detection-item">${t('no_high_confidence')}</div>`;
|
|
||||||
panel.style.display = 'block';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results.innerHTML = html || `<div class="detection-item">${t('no_high_confidence')}</div>`;
|
||||||
|
panel.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成推荐配置
|
||||||
|
function generateRecommendations() {
|
||||||
|
recommendedConfigs = [];
|
||||||
|
|
||||||
|
// 找出最佳编码格式
|
||||||
|
let bestEncoding = 'DEC';
|
||||||
|
let bestEncodingScore = 0;
|
||||||
|
for (let [format, result] of Object.entries(smartAnalysisResults.encoding)) {
|
||||||
|
if (result.score > bestEncodingScore && result.confidence !== 'low') {
|
||||||
|
bestEncoding = format;
|
||||||
|
bestEncodingScore = result.score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出最佳字节序
|
||||||
|
let bestByteOrder = 'DCBA';
|
||||||
|
let bestByteOrderScore = 0;
|
||||||
|
for (let [order, result] of Object.entries(smartAnalysisResults.byteOrder)) {
|
||||||
|
if (result.score > bestByteOrderScore) {
|
||||||
|
bestByteOrder = order;
|
||||||
|
bestByteOrderScore = result.score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 生成推荐配置
|
// 生成推荐配置
|
||||||
function generateRecommendations() {
|
if (smartAnalysisResults.encoding.ASCII.confidence === 'high') {
|
||||||
recommendedConfigs = [];
|
recommendedConfigs.push({
|
||||||
|
encoding: 'DEC',
|
||||||
// 找出最佳编码格式
|
byteOrder: 'DCBA',
|
||||||
let bestEncoding = 'DEC';
|
dataType: 'ascii',
|
||||||
let bestEncodingScore = 0;
|
reason: t('high_confidence_ascii'),
|
||||||
for (let [format, result] of Object.entries(smartAnalysisResults.encoding)) {
|
confidence: 'high'
|
||||||
if (result.score > bestEncodingScore && result.confidence !== 'low') {
|
});
|
||||||
bestEncoding = format;
|
|
||||||
bestEncodingScore = result.score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 找出最佳字节序
|
|
||||||
let bestByteOrder = 'DCBA';
|
|
||||||
let bestByteOrderScore = 0;
|
|
||||||
for (let [order, result] of Object.entries(smartAnalysisResults.byteOrder)) {
|
|
||||||
if (result.score > bestByteOrderScore) {
|
|
||||||
bestByteOrder = order;
|
|
||||||
bestByteOrderScore = result.score;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成推荐配置
|
|
||||||
if (smartAnalysisResults.encoding.ASCII.confidence === 'high') {
|
|
||||||
recommendedConfigs.push({
|
|
||||||
encoding: 'DEC',
|
|
||||||
byteOrder: 'DCBA',
|
|
||||||
dataType: 'ascii',
|
|
||||||
reason: t('high_confidence_ascii'),
|
|
||||||
confidence: 'high'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smartAnalysisResults.encoding.BCD.confidence === 'high') {
|
|
||||||
recommendedConfigs.push({
|
|
||||||
encoding: 'BCD',
|
|
||||||
byteOrder: bestByteOrder,
|
|
||||||
dataType: 'uint',
|
|
||||||
reason: t('high_confidence_bcd'),
|
|
||||||
confidence: 'high'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 浮点数推荐
|
|
||||||
if (bestByteOrderScore > 50) {
|
|
||||||
recommendedConfigs.push({
|
|
||||||
encoding: bestEncoding,
|
|
||||||
byteOrder: bestByteOrder,
|
|
||||||
dataType: 'float',
|
|
||||||
reason: `${bestByteOrder}${t('best_byteorder_float')}`,
|
|
||||||
confidence: bestByteOrderScore > 70 ? 'high' : 'medium'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 整数推荐
|
|
||||||
if (smartAnalysisResults.dataType && smartAnalysisResults.dataType.short &&
|
|
||||||
smartAnalysisResults.dataType.short.confidence !== 'low') {
|
|
||||||
recommendedConfigs.push({
|
|
||||||
encoding: bestEncoding,
|
|
||||||
byteOrder: bestByteOrder,
|
|
||||||
dataType: 'ushort',
|
|
||||||
reason: t('good_16bit_parsing'),
|
|
||||||
confidence: smartAnalysisResults.dataType.short.confidence
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示推荐配置
|
|
||||||
displayRecommendations();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (smartAnalysisResults.encoding.BCD.confidence === 'high') {
|
||||||
|
recommendedConfigs.push({
|
||||||
|
encoding: 'BCD',
|
||||||
|
byteOrder: bestByteOrder,
|
||||||
|
dataType: 'uint',
|
||||||
|
reason: t('high_confidence_bcd'),
|
||||||
|
confidence: 'high'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 浮点数推荐
|
||||||
|
if (bestByteOrderScore > 50) {
|
||||||
|
recommendedConfigs.push({
|
||||||
|
encoding: bestEncoding,
|
||||||
|
byteOrder: bestByteOrder,
|
||||||
|
dataType: 'float',
|
||||||
|
reason: `${bestByteOrder}${t('best_byteorder_float')}`,
|
||||||
|
confidence: bestByteOrderScore > 70 ? 'high' : 'medium'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 整数推荐
|
||||||
|
if (smartAnalysisResults.dataType && smartAnalysisResults.dataType.short &&
|
||||||
|
smartAnalysisResults.dataType.short.confidence !== 'low') {
|
||||||
|
recommendedConfigs.push({
|
||||||
|
encoding: bestEncoding,
|
||||||
|
byteOrder: bestByteOrder,
|
||||||
|
dataType: 'ushort',
|
||||||
|
reason: t('good_16bit_parsing'),
|
||||||
|
confidence: smartAnalysisResults.dataType.short.confidence
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 显示推荐配置
|
// 显示推荐配置
|
||||||
function displayRecommendations() {
|
displayRecommendations();
|
||||||
const panel = document.getElementById('recommendationPanel');
|
}
|
||||||
const container = document.getElementById('recommendedConfigs');
|
|
||||||
|
// 显示推荐配置
|
||||||
if (recommendedConfigs.length === 0) {
|
function displayRecommendations() {
|
||||||
panel.style.display = 'none';
|
const panel = document.getElementById('recommendationPanel');
|
||||||
return;
|
const container = document.getElementById('recommendedConfigs');
|
||||||
}
|
|
||||||
|
if (recommendedConfigs.length === 0) {
|
||||||
let html = '';
|
panel.style.display = 'none';
|
||||||
recommendedConfigs.forEach((config, index) => {
|
return;
|
||||||
const badgeText = config.confidence === 'high' ? t('strongly_recommended') : t('recommended');
|
}
|
||||||
html += `
|
|
||||||
|
let html = '';
|
||||||
|
recommendedConfigs.forEach((config, index) => {
|
||||||
|
const badgeText = config.confidence === 'high' ? t('strongly_recommended') : t('recommended');
|
||||||
|
html += `
|
||||||
<div class="recommended-item" data-index="${index}">
|
<div class="recommended-item" data-index="${index}">
|
||||||
<div class="recommended-badge">${badgeText}</div>
|
<div class="recommended-badge">${badgeText}</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -1837,302 +1937,302 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
container.innerHTML = html;
|
container.innerHTML = html;
|
||||||
panel.style.display = 'block';
|
panel.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用推荐配置
|
||||||
|
function applyRecommendation() {
|
||||||
|
if (recommendedConfigs.length === 0) {
|
||||||
|
alert(t('no_recommendations'));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用推荐配置
|
// 应用第一个(最佳)推荐配置
|
||||||
function applyRecommendation() {
|
const config = recommendedConfigs[0];
|
||||||
if (recommendedConfigs.length === 0) {
|
document.getElementById('targetType').value = config.dataType;
|
||||||
alert(t('no_recommendations'));
|
document.getElementById('byteOrder').value = config.byteOrder;
|
||||||
return;
|
document.getElementById('encoding').value = config.encoding;
|
||||||
|
|
||||||
|
// 自动转换
|
||||||
|
convertData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析输入数据(增强版)
|
||||||
|
function parseInput(showMessage = true) {
|
||||||
|
const rawDataElement = document.getElementById('rawData');
|
||||||
|
const inputFormat = document.getElementById('inputFormat').value;
|
||||||
|
const validation = document.getElementById('inputValidation');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const inputText = rawDataElement.value.trim();
|
||||||
|
if (!inputText) {
|
||||||
|
throw new Error(t('no_data_input'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 应用第一个(最佳)推荐配置
|
// 清空之前的结果
|
||||||
const config = recommendedConfigs[0];
|
clearPreviousResults();
|
||||||
document.getElementById('targetType').value = config.dataType;
|
|
||||||
document.getElementById('byteOrder').value = config.byteOrder;
|
let parsedData = [];
|
||||||
document.getElementById('encoding').value = config.encoding;
|
currentInputFormat = inputFormat;
|
||||||
|
|
||||||
// 自动转换
|
if (inputFormat === 'hex') {
|
||||||
convertData(true);
|
const hexPattern = /0x[0-9A-Fa-f]+|[0-9A-Fa-f]+/g;
|
||||||
}
|
const hexMatches = inputText.match(hexPattern);
|
||||||
|
if (!hexMatches) {
|
||||||
// 解析输入数据(增强版)
|
throw new Error(t('invalid_hex_format'));
|
||||||
function parseInput(showMessage = true) {
|
|
||||||
const rawDataElement = document.getElementById('rawData');
|
|
||||||
const inputFormat = document.getElementById('inputFormat').value;
|
|
||||||
const validation = document.getElementById('inputValidation');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const inputText = rawDataElement.value.trim();
|
|
||||||
if (!inputText) {
|
|
||||||
throw new Error(t('no_data_input'));
|
|
||||||
}
|
}
|
||||||
|
parsedData = hexMatches.map(hex => {
|
||||||
// 清空之前的结果
|
const value = parseInt(hex.replace('0x', ''), 16);
|
||||||
clearPreviousResults();
|
return value;
|
||||||
|
});
|
||||||
let parsedData = [];
|
|
||||||
currentInputFormat = inputFormat;
|
|
||||||
|
|
||||||
if (inputFormat === 'hex') {
|
|
||||||
const hexPattern = /0x[0-9A-Fa-f]+|[0-9A-Fa-f]+/g;
|
|
||||||
const hexMatches = inputText.match(hexPattern);
|
|
||||||
if (!hexMatches) {
|
|
||||||
throw new Error(t('invalid_hex_format'));
|
|
||||||
}
|
|
||||||
parsedData = hexMatches.map(hex => {
|
|
||||||
const value = parseInt(hex.replace('0x', ''), 16);
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const cleanText = inputText.replace(/[\[\]]/g, '');
|
|
||||||
const values = cleanText.split(/[,\s\n]+/).filter(v => v.trim());
|
|
||||||
|
|
||||||
parsedData = values.map(v => {
|
|
||||||
const num = v.includes('0x') ? parseInt(v, 16) : parseInt(v);
|
|
||||||
if (isNaN(num)) {
|
|
||||||
throw new Error(`${t('invalid_number')}: ${v}`);
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
validateDataRange(parsedData, inputFormat);
|
|
||||||
currentData = parsedData;
|
|
||||||
|
|
||||||
if (showMessage) {
|
|
||||||
validation.innerHTML = `<div class="success">✅ ${t('data_parsed_successfully')} ${parsedData.length} ${t('items')}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
if (showMessage) {
|
|
||||||
validation.innerHTML = `<div class="error">❌ ${error.message}</div>`;
|
|
||||||
}
|
|
||||||
currentData = [];
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证数据范围
|
|
||||||
function validateDataRange(data, format) {
|
|
||||||
const ranges = {
|
|
||||||
'byte': [0, 255],
|
|
||||||
'sbyte': [-128, 127],
|
|
||||||
'ushort': [0, 65535],
|
|
||||||
'short': [-32768, 32767],
|
|
||||||
'int': [-2147483648, 2147483647],
|
|
||||||
'uint': [0, 4294967295],
|
|
||||||
'hex': [0, 4294967295]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ranges[format]) {
|
|
||||||
const [min, max] = ranges[format];
|
|
||||||
for (let value of data) {
|
|
||||||
if (value < min || value > max) {
|
|
||||||
throw new Error(`${t('value_out_of_range')} ${value} ${format} [${min}, ${max}]`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转换数据(增强版)
|
|
||||||
function convertData(isSmartMode = false) {
|
|
||||||
if (currentData.length === 0) {
|
|
||||||
alert(t('no_data_to_convert'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetType = document.getElementById('targetType').value;
|
|
||||||
const byteOrder = document.getElementById('byteOrder').value;
|
|
||||||
const encoding = document.getElementById('encoding').value;
|
|
||||||
|
|
||||||
document.getElementById('resultsSection').style.display = 'block';
|
|
||||||
|
|
||||||
// 生成所有结果
|
|
||||||
allResults = generateAllResults();
|
|
||||||
|
|
||||||
// 计算统计信息
|
|
||||||
const statistics = calculateStatistics();
|
|
||||||
displayStatistics(statistics);
|
|
||||||
|
|
||||||
// 自动滚动到结果区域并切换标签
|
|
||||||
setTimeout(() => {
|
|
||||||
const resultsSection = document.getElementById('resultsSection');
|
|
||||||
resultsSection.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
|
|
||||||
// 切换到相应的标签
|
|
||||||
if (!isSmartMode) {
|
|
||||||
switchTabProgrammatically('all-results');
|
|
||||||
} else {
|
|
||||||
switchTabProgrammatically('smart-results');
|
|
||||||
}
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// 显示结果
|
|
||||||
if (isSmartMode) {
|
|
||||||
displaySmartResults();
|
|
||||||
} else {
|
} else {
|
||||||
displayResults(allResults, targetType, byteOrder, encoding);
|
const cleanText = inputText.replace(/[\[\]]/g, '');
|
||||||
|
const values = cleanText.split(/[,\s\n]+/).filter(v => v.trim());
|
||||||
|
|
||||||
|
parsedData = values.map(v => {
|
||||||
|
const num = v.includes('0x') ? parseInt(v, 16) : parseInt(v);
|
||||||
|
if (isNaN(num)) {
|
||||||
|
throw new Error(`${t('invalid_number')}: ${v}`);
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
displayRawData();
|
validateDataRange(parsedData, inputFormat);
|
||||||
}
|
currentData = parsedData;
|
||||||
|
|
||||||
// 更新结果计数器
|
if (showMessage) {
|
||||||
function updateResultCounter(results = null) {
|
validation.innerHTML = `<div class="success">✅ ${t('data_parsed_successfully')} ${parsedData.length} ${t('items')}</div>`;
|
||||||
const counter = document.getElementById('allResultsCounter');
|
|
||||||
|
|
||||||
if (results === null) {
|
|
||||||
// 如果没有传入结果,隐藏计数器
|
|
||||||
counter.style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const validResults = results.filter(r => r.isValid).length;
|
return true;
|
||||||
counter.textContent = validResults;
|
|
||||||
counter.style.display = validResults > 0 ? 'flex' : 'none';
|
} catch (error) {
|
||||||
|
if (showMessage) {
|
||||||
|
validation.innerHTML = `<div class="error">❌ ${error.message}</div>`;
|
||||||
|
}
|
||||||
|
currentData = [];
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 生成所有转换结果(增强版)
|
|
||||||
function generateAllResults() {
|
// 验证数据范围
|
||||||
const results = [];
|
function validateDataRange(data, format) {
|
||||||
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
const ranges = {
|
||||||
const encodings = ['DEC', 'BCD', 'HEX'];
|
'byte': [0, 255],
|
||||||
const dataTypes = ['bool', 'byte', 'sbyte', 'short', 'ushort', 'int', 'uint', 'long', 'ulong', 'float', 'double', 'ascii'];
|
'sbyte': [-128, 127],
|
||||||
|
'ushort': [0, 65535],
|
||||||
const bytes = convertToBytes(currentData, currentInputFormat);
|
'short': [-32768, 32767],
|
||||||
|
'int': [-2147483648, 2147483647],
|
||||||
for (let byteOrder of byteOrders) {
|
'uint': [0, 4294967295],
|
||||||
for (let encoding of encodings) {
|
'hex': [0, 4294967295]
|
||||||
for (let dataType of dataTypes) {
|
};
|
||||||
try {
|
|
||||||
const result = convertWithParams(bytes, dataType, byteOrder, encoding);
|
if (ranges[format]) {
|
||||||
if (result.success) {
|
const [min, max] = ranges[format];
|
||||||
// 计算智能评分
|
for (let value of data) {
|
||||||
const smartScore = calculateSmartScore(result, dataType, byteOrder, encoding);
|
if (value < min || value > max) {
|
||||||
|
throw new Error(`${t('value_out_of_range')} ${value} ${format} [${min}, ${max}]`);
|
||||||
results.push({
|
}
|
||||||
inputFormat: currentInputFormat,
|
}
|
||||||
dataType,
|
}
|
||||||
byteOrder,
|
}
|
||||||
encoding,
|
|
||||||
result: result.data,
|
// 转换数据(增强版)
|
||||||
displayValue: result.displayValue,
|
function convertData(isSmartMode = false) {
|
||||||
isValid: result.isValid,
|
if (currentData.length === 0) {
|
||||||
smartScore: smartScore,
|
alert(t('no_data_to_convert'));
|
||||||
isRecommended: smartScore > 70,
|
return;
|
||||||
originalBytes: bytes
|
}
|
||||||
});
|
|
||||||
}
|
const targetType = document.getElementById('targetType').value;
|
||||||
} catch (error) {
|
const byteOrder = document.getElementById('byteOrder').value;
|
||||||
// 记录失败的转换
|
const encoding = document.getElementById('encoding').value;
|
||||||
|
|
||||||
|
document.getElementById('resultsSection').style.display = 'block';
|
||||||
|
|
||||||
|
// 生成所有结果
|
||||||
|
allResults = generateAllResults();
|
||||||
|
|
||||||
|
// 计算统计信息
|
||||||
|
const statistics = calculateStatistics();
|
||||||
|
displayStatistics(statistics);
|
||||||
|
|
||||||
|
// 自动滚动到结果区域并切换标签
|
||||||
|
setTimeout(() => {
|
||||||
|
const resultsSection = document.getElementById('resultsSection');
|
||||||
|
resultsSection.scrollIntoView({behavior: 'smooth', block: 'start'});
|
||||||
|
|
||||||
|
// 切换到相应的标签
|
||||||
|
if (!isSmartMode) {
|
||||||
|
switchTabProgrammatically('all-results');
|
||||||
|
} else {
|
||||||
|
switchTabProgrammatically('smart-results');
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// 显示结果
|
||||||
|
if (isSmartMode) {
|
||||||
|
displaySmartResults();
|
||||||
|
} else {
|
||||||
|
displayResults(allResults, targetType, byteOrder, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
displayRawData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新结果计数器
|
||||||
|
function updateResultCounter(results = null) {
|
||||||
|
const counter = document.getElementById('allResultsCounter');
|
||||||
|
|
||||||
|
if (results === null) {
|
||||||
|
// 如果没有传入结果,隐藏计数器
|
||||||
|
counter.style.display = 'none';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validResults = results.filter(r => r.isValid).length;
|
||||||
|
counter.textContent = validResults;
|
||||||
|
counter.style.display = validResults > 0 ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成所有转换结果(增强版)
|
||||||
|
function generateAllResults() {
|
||||||
|
const results = [];
|
||||||
|
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
||||||
|
const encodings = ['DEC', 'BCD', 'HEX'];
|
||||||
|
const dataTypes = ['bool', 'byte', 'sbyte', 'short', 'ushort', 'int', 'uint', 'long', 'ulong', 'float', 'double', 'ascii'];
|
||||||
|
|
||||||
|
const bytes = convertToBytes(currentData, currentInputFormat);
|
||||||
|
|
||||||
|
for (let byteOrder of byteOrders) {
|
||||||
|
for (let encoding of encodings) {
|
||||||
|
for (let dataType of dataTypes) {
|
||||||
|
try {
|
||||||
|
const result = convertWithParams(bytes, dataType, byteOrder, encoding);
|
||||||
|
if (result.success) {
|
||||||
|
// 计算智能评分
|
||||||
|
const smartScore = calculateSmartScore(result, dataType, byteOrder, encoding);
|
||||||
|
|
||||||
results.push({
|
results.push({
|
||||||
inputFormat: currentInputFormat,
|
inputFormat: currentInputFormat,
|
||||||
dataType,
|
dataType,
|
||||||
byteOrder,
|
byteOrder,
|
||||||
encoding,
|
encoding,
|
||||||
result: null,
|
result: result.data,
|
||||||
displayValue: t('conversion_failed'),
|
displayValue: result.displayValue,
|
||||||
isValid: false,
|
isValid: result.isValid,
|
||||||
smartScore: 0,
|
smartScore: smartScore,
|
||||||
isRecommended: false,
|
isRecommended: smartScore > 70,
|
||||||
error: error.message,
|
|
||||||
originalBytes: bytes
|
originalBytes: bytes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 记录失败的转换
|
||||||
|
results.push({
|
||||||
|
inputFormat: currentInputFormat,
|
||||||
|
dataType,
|
||||||
|
byteOrder,
|
||||||
|
encoding,
|
||||||
|
result: null,
|
||||||
|
displayValue: t('conversion_failed'),
|
||||||
|
isValid: false,
|
||||||
|
smartScore: 0,
|
||||||
|
isRecommended: false,
|
||||||
|
error: error.message,
|
||||||
|
originalBytes: bytes
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算智能评分
|
return results;
|
||||||
function calculateSmartScore(result, dataType, byteOrder, encoding) {
|
}
|
||||||
let score = 0;
|
|
||||||
|
// 计算智能评分
|
||||||
// 基础有效性评分
|
function calculateSmartScore(result, dataType, byteOrder, encoding) {
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
// 基础有效性评分
|
||||||
|
if (result.isValid) {
|
||||||
|
score += 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据智能分析结果加分
|
||||||
|
if (smartAnalysisResults.encoding && smartAnalysisResults.encoding[encoding]) {
|
||||||
|
const encodingAnalysis = smartAnalysisResults.encoding[encoding];
|
||||||
|
if (encodingAnalysis.confidence === 'high') {
|
||||||
|
score += 25;
|
||||||
|
} else if (encodingAnalysis.confidence === 'medium') {
|
||||||
|
score += 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smartAnalysisResults.byteOrder && smartAnalysisResults.byteOrder[byteOrder]) {
|
||||||
|
const byteOrderAnalysis = smartAnalysisResults.byteOrder[byteOrder];
|
||||||
|
if (byteOrderAnalysis.confidence === 'high') {
|
||||||
|
score += 25;
|
||||||
|
} else if (byteOrderAnalysis.confidence === 'medium') {
|
||||||
|
score += 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据类型特定评分
|
||||||
|
if (dataType === 'float' || dataType === 'double') {
|
||||||
|
if (result.data && result.data.length > 0) {
|
||||||
|
const validNumbers = result.data.filter(v => isFinite(v) && !isNaN(v) && Math.abs(v) > 0.0001 && Math.abs(v) < 1000000).length;
|
||||||
|
score += (validNumbers / result.data.length) * 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.min(100, score);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算统计信息
|
||||||
|
function calculateStatistics() {
|
||||||
|
const stats = {
|
||||||
|
total: allResults.length,
|
||||||
|
successful: 0,
|
||||||
|
failed: 0,
|
||||||
|
recommended: 0,
|
||||||
|
byEncoding: {},
|
||||||
|
byByteOrder: {},
|
||||||
|
byDataType: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let result of allResults) {
|
||||||
if (result.isValid) {
|
if (result.isValid) {
|
||||||
score += 30;
|
stats.successful++;
|
||||||
|
} else {
|
||||||
|
stats.failed++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据智能分析结果加分
|
if (result.isRecommended) {
|
||||||
if (smartAnalysisResults.encoding && smartAnalysisResults.encoding[encoding]) {
|
stats.recommended++;
|
||||||
const encodingAnalysis = smartAnalysisResults.encoding[encoding];
|
|
||||||
if (encodingAnalysis.confidence === 'high') {
|
|
||||||
score += 25;
|
|
||||||
} else if (encodingAnalysis.confidence === 'medium') {
|
|
||||||
score += 15;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (smartAnalysisResults.byteOrder && smartAnalysisResults.byteOrder[byteOrder]) {
|
// 按分类统计
|
||||||
const byteOrderAnalysis = smartAnalysisResults.byteOrder[byteOrder];
|
stats.byEncoding[result.encoding] = (stats.byEncoding[result.encoding] || 0) + 1;
|
||||||
if (byteOrderAnalysis.confidence === 'high') {
|
stats.byByteOrder[result.byteOrder] = (stats.byByteOrder[result.byteOrder] || 0) + 1;
|
||||||
score += 25;
|
stats.byDataType[result.dataType] = (stats.byDataType[result.dataType] || 0) + 1;
|
||||||
} else if (byteOrderAnalysis.confidence === 'medium') {
|
|
||||||
score += 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据类型特定评分
|
|
||||||
if (dataType === 'float' || dataType === 'double') {
|
|
||||||
if (result.data && result.data.length > 0) {
|
|
||||||
const validNumbers = result.data.filter(v => isFinite(v) && !isNaN(v) && Math.abs(v) > 0.0001 && Math.abs(v) < 1000000).length;
|
|
||||||
score += (validNumbers / result.data.length) * 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.min(100, score);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算统计信息
|
return stats;
|
||||||
function calculateStatistics() {
|
}
|
||||||
const stats = {
|
|
||||||
total: allResults.length,
|
// 显示统计信息
|
||||||
successful: 0,
|
function displayStatistics(stats) {
|
||||||
failed: 0,
|
const panel = document.getElementById('statisticsPanel');
|
||||||
recommended: 0,
|
const grid = document.getElementById('statisticsGrid');
|
||||||
byEncoding: {},
|
|
||||||
byByteOrder: {},
|
const successRate = ((stats.successful / stats.total) * 100).toFixed(1);
|
||||||
byDataType: {}
|
const recommendRate = ((stats.recommended / stats.total) * 100).toFixed(1);
|
||||||
};
|
|
||||||
|
grid.innerHTML = `
|
||||||
for (let result of allResults) {
|
|
||||||
if (result.isValid) {
|
|
||||||
stats.successful++;
|
|
||||||
} else {
|
|
||||||
stats.failed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.isRecommended) {
|
|
||||||
stats.recommended++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按分类统计
|
|
||||||
stats.byEncoding[result.encoding] = (stats.byEncoding[result.encoding] || 0) + 1;
|
|
||||||
stats.byByteOrder[result.byteOrder] = (stats.byByteOrder[result.byteOrder] || 0) + 1;
|
|
||||||
stats.byDataType[result.dataType] = (stats.byDataType[result.dataType] || 0) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示统计信息
|
|
||||||
function displayStatistics(stats) {
|
|
||||||
const panel = document.getElementById('statisticsPanel');
|
|
||||||
const grid = document.getElementById('statisticsGrid');
|
|
||||||
|
|
||||||
const successRate = ((stats.successful / stats.total) * 100).toFixed(1);
|
|
||||||
const recommendRate = ((stats.recommended / stats.total) * 100).toFixed(1);
|
|
||||||
|
|
||||||
grid.innerHTML = `
|
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-value">${stats.total}</div>
|
<div class="stat-value">${stats.total}</div>
|
||||||
<div class="stat-label">${t('total_conversions')}</div>
|
<div class="stat-label">${t('total_conversions')}</div>
|
||||||
@@ -2158,110 +2258,110 @@
|
|||||||
<div class="stat-label">${t('recommendation_rate')}</div>
|
<div class="stat-label">${t('recommendation_rate')}</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
panel.style.display = 'block';
|
panel.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示智能推荐结果
|
||||||
|
function displaySmartResults() {
|
||||||
|
const grid = document.getElementById('smartResultsGrid');
|
||||||
|
|
||||||
|
// 按智能评分排序
|
||||||
|
const sortedResults = [...allResults].sort((a, b) => b.smartScore - a.smartScore);
|
||||||
|
|
||||||
|
// 只显示评分较高的结果
|
||||||
|
const topResults = sortedResults.filter(r => r.smartScore > 30).slice(0, 20);
|
||||||
|
|
||||||
|
grid.innerHTML = '';
|
||||||
|
for (let result of topResults) {
|
||||||
|
const element = createResultElement(result, true);
|
||||||
|
grid.appendChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示智能推荐结果
|
if (topResults.length === 0) {
|
||||||
function displaySmartResults() {
|
grid.innerHTML = '<div class="result-item">没有找到高置信度的转换结果</div>';
|
||||||
const grid = document.getElementById('smartResultsGrid');
|
|
||||||
|
|
||||||
// 按智能评分排序
|
|
||||||
const sortedResults = [...allResults].sort((a, b) => b.smartScore - a.smartScore);
|
|
||||||
|
|
||||||
// 只显示评分较高的结果
|
|
||||||
const topResults = sortedResults.filter(r => r.smartScore > 30).slice(0, 20);
|
|
||||||
|
|
||||||
grid.innerHTML = '';
|
|
||||||
for (let result of topResults) {
|
|
||||||
const element = createResultElement(result, true);
|
|
||||||
grid.appendChild(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (topResults.length === 0) {
|
|
||||||
grid.innerHTML = '<div class="result-item">没有找到高置信度的转换结果</div>';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 创建结果元素(增强版)
|
|
||||||
function createResultElement(result, showSmartInfo = false) {
|
// 创建结果元素(增强版)
|
||||||
const div = document.createElement('div');
|
function createResultElement(result, showSmartInfo = false) {
|
||||||
div.className = 'result-item';
|
const div = document.createElement('div');
|
||||||
|
div.className = 'result-item';
|
||||||
if (result.isRecommended) {
|
|
||||||
div.classList.add('recommended');
|
if (result.isRecommended) {
|
||||||
}
|
div.classList.add('recommended');
|
||||||
|
}
|
||||||
if (!result.isValid) {
|
|
||||||
div.classList.add('failed');
|
if (!result.isValid) {
|
||||||
}
|
div.classList.add('failed');
|
||||||
|
}
|
||||||
let smartInfo = '';
|
|
||||||
if (showSmartInfo && result.smartScore !== undefined) {
|
let smartInfo = '';
|
||||||
const scoreClass = result.smartScore > 70 ? 'success' : result.smartScore > 40 ? 'warning' : 'error';
|
if (showSmartInfo && result.smartScore !== undefined) {
|
||||||
smartInfo = `
|
const scoreClass = result.smartScore > 70 ? 'success' : result.smartScore > 40 ? 'warning' : 'error';
|
||||||
|
smartInfo = `
|
||||||
<div class="success-indicator ${scoreClass}" title="智能评分: ${result.smartScore}"></div>
|
<div class="success-indicator ${scoreClass}" title="智能评分: ${result.smartScore}"></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let statusIcon = '✅';
|
let statusIcon = '✅';
|
||||||
if (!result.isValid) {
|
if (!result.isValid) {
|
||||||
statusIcon = '❌';
|
statusIcon = '❌';
|
||||||
} else if (result.isRecommended) {
|
} else if (result.isRecommended) {
|
||||||
statusIcon = '⭐';
|
statusIcon = '⭐';
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultIndex = allResults.indexOf(result);
|
const resultIndex = allResults.indexOf(result);
|
||||||
|
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
${smartInfo}
|
${smartInfo}
|
||||||
<div class="result-header">
|
<div class="result-header">
|
||||||
<span class="result-header-sub">${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span>
|
<span>${statusIcon} ${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} - ${result.byteOrder} - ${result.encoding}</span>
|
||||||
<div>
|
<div class="result-header-sub">
|
||||||
<button class="copy-btn" data-copy-value="${result.displayValue.replace(/"/g, '"')}" onclick="copyResultFromButton(this)" title="复制结果">📋</button>
|
<button class="copy-btn" data-copy-value="${result.displayValue.replace(/"/g, '"')}" onclick="copyResultFromButton(this)" title="复制结果">📋</button>
|
||||||
<button class="details-btn" onclick="showConversionDetails(${resultIndex})" title="查看详情">🔍</button>
|
<button class="details-btn" onclick="showConversionDetails(${resultIndex})" title="查看详情">🔍</button>
|
||||||
${showSmartInfo ? `<span style="font-size: 12px; color: #6c757d;">${result.smartScore}分</span>` : ''}
|
${showSmartInfo ? `<span style="font-size: 16px; color: #6c757d;">${result.smartScore}分</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-value">${result.displayValue}</div>
|
<div class="result-value">${result.displayValue}</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从按钮复制结果的新方法
|
// 从按钮复制结果的新方法
|
||||||
function copyResultFromButton(button) {
|
function copyResultFromButton(button) {
|
||||||
const value = button.getAttribute('data-copy-value').replace(/"/g, '"');
|
const value = button.getAttribute('data-copy-value').replace(/"/g, '"');
|
||||||
copyResult(value);
|
copyResult(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示转换详情(增强版,包含禁用body滚动)
|
// 显示转换详情(增强版,包含禁用body滚动)
|
||||||
function showConversionDetails(resultIndex) {
|
function showConversionDetails(resultIndex) {
|
||||||
const result = allResults[resultIndex];
|
const result = allResults[resultIndex];
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
const modal = document.getElementById('detailsModal');
|
const modal = document.getElementById('detailsModal');
|
||||||
const modalTitle = document.getElementById('modalTitle');
|
const modalTitle = document.getElementById('modalTitle');
|
||||||
const modalBody = document.getElementById('modalBody');
|
const modalBody = document.getElementById('modalBody');
|
||||||
|
|
||||||
modalTitle.textContent = `${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} ${t('conversion_details')}`;
|
modalTitle.textContent = `${result.inputFormat.toUpperCase()} → ${result.dataType.toUpperCase()} ${t('conversion_details')}`;
|
||||||
|
|
||||||
// 生成详细内容
|
// 生成详细内容
|
||||||
const detailsHtml = generateConversionDetails(result);
|
const detailsHtml = generateConversionDetails(result);
|
||||||
modalBody.innerHTML = detailsHtml;
|
modalBody.innerHTML = detailsHtml;
|
||||||
|
|
||||||
// 禁用body滚动
|
// 禁用body滚动
|
||||||
document.body.classList.add('modal-open');
|
document.body.classList.add('modal-open');
|
||||||
|
|
||||||
modal.style.display = 'block';
|
modal.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成转换详情内容
|
// 生成转换详情内容 - 增强版,更详细的步骤展示
|
||||||
function generateConversionDetails(result) {
|
function generateConversionDetails(result) {
|
||||||
const bytes = result.originalBytes;
|
const bytes = result.originalBytes;
|
||||||
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
const byteOrders = ['ABCD', 'DCBA', 'CDAB', 'BADC'];
|
||||||
|
|
||||||
let html = `
|
let html = `
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<div class="detail-title">📤 ${t('conversion_result_title')}</div>
|
<div class="detail-title">📤 ${t('conversion_result_title')}</div>
|
||||||
<div class="byteorder-test">
|
<div class="byteorder-test">
|
||||||
@@ -2289,72 +2389,155 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// 显示所有字节序测试结果
|
// 添加详细的转换步骤
|
||||||
if (result.dataType !== 'ascii' && result.dataType !== 'bool' && result.dataType !== 'byte') {
|
html += `
|
||||||
html += `
|
<div class="detail-section">
|
||||||
|
<div class="detail-title">🔄 ${t('step_convert_process')}</div>
|
||||||
|
<div class="conversion-steps">
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 步骤1:解析输入数据
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">1</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_parse_input')}</div>
|
||||||
|
<div class="step-description">
|
||||||
|
${t('input_format')}: ${currentInputFormat.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
<div class="step-data">
|
||||||
|
[${currentData.slice(0, 10).join(', ')}${currentData.length > 10 ? '...' : ''}]
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 步骤2:转换为字节数组
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">2</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_convert_bytes')}</div>
|
||||||
|
<div class="step-description">
|
||||||
|
${getByteConversionDescription(currentInputFormat)}
|
||||||
|
</div>
|
||||||
|
<div class="step-data">
|
||||||
|
${formatBytesForDisplay(bytes)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 步骤3:应用编码格式
|
||||||
|
if (result.encoding !== 'DEC') {
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">3</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_apply_encoding')} (${result.encoding})</div>
|
||||||
|
<div class="step-description">
|
||||||
|
${getEncodingDescription(result.encoding)}
|
||||||
|
</div>
|
||||||
|
<div class="step-data">
|
||||||
|
${getEncodedBytesDisplay(bytes, result.encoding)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤4:应用字节序
|
||||||
|
if (needsByteOrder(result.dataType)) {
|
||||||
|
const stepNumber = result.encoding !== 'DEC' ? 4 : 3;
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">${stepNumber}</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_apply_byteorder')} (${result.byteOrder})</div>
|
||||||
|
<div class="step-description">
|
||||||
|
${getByteOrderDescription(result.byteOrder, result.dataType)}
|
||||||
|
</div>
|
||||||
|
<div class="step-data">
|
||||||
|
${getByteOrderDisplay(bytes, result.byteOrder, result.dataType)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 步骤5:按数据类型解析
|
||||||
|
const finalStepNumber = result.encoding !== 'DEC' ? (needsByteOrder(result.dataType) ? 5 : 4) : (needsByteOrder(result.dataType) ? 4 : 3);
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">${finalStepNumber}</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_parse_datatype')} (${result.dataType.toUpperCase()})</div>
|
||||||
|
<div class="step-description">
|
||||||
|
${getDataTypeParseDescription(result.dataType)}
|
||||||
|
</div>
|
||||||
|
<div class="step-data">
|
||||||
|
${getDataTypeCalculation(bytes, result.dataType, result.byteOrder, result.encoding)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 最终结果
|
||||||
|
html += `
|
||||||
|
<div class="step-item">
|
||||||
|
<div class="step-number">${finalStepNumber + 1}</div>
|
||||||
|
<div>
|
||||||
|
<div class="step-title">${t('step_final_result')}</div>
|
||||||
|
<div class="step-data">
|
||||||
|
${result.displayValue}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 显示所有字节序测试结果
|
||||||
|
if (result.dataType !== 'ascii' && result.dataType !== 'bool' && result.dataType !== 'byte') {
|
||||||
|
html += `
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<div class="detail-title">🧪 ${t('all_byte_order_tests')}</div>
|
<div class="detail-title">🧪 ${t('all_byte_order_tests')}</div>
|
||||||
<div class="byteorder-test">
|
<div class="byteorder-test">
|
||||||
`;
|
`;
|
||||||
|
|
||||||
for (let byteOrder of byteOrders) {
|
for (let byteOrder of byteOrders) {
|
||||||
try {
|
try {
|
||||||
const testResult = convertWithParams(bytes, result.dataType, byteOrder, result.encoding);
|
const testResult = convertWithParams(bytes, result.dataType, byteOrder, result.encoding);
|
||||||
const isSuccess = testResult.success && testResult.isValid;
|
const isSuccess = testResult.success && testResult.isValid;
|
||||||
const displayValue = testResult.success ? testResult.displayValue : t('conversion_failed');
|
const displayValue = testResult.success ? testResult.displayValue : t('conversion_failed');
|
||||||
|
|
||||||
html += `
|
html += `
|
||||||
<div class="byteorder-item">
|
<div class="byteorder-item">
|
||||||
<span class="byteorder-label">${byteOrder}${t('format')}</span>
|
<span class="byteorder-label">${byteOrder}${t('format')}</span>
|
||||||
<span class="byteorder-value">[${displayValue}]</span>
|
<span class="byteorder-value">[${displayValue}]</span>
|
||||||
<span class="byteorder-status">${isSuccess ? '✅' : '❌'}</span>
|
<span class="byteorder-status">${isSuccess ? '✅' : '❌'}</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
html += `
|
html += `
|
||||||
<div class="byteorder-item">
|
<div class="byteorder-item">
|
||||||
<span class="byteorder-label">${byteOrder}${t('format')}</span>
|
<span class="byteorder-label">${byteOrder}${t('format')}</span>
|
||||||
<span class="byteorder-value">${t('conversion_failed')}</span>
|
<span class="byteorder-value">${t('conversion_failed')}</span>
|
||||||
<span class="byteorder-status">❌</span>
|
<span class="byteorder-status">❌</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</div></div>';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换原理说明
|
html += '</div></div>';
|
||||||
html += `
|
}
|
||||||
|
|
||||||
|
// 转换原理说明
|
||||||
|
html += `
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<div class="detail-title">📚 ${t('conversion_principle')}</div>
|
<div class="detail-title">📚 ${t('conversion_principle')}</div>
|
||||||
<div class="principle-box">
|
<div class="principle-box">
|
||||||
<div class="principle-item">
|
|
||||||
<strong>${t('problem_analysis')}</strong>
|
|
||||||
<span>${t('you_input')}${currentData.length}${t('values')}${currentData.slice(0, 10).join(', ')}${currentData.length > 10 ? '...' : ''}]${t('convert_to')}${result.dataType}${t('type')}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 具体的转换分析
|
|
||||||
if (currentInputFormat === 'ushort' && result.dataType === 'int') {
|
|
||||||
html += `
|
|
||||||
<div class="principle-item">
|
|
||||||
<strong>${t('data_analysis')}</strong>
|
|
||||||
<span>${t('ushort_to_int_analysis')}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} else if (currentInputFormat === 'int' && result.dataType === 'ushort') {
|
|
||||||
html += `
|
|
||||||
<div class="principle-item">
|
|
||||||
<strong>${t('data_analysis')}</strong>
|
|
||||||
<span>${t('int_to_ushort_analysis')}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 字节序说明
|
|
||||||
html += `
|
|
||||||
<div class="principle-item">
|
<div class="principle-item">
|
||||||
<strong>${t('byte_order_explanation')}</strong>
|
<strong>${t('byte_order_explanation')}</strong>
|
||||||
<div style="margin-top: 8px;">
|
<div style="margin-top: 8px;">
|
||||||
@@ -2367,362 +2550,561 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// 内存布局展示
|
return html;
|
||||||
if (bytes.length > 0) {
|
}
|
||||||
html += `
|
|
||||||
<div class="detail-section">
|
// 辅助函数:获取字节转换描述
|
||||||
<div class="detail-title">💾 ${t('memory_layout_analysis')}</div>
|
function getByteConversionDescription(inputFormat) {
|
||||||
<div class="memory-layout">
|
const descriptions = {
|
||||||
<div class="memory-step">${t('original_data')}: [${currentData.slice(0, 5).join(', ')}${currentData.length > 5 ? '...' : ''}]</div>
|
'byte': '每个byte值直接作为一个字节',
|
||||||
<div class="memory-step">${t('byte_representation')}: [${bytes.slice(0, 20).map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}${bytes.length > 20 ? '...' : ''}]</div>
|
'ushort': '每个ushort值(16位)拆分为2个字节:低字节在前,高字节在后',
|
||||||
`;
|
'short': '每个short值(16位)拆分为2个字节:低字节在前,高字节在后',
|
||||||
|
'int': '每个int值(32位)拆分为4个字节:从低到高依次排列',
|
||||||
if (result.dataType === 'float' && bytes.length >= 4) {
|
'uint': '每个uint值(32位)拆分为4个字节:从低到高依次排列',
|
||||||
const firstFourBytes = bytes.slice(0, 4);
|
'hex': '十六进制字符串转换为对应的字节值'
|
||||||
html += `
|
};
|
||||||
<div class="memory-step">
|
return descriptions[inputFormat] || '';
|
||||||
${t('float_conversion_example')} (前4字节):
|
}
|
||||||
<div style="margin-top: 5px;">
|
|
||||||
DCBA${t('order')}: <span class="memory-bytes">[${firstFourBytes.map(b => '0x' + b.toString(16).padStart(2, '0')).join('][')}]}</span> → ${getFloatFromBytes(firstFourBytes, 'DCBA')}
|
// 辅助函数:格式化字节显示
|
||||||
</div>
|
function formatBytesForDisplay(bytes) {
|
||||||
<div>
|
const maxDisplay = 20;
|
||||||
ABCD${t('order')}: <span class="memory-bytes">[${firstFourBytes.slice().reverse().map(b => '0x' + b.toString(16).padStart(2, '0')).join('][')}]}</span> → ${getFloatFromBytes(firstFourBytes, 'ABCD')}
|
const displayBytes = bytes.slice(0, maxDisplay);
|
||||||
</div>
|
let formatted = displayBytes.map((b, i) => {
|
||||||
</div>
|
const hex = b.toString(16).padStart(2, '0').toUpperCase();
|
||||||
`;
|
return `[${i}]: 0x${hex} (${b})`;
|
||||||
}
|
}).join(', ');
|
||||||
|
|
||||||
html += '</div></div>';
|
if (bytes.length > maxDisplay) {
|
||||||
}
|
formatted += ` ... (共 ${bytes.length} 字节)`;
|
||||||
|
|
||||||
return html;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从字节数组获取浮点数(用于演示)
|
return formatted;
|
||||||
function getFloatFromBytes(bytes, byteOrder) {
|
}
|
||||||
try {
|
|
||||||
const orderedBytes = applyByteOrder([...bytes], byteOrder, 4);
|
// 辅助函数:获取编码描述
|
||||||
const buffer = new ArrayBuffer(4);
|
function getEncodingDescription(encoding) {
|
||||||
const view = new DataView(buffer);
|
const descriptions = {
|
||||||
for (let i = 0; i < 4; i++) {
|
'DEC': t('encoding_dec_desc'),
|
||||||
view.setUint8(i, orderedBytes[i]);
|
'BCD': t('encoding_bcd_desc'),
|
||||||
}
|
'HEX': t('encoding_hex_desc')
|
||||||
const value = view.getFloat32(0, true);
|
};
|
||||||
return isFinite(value) ? value.toFixed(6) : value.toString();
|
return descriptions[encoding] || '';
|
||||||
} catch (error) {
|
}
|
||||||
return t('conversion_failed');
|
|
||||||
|
// 辅助函数:获取编码后的字节显示
|
||||||
|
function getEncodedBytesDisplay(bytes, encoding) {
|
||||||
|
if (encoding === 'BCD') {
|
||||||
|
const maxDisplay = 10;
|
||||||
|
const displayBytes = bytes.slice(0, maxDisplay);
|
||||||
|
let formatted = displayBytes.map((b, i) => {
|
||||||
|
const high = (b >> 4) & 0xF;
|
||||||
|
const low = b & 0xF;
|
||||||
|
const bcdValue = high * 10 + low;
|
||||||
|
return `0x${b.toString(16).padStart(2, '0').toUpperCase()} → ${bcdValue}`;
|
||||||
|
}).join(', ');
|
||||||
|
|
||||||
|
if (bytes.length > maxDisplay) {
|
||||||
|
formatted += ` ... (共 ${bytes.length} 字节)`;
|
||||||
|
}
|
||||||
|
return formatted;
|
||||||
|
}
|
||||||
|
return formatBytesForDisplay(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:检查是否需要字节序
|
||||||
|
function needsByteOrder(dataType) {
|
||||||
|
return !['bool', 'byte', 'sbyte', 'ascii'].includes(dataType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取字节序描述
|
||||||
|
function getByteOrderDescription(byteOrder, dataType) {
|
||||||
|
const bytesNeeded = getDataTypeByteSize(dataType);
|
||||||
|
const examples = {
|
||||||
|
'ABCD': {
|
||||||
|
2: '例: [0x12, 0x34] → [0x34, 0x12]',
|
||||||
|
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x78, 0x56, 0x34, 0x12]'
|
||||||
|
},
|
||||||
|
'DCBA': {
|
||||||
|
2: '例: [0x12, 0x34] → [0x12, 0x34] (不变)',
|
||||||
|
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x12, 0x34, 0x56, 0x78] (不变)'
|
||||||
|
},
|
||||||
|
'CDAB': {
|
||||||
|
2: '例: [0x12, 0x34] → [0x34, 0x12]',
|
||||||
|
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x56, 0x78, 0x12, 0x34]'
|
||||||
|
},
|
||||||
|
'BADC': {
|
||||||
|
2: '例: [0x12, 0x34] → [0x34, 0x12]',
|
||||||
|
4: '例: [0x12, 0x34, 0x56, 0x78] → [0x34, 0x12, 0x78, 0x56]'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return examples[byteOrder] && examples[byteOrder][bytesNeeded] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取数据类型字节大小
|
||||||
|
function getDataTypeByteSize(dataType) {
|
||||||
|
const sizes = {
|
||||||
|
'short': 2, 'ushort': 2,
|
||||||
|
'int': 4, 'uint': 4, 'float': 4,
|
||||||
|
'long': 8, 'ulong': 8, 'double': 8
|
||||||
|
};
|
||||||
|
return sizes[dataType] || 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取字节序显示
|
||||||
|
function getByteOrderDisplay(bytes, byteOrder, dataType) {
|
||||||
|
const byteSize = getDataTypeByteSize(dataType);
|
||||||
|
const maxGroups = 3;
|
||||||
|
let display = '';
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(bytes.length, maxGroups * byteSize); i += byteSize) {
|
||||||
|
const group = bytes.slice(i, i + byteSize);
|
||||||
|
const ordered = applyByteOrder([...group], byteOrder, byteSize);
|
||||||
|
|
||||||
|
display += `[${group.map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ')}] → `;
|
||||||
|
display += `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0').toUpperCase()).join(', ')}]`;
|
||||||
|
|
||||||
|
if (i + byteSize < bytes.length) {
|
||||||
|
display += ', ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭详情模态框(增强版,恢复body滚动)
|
if (bytes.length > maxGroups * byteSize) {
|
||||||
function closeDetailsModal() {
|
display += ' ...';
|
||||||
document.getElementById('detailsModal').style.display = 'none';
|
|
||||||
// 恢复body滚动
|
|
||||||
document.body.classList.remove('modal-open');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 点击模态框外部关闭
|
return display;
|
||||||
window.onclick = function(event) {
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取数据类型解析描述
|
||||||
|
function getDataTypeParseDescription(dataType) {
|
||||||
|
const descriptions = {
|
||||||
|
'bool': '每个字节的8个位分别解析为布尔值',
|
||||||
|
'byte': '每个字节直接作为无符号8位整数',
|
||||||
|
'sbyte': '每个字节作为有符号8位整数',
|
||||||
|
'short': '每2个字节组合为有符号16位整数',
|
||||||
|
'ushort': '每2个字节组合为无符号16位整数',
|
||||||
|
'int': '每4个字节组合为有符号32位整数',
|
||||||
|
'uint': '每4个字节组合为无符号32位整数',
|
||||||
|
'long': '每8个字节组合为有符号64位整数',
|
||||||
|
'ulong': '每8个字节组合为无符号64位整数',
|
||||||
|
'float': '每4个字节按IEEE 754标准解析为32位浮点数',
|
||||||
|
'double': '每8个字节按IEEE 754标准解析为64位浮点数',
|
||||||
|
'ascii': '每个字节解析为ASCII字符'
|
||||||
|
};
|
||||||
|
return descriptions[dataType] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:获取数据类型计算过程
|
||||||
|
function getDataTypeCalculation(bytes, dataType, byteOrder, encoding) {
|
||||||
|
const processedBytes = applyEncoding(bytes, encoding);
|
||||||
|
if (!processedBytes.success) {
|
||||||
|
return '编码转换失败';
|
||||||
|
}
|
||||||
|
|
||||||
|
const actualBytes = processedBytes.data;
|
||||||
|
const byteSize = getDataTypeByteSize(dataType);
|
||||||
|
|
||||||
|
if (dataType === 'ascii') {
|
||||||
|
const chars = actualBytes.slice(0, 20).map(b => {
|
||||||
|
const char = b >= 32 && b <= 126 ? String.fromCharCode(b) : '·';
|
||||||
|
return `0x${b.toString(16).padStart(2, '0')} → '${char}'`;
|
||||||
|
}).join(', ');
|
||||||
|
return chars + (actualBytes.length > 20 ? ' ...' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataType === 'bool') {
|
||||||
|
const firstByte = actualBytes[0] || 0;
|
||||||
|
let bits = '';
|
||||||
|
for (let i = 7; i >= 0; i--) {
|
||||||
|
bits += ((firstByte >> i) & 1) ? '1' : '0';
|
||||||
|
if (i === 4) bits += ' ';
|
||||||
|
}
|
||||||
|
return `字节 0x${firstByte.toString(16).padStart(2, '0')} = ${bits} → [${bits.replace(' ', '').split('').map(b => b === '1').join(', ')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示前几个转换
|
||||||
|
let calculations = [];
|
||||||
|
const maxCalc = 3;
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(actualBytes.length, maxCalc * byteSize); i += byteSize) {
|
||||||
|
const group = actualBytes.slice(i, i + byteSize);
|
||||||
|
if (group.length < byteSize) break;
|
||||||
|
|
||||||
|
const ordered = applyByteOrder([...group], byteOrder, byteSize);
|
||||||
|
let value = 0;
|
||||||
|
let calc = '';
|
||||||
|
|
||||||
|
switch (dataType) {
|
||||||
|
case 'byte':
|
||||||
|
value = group[0];
|
||||||
|
calc = `0x${group[0].toString(16).padStart(2, '0')} = ${value}`;
|
||||||
|
break;
|
||||||
|
case 'sbyte':
|
||||||
|
value = group[0] > 127 ? group[0] - 256 : group[0];
|
||||||
|
calc = `0x${group[0].toString(16).padStart(2, '0')} = ${value}`;
|
||||||
|
break;
|
||||||
|
case 'short':
|
||||||
|
case 'ushort':
|
||||||
|
value = ordered[0] | (ordered[1] << 8);
|
||||||
|
if (dataType === 'short' && value > 32767) {
|
||||||
|
value -= 65536;
|
||||||
|
}
|
||||||
|
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = 0x${value.toString(16).padStart(4, '0')} = ${value}`;
|
||||||
|
break;
|
||||||
|
case 'int':
|
||||||
|
case 'uint':
|
||||||
|
value = ordered[0] | (ordered[1] << 8) | (ordered[2] << 16) | (ordered[3] << 24);
|
||||||
|
if (dataType === 'int' && value > 2147483647) {
|
||||||
|
value -= 4294967296;
|
||||||
|
} else if (dataType === 'uint') {
|
||||||
|
value = value >>> 0;
|
||||||
|
}
|
||||||
|
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = 0x${(value >>> 0).toString(16).padStart(8, '0')} = ${value}`;
|
||||||
|
break;
|
||||||
|
case 'float':
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
view.setUint8(j, ordered[j]);
|
||||||
|
}
|
||||||
|
value = view.getFloat32(0, true);
|
||||||
|
calc = `[${ordered.map(b => '0x' + b.toString(16).padStart(2, '0')).join(', ')}] = ${value.toFixed(6)}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
calculations.push(calc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return calculations.join('\n') + (actualBytes.length > maxCalc * byteSize ? '\n...' : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭详情模态框(增强版,恢复body滚动)
|
||||||
|
function closeDetailsModal() {
|
||||||
|
document.getElementById('detailsModal').style.display = 'none';
|
||||||
|
// 恢复body滚动
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击模态框外部关闭
|
||||||
|
window.onclick = function (event) {
|
||||||
|
const modal = document.getElementById('detailsModal');
|
||||||
|
if (event.target === modal) {
|
||||||
|
closeDetailsModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ESC键关闭模态框
|
||||||
|
document.addEventListener('keydown', function (event) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
const modal = document.getElementById('detailsModal');
|
const modal = document.getElementById('detailsModal');
|
||||||
if (event.target === modal) {
|
if (modal.style.display === 'block') {
|
||||||
closeDetailsModal();
|
closeDetailsModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
// ESC键关闭模态框
|
|
||||||
document.addEventListener('keydown', function(event) {
|
// 从字节数组获取浮点数(用于演示)
|
||||||
if (event.key === 'Escape') {
|
function getFloatFromBytes(bytes, byteOrder) {
|
||||||
const modal = document.getElementById('detailsModal');
|
try {
|
||||||
if (modal.style.display === 'block') {
|
const orderedBytes = applyByteOrder([...bytes], byteOrder, 4);
|
||||||
closeDetailsModal();
|
const buffer = new ArrayBuffer(4);
|
||||||
}
|
const view = new DataView(buffer);
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
view.setUint8(i, orderedBytes[i]);
|
||||||
}
|
}
|
||||||
});
|
const value = view.getFloat32(0, true);
|
||||||
|
return isFinite(value) ? value.toFixed(6) : value.toString();
|
||||||
// 显示结果
|
} catch (error) {
|
||||||
function displayResults(results, targetTypeFilter, byteOrderFilter, encodingFilter) {
|
return t('conversion_failed');
|
||||||
const allResultsGrid = document.getElementById('allResultsGrid');
|
}
|
||||||
allResultsGrid.innerHTML = '';
|
}
|
||||||
|
|
||||||
let filteredResults = results;
|
// 显示结果
|
||||||
if (targetTypeFilter !== 'all') {
|
function displayResults(results, targetTypeFilter, byteOrderFilter, encodingFilter) {
|
||||||
filteredResults = filteredResults.filter(r => r.dataType === targetTypeFilter);
|
const allResultsGrid = document.getElementById('allResultsGrid');
|
||||||
}
|
allResultsGrid.innerHTML = '';
|
||||||
if (byteOrderFilter !== 'all') {
|
|
||||||
filteredResults = filteredResults.filter(r => r.byteOrder === byteOrderFilter);
|
let filteredResults = results;
|
||||||
}
|
if (targetTypeFilter !== 'all') {
|
||||||
if (encodingFilter !== 'all') {
|
filteredResults = filteredResults.filter(r => r.dataType === targetTypeFilter);
|
||||||
filteredResults = filteredResults.filter(r => r.encoding === encodingFilter);
|
}
|
||||||
}
|
if (byteOrderFilter !== 'all') {
|
||||||
|
filteredResults = filteredResults.filter(r => r.byteOrder === byteOrderFilter);
|
||||||
// 更新筛选后的计数器
|
}
|
||||||
updateResultCounter(filteredResults);
|
if (encodingFilter !== 'all') {
|
||||||
|
filteredResults = filteredResults.filter(r => r.encoding === encodingFilter);
|
||||||
for (let result of filteredResults) {
|
}
|
||||||
const element = createResultElement(result);
|
|
||||||
allResultsGrid.appendChild(element);
|
// 更新筛选后的计数器
|
||||||
|
updateResultCounter(filteredResults);
|
||||||
|
|
||||||
|
for (let result of filteredResults) {
|
||||||
|
const element = createResultElement(result);
|
||||||
|
allResultsGrid.appendChild(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选结果
|
||||||
|
function filterResults() {
|
||||||
|
if (allResults.length === 0) return;
|
||||||
|
|
||||||
|
const targetType = document.getElementById('targetType').value;
|
||||||
|
const byteOrder = document.getElementById('byteOrder').value;
|
||||||
|
const encoding = document.getElementById('encoding').value;
|
||||||
|
|
||||||
|
displayResults(allResults, targetType, byteOrder, encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原有的辅助函数保持不变
|
||||||
|
function convertToBytes(data, inputFormat) {
|
||||||
|
const bytes = [];
|
||||||
|
|
||||||
|
for (let value of data) {
|
||||||
|
switch (inputFormat) {
|
||||||
|
case 'byte':
|
||||||
|
bytes.push(value);
|
||||||
|
break;
|
||||||
|
case 'ushort':
|
||||||
|
case 'short':
|
||||||
|
bytes.push(value & 0xFF);
|
||||||
|
bytes.push((value >> 8) & 0xFF);
|
||||||
|
break;
|
||||||
|
case 'int':
|
||||||
|
case 'uint':
|
||||||
|
case 'hex':
|
||||||
|
bytes.push(value & 0xFF);
|
||||||
|
bytes.push((value >> 8) & 0xFF);
|
||||||
|
bytes.push((value >> 16) & 0xFF);
|
||||||
|
bytes.push((value >> 24) & 0xFF);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 筛选结果
|
return bytes;
|
||||||
function filterResults() {
|
}
|
||||||
if (allResults.length === 0) return;
|
|
||||||
|
function convertWithParams(bytes, dataType, byteOrder, encoding) {
|
||||||
const targetType = document.getElementById('targetType').value;
|
try {
|
||||||
const byteOrder = document.getElementById('byteOrder').value;
|
const processedBytes = applyEncoding(bytes, encoding);
|
||||||
const encoding = document.getElementById('encoding').value;
|
if (!processedBytes.success) {
|
||||||
|
return {success: false};
|
||||||
displayResults(allResults, targetType, byteOrder, encoding);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 原有的辅助函数保持不变
|
|
||||||
function convertToBytes(data, inputFormat) {
|
|
||||||
const bytes = [];
|
|
||||||
|
|
||||||
for (let value of data) {
|
|
||||||
switch (inputFormat) {
|
|
||||||
case 'byte':
|
|
||||||
bytes.push(value);
|
|
||||||
break;
|
|
||||||
case 'ushort':
|
|
||||||
case 'short':
|
|
||||||
bytes.push(value & 0xFF);
|
|
||||||
bytes.push((value >> 8) & 0xFF);
|
|
||||||
break;
|
|
||||||
case 'int':
|
|
||||||
case 'uint':
|
|
||||||
case 'hex':
|
|
||||||
bytes.push(value & 0xFF);
|
|
||||||
bytes.push((value >> 8) & 0xFF);
|
|
||||||
bytes.push((value >> 16) & 0xFF);
|
|
||||||
bytes.push((value >> 24) & 0xFF);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
const result = convertByDataType(processedBytes.data, dataType, byteOrder);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
return {success: false, error: error.message};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function convertWithParams(bytes, dataType, byteOrder, encoding) {
|
|
||||||
try {
|
function applyEncoding(bytes, encoding) {
|
||||||
const processedBytes = applyEncoding(bytes, encoding);
|
switch (encoding) {
|
||||||
if (!processedBytes.success) {
|
case 'DEC':
|
||||||
return { success: false };
|
return {success: true, data: bytes};
|
||||||
}
|
|
||||||
|
case 'BCD':
|
||||||
const result = convertByDataType(processedBytes.data, dataType, byteOrder);
|
for (let byte of bytes) {
|
||||||
return result;
|
const high = (byte >> 4) & 0xF;
|
||||||
} catch (error) {
|
const low = byte & 0xF;
|
||||||
return { success: false, error: error.message };
|
if (high > 9 || low > 9) {
|
||||||
}
|
return {success: false, error: 'Invalid BCD format'};
|
||||||
}
|
|
||||||
|
|
||||||
function applyEncoding(bytes, encoding) {
|
|
||||||
switch (encoding) {
|
|
||||||
case 'DEC':
|
|
||||||
return { success: true, data: bytes };
|
|
||||||
|
|
||||||
case 'BCD':
|
|
||||||
for (let byte of bytes) {
|
|
||||||
const high = (byte >> 4) & 0xF;
|
|
||||||
const low = byte & 0xF;
|
|
||||||
if (high > 9 || low > 9) {
|
|
||||||
return { success: false, error: 'Invalid BCD format' };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bcdBytes = bytes.map(byte => {
|
|
||||||
const high = (byte >> 4) & 0xF;
|
|
||||||
const low = byte & 0xF;
|
|
||||||
return high * 10 + low;
|
|
||||||
});
|
|
||||||
return { success: true, data: bcdBytes };
|
|
||||||
|
|
||||||
case 'HEX':
|
|
||||||
return { success: true, data: bytes };
|
|
||||||
|
|
||||||
default:
|
|
||||||
return { success: true, data: bytes };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertByDataType(bytes, dataType, byteOrder) {
|
|
||||||
const result = [];
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (dataType) {
|
|
||||||
case 'bool':
|
|
||||||
for (let byte of bytes) {
|
|
||||||
for (let i = 0; i < 8; i++) {
|
|
||||||
result.push((byte & (1 << i)) !== 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'byte':
|
|
||||||
result.push(...bytes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'sbyte':
|
|
||||||
for (let byte of bytes) {
|
|
||||||
result.push(byte > 127 ? byte - 256 : byte);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'short':
|
|
||||||
case 'ushort':
|
|
||||||
for (let i = 0; i < bytes.length - 1; i += 2) {
|
|
||||||
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1]], byteOrder, 2);
|
|
||||||
const value = orderedBytes[0] | (orderedBytes[1] << 8);
|
|
||||||
result.push(dataType === 'short' && value > 32767 ? value - 65536 : value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'int':
|
|
||||||
case 'uint':
|
|
||||||
for (let i = 0; i < bytes.length - 3; i += 4) {
|
|
||||||
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
|
||||||
const value = orderedBytes[0] | (orderedBytes[1] << 8) | (orderedBytes[2] << 16) | (orderedBytes[3] << 24);
|
|
||||||
result.push(dataType === 'int' && value > 2147483647 ? value - 4294967296 : value >>> 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'long':
|
|
||||||
case 'ulong':
|
|
||||||
for (let i = 0; i < bytes.length - 7; i += 8) {
|
|
||||||
const orderedBytes = applyByteOrder(bytes.slice(i, i + 8), byteOrder, 8);
|
|
||||||
let value = BigInt(0);
|
|
||||||
for (let j = 0; j < 8; j++) {
|
|
||||||
value |= BigInt(orderedBytes[j]) << BigInt(j * 8);
|
|
||||||
}
|
|
||||||
result.push(Number(value));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'float':
|
|
||||||
for (let i = 0; i < bytes.length - 3; i += 4) {
|
|
||||||
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
|
||||||
const buffer = new ArrayBuffer(4);
|
|
||||||
const view = new DataView(buffer);
|
|
||||||
for (let j = 0; j < 4; j++) {
|
|
||||||
view.setUint8(j, orderedBytes[j]);
|
|
||||||
}
|
|
||||||
const floatValue = view.getFloat32(0, true);
|
|
||||||
result.push(floatValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'double':
|
|
||||||
for (let i = 0; i < bytes.length - 7; i += 8) {
|
|
||||||
const orderedBytes = applyByteOrder(bytes.slice(i, i + 8), byteOrder, 8);
|
|
||||||
const buffer = new ArrayBuffer(8);
|
|
||||||
const view = new DataView(buffer);
|
|
||||||
for (let j = 0; j < 8; j++) {
|
|
||||||
view.setUint8(j, orderedBytes[j]);
|
|
||||||
}
|
|
||||||
const doubleValue = view.getFloat64(0, true);
|
|
||||||
result.push(doubleValue);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'ascii':
|
|
||||||
const asciiString = bytes.map(byte =>
|
|
||||||
byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '·'
|
|
||||||
).join('');
|
|
||||||
result.push(asciiString);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayValue = formatDisplayValue(result, dataType);
|
const bcdBytes = bytes.map(byte => {
|
||||||
const isValid = validateResult(result, dataType);
|
const high = (byte >> 4) & 0xF;
|
||||||
|
const low = byte & 0xF;
|
||||||
return {
|
return high * 10 + low;
|
||||||
success: true,
|
});
|
||||||
data: result,
|
return {success: true, data: bcdBytes};
|
||||||
displayValue: displayValue,
|
|
||||||
isValid: isValid
|
case 'HEX':
|
||||||
};
|
return {success: true, data: bytes};
|
||||||
|
|
||||||
} catch (error) {
|
default:
|
||||||
return { success: false, error: error.message };
|
return {success: true, data: bytes};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function applyByteOrder(bytes, byteOrder, expectedLength) {
|
|
||||||
const paddedBytes = [...bytes];
|
function convertByDataType(bytes, dataType, byteOrder) {
|
||||||
while (paddedBytes.length < expectedLength) {
|
const result = [];
|
||||||
paddedBytes.push(0);
|
|
||||||
}
|
try {
|
||||||
|
|
||||||
switch (byteOrder) {
|
|
||||||
case 'ABCD':
|
|
||||||
return paddedBytes.reverse();
|
|
||||||
case 'DCBA':
|
|
||||||
return paddedBytes;
|
|
||||||
case 'CDAB':
|
|
||||||
if (expectedLength === 4) {
|
|
||||||
return [paddedBytes[2], paddedBytes[3], paddedBytes[0], paddedBytes[1]];
|
|
||||||
} else if (expectedLength === 2) {
|
|
||||||
return [paddedBytes[1], paddedBytes[0]];
|
|
||||||
} else if (expectedLength === 8) {
|
|
||||||
return [paddedBytes[6], paddedBytes[7], paddedBytes[4], paddedBytes[5], paddedBytes[2], paddedBytes[3], paddedBytes[0], paddedBytes[1]];
|
|
||||||
}
|
|
||||||
return paddedBytes;
|
|
||||||
case 'BADC':
|
|
||||||
if (expectedLength === 4) {
|
|
||||||
return [paddedBytes[1], paddedBytes[0], paddedBytes[3], paddedBytes[2]];
|
|
||||||
} else if (expectedLength === 2) {
|
|
||||||
return [paddedBytes[1], paddedBytes[0]];
|
|
||||||
} else if (expectedLength === 8) {
|
|
||||||
return [paddedBytes[1], paddedBytes[0], paddedBytes[3], paddedBytes[2], paddedBytes[5], paddedBytes[4], paddedBytes[7], paddedBytes[6]];
|
|
||||||
}
|
|
||||||
return paddedBytes;
|
|
||||||
default:
|
|
||||||
return paddedBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDisplayValue(result, dataType) {
|
|
||||||
if (result.length === 0) return '';
|
|
||||||
|
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case 'float':
|
|
||||||
case 'double':
|
|
||||||
return result.map(v => {
|
|
||||||
if (isNaN(v) || !isFinite(v)) {
|
|
||||||
return v.toString();
|
|
||||||
}
|
|
||||||
return parseFloat(v.toFixed(6));
|
|
||||||
}).join(', ');
|
|
||||||
case 'bool':
|
case 'bool':
|
||||||
return result.slice(0, 32).map(v => v ? '1' : '0').join('');
|
for (let byte of bytes) {
|
||||||
case 'ascii':
|
for (let i = 0; i < 8; i++) {
|
||||||
return result[0];
|
result.push((byte & (1 << i)) !== 0);
|
||||||
default:
|
}
|
||||||
return result.join(', ');
|
}
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
case 'byte':
|
||||||
function validateResult(result, dataType) {
|
result.push(...bytes);
|
||||||
if (result.length === 0) return false;
|
break;
|
||||||
|
|
||||||
switch (dataType) {
|
case 'sbyte':
|
||||||
|
for (let byte of bytes) {
|
||||||
|
result.push(byte > 127 ? byte - 256 : byte);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'short':
|
||||||
|
case 'ushort':
|
||||||
|
for (let i = 0; i < bytes.length - 1; i += 2) {
|
||||||
|
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1]], byteOrder, 2);
|
||||||
|
const value = orderedBytes[0] | (orderedBytes[1] << 8);
|
||||||
|
result.push(dataType === 'short' && value > 32767 ? value - 65536 : value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'int':
|
||||||
|
case 'uint':
|
||||||
|
for (let i = 0; i < bytes.length - 3; i += 4) {
|
||||||
|
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
||||||
|
const value = orderedBytes[0] | (orderedBytes[1] << 8) | (orderedBytes[2] << 16) | (orderedBytes[3] << 24);
|
||||||
|
result.push(dataType === 'int' && value > 2147483647 ? value - 4294967296 : value >>> 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'long':
|
||||||
|
case 'ulong':
|
||||||
|
for (let i = 0; i < bytes.length - 7; i += 8) {
|
||||||
|
const orderedBytes = applyByteOrder(bytes.slice(i, i + 8), byteOrder, 8);
|
||||||
|
let value = BigInt(0);
|
||||||
|
for (let j = 0; j < 8; j++) {
|
||||||
|
value |= BigInt(orderedBytes[j]) << BigInt(j * 8);
|
||||||
|
}
|
||||||
|
result.push(Number(value));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'float':
|
case 'float':
|
||||||
|
for (let i = 0; i < bytes.length - 3; i += 4) {
|
||||||
|
const orderedBytes = applyByteOrder([bytes[i], bytes[i + 1], bytes[i + 2], bytes[i + 3]], byteOrder, 4);
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
for (let j = 0; j < 4; j++) {
|
||||||
|
view.setUint8(j, orderedBytes[j]);
|
||||||
|
}
|
||||||
|
const floatValue = view.getFloat32(0, true);
|
||||||
|
result.push(floatValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'double':
|
case 'double':
|
||||||
return result.some(v => isFinite(v) && !isNaN(v) && Math.abs(v) < 1e10);
|
for (let i = 0; i < bytes.length - 7; i += 8) {
|
||||||
|
const orderedBytes = applyByteOrder(bytes.slice(i, i + 8), byteOrder, 8);
|
||||||
|
const buffer = new ArrayBuffer(8);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
for (let j = 0; j < 8; j++) {
|
||||||
|
view.setUint8(j, orderedBytes[j]);
|
||||||
|
}
|
||||||
|
const doubleValue = view.getFloat64(0, true);
|
||||||
|
result.push(doubleValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'ascii':
|
case 'ascii':
|
||||||
return /[a-zA-Z0-9]/.test(result[0]);
|
const asciiString = bytes.map(byte =>
|
||||||
default:
|
byte >= 32 && byte <= 126 ? String.fromCharCode(byte) : '·'
|
||||||
return true;
|
).join('');
|
||||||
|
result.push(asciiString);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const displayValue = formatDisplayValue(result, dataType);
|
||||||
|
const isValid = validateResult(result, dataType);
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: result,
|
||||||
|
displayValue: displayValue,
|
||||||
|
isValid: isValid
|
||||||
|
};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return {success: false, error: error.message};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function displayRawData() {
|
|
||||||
const rawDataDisplay = document.getElementById('rawDataDisplay');
|
function applyByteOrder(bytes, byteOrder, expectedLength) {
|
||||||
const bytes = convertToBytes(currentData, currentInputFormat);
|
const paddedBytes = [...bytes];
|
||||||
|
while (paddedBytes.length < expectedLength) {
|
||||||
rawDataDisplay.innerHTML = `
|
paddedBytes.push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (byteOrder) {
|
||||||
|
case 'ABCD':
|
||||||
|
return paddedBytes.reverse();
|
||||||
|
case 'DCBA':
|
||||||
|
return paddedBytes;
|
||||||
|
case 'CDAB':
|
||||||
|
if (expectedLength === 4) {
|
||||||
|
return [paddedBytes[2], paddedBytes[3], paddedBytes[0], paddedBytes[1]];
|
||||||
|
} else if (expectedLength === 2) {
|
||||||
|
return [paddedBytes[1], paddedBytes[0]];
|
||||||
|
} else if (expectedLength === 8) {
|
||||||
|
return [paddedBytes[6], paddedBytes[7], paddedBytes[4], paddedBytes[5], paddedBytes[2], paddedBytes[3], paddedBytes[0], paddedBytes[1]];
|
||||||
|
}
|
||||||
|
return paddedBytes;
|
||||||
|
case 'BADC':
|
||||||
|
if (expectedLength === 4) {
|
||||||
|
return [paddedBytes[1], paddedBytes[0], paddedBytes[3], paddedBytes[2]];
|
||||||
|
} else if (expectedLength === 2) {
|
||||||
|
return [paddedBytes[1], paddedBytes[0]];
|
||||||
|
} else if (expectedLength === 8) {
|
||||||
|
return [paddedBytes[1], paddedBytes[0], paddedBytes[3], paddedBytes[2], paddedBytes[5], paddedBytes[4], paddedBytes[7], paddedBytes[6]];
|
||||||
|
}
|
||||||
|
return paddedBytes;
|
||||||
|
default:
|
||||||
|
return paddedBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDisplayValue(result, dataType) {
|
||||||
|
if (result.length === 0) return '';
|
||||||
|
|
||||||
|
switch (dataType) {
|
||||||
|
case 'float':
|
||||||
|
case 'double':
|
||||||
|
return result.map(v => {
|
||||||
|
if (isNaN(v) || !isFinite(v)) {
|
||||||
|
return v.toString();
|
||||||
|
}
|
||||||
|
return parseFloat(v.toFixed(6));
|
||||||
|
}).join(', ');
|
||||||
|
case 'bool':
|
||||||
|
return result.slice(0, 32).map(v => v ? '1' : '0').join('');
|
||||||
|
case 'ascii':
|
||||||
|
return result[0];
|
||||||
|
default:
|
||||||
|
return result.join(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateResult(result, dataType) {
|
||||||
|
if (result.length === 0) return false;
|
||||||
|
|
||||||
|
switch (dataType) {
|
||||||
|
case 'float':
|
||||||
|
case 'double':
|
||||||
|
return result.some(v => isFinite(v) && !isNaN(v) && Math.abs(v) < 1e10);
|
||||||
|
case 'ascii':
|
||||||
|
return /[a-zA-Z0-9]/.test(result[0]);
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayRawData() {
|
||||||
|
const rawDataDisplay = document.getElementById('rawDataDisplay');
|
||||||
|
const bytes = convertToBytes(currentData, currentInputFormat);
|
||||||
|
|
||||||
|
rawDataDisplay.innerHTML = `
|
||||||
<div class="process-step">
|
<div class="process-step">
|
||||||
<div class="step-title">📥 ${t('input_data')} (${currentInputFormat})</div>
|
<div class="step-title">📥 ${t('input_data')} (${currentInputFormat})</div>
|
||||||
<div class="step-content">[${currentData.join(', ')}]</div>
|
<div class="step-content">[${currentData.join(', ')}]</div>
|
||||||
@@ -2740,67 +3122,62 @@
|
|||||||
<div class="step-content">[${bytes.map(b => '0b' + b.toString(2).padStart(8, '0')).join(', ')}]</div>
|
<div class="step-content">[${bytes.map(b => '0b' + b.toString(2).padStart(8, '0')).join(', ')}]</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchTab(tabName) {
|
||||||
|
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||||||
|
|
||||||
|
event.target.classList.add('active');
|
||||||
|
document.getElementById(tabName).classList.add('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 程序化切换标签(不依赖event对象)
|
||||||
|
function switchTabProgrammatically(tabName) {
|
||||||
|
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
||||||
|
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
||||||
|
|
||||||
|
// 根据标签名找到对应的按钮并激活
|
||||||
|
const tabMapping = {
|
||||||
|
'smart-results': 0,
|
||||||
|
'all-results': 1,
|
||||||
|
'raw-data': 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = document.querySelectorAll('.tab-btn');
|
||||||
|
const targetIndex = tabMapping[tabName];
|
||||||
|
if (targetIndex !== undefined && buttons[targetIndex]) {
|
||||||
|
buttons[targetIndex].classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyResult(text) {
|
document.getElementById(tabName).classList.add('active');
|
||||||
navigator.clipboard.writeText(text).then(() => {
|
}
|
||||||
console.log('已复制到剪贴板');
|
|
||||||
});
|
|
||||||
}
|
// 清空之前的结果
|
||||||
|
function clearPreviousResults() {
|
||||||
function switchTab(tabName) {
|
allResults = [];
|
||||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
document.getElementById('allResultsGrid').innerHTML = '';
|
||||||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
document.getElementById('smartResultsGrid').innerHTML = '';
|
||||||
|
document.getElementById('rawDataDisplay').innerHTML = '';
|
||||||
event.target.classList.add('active');
|
document.getElementById('statisticsPanel').style.display = 'none';
|
||||||
document.getElementById(tabName).classList.add('active');
|
updateResultCounter(null); // 隐藏计数器
|
||||||
}
|
}
|
||||||
|
|
||||||
// 程序化切换标签(不依赖event对象)
|
function clearInput() {
|
||||||
function switchTabProgrammatically(tabName) {
|
document.getElementById('rawData').value = '';
|
||||||
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
|
document.getElementById('inputValidation').innerHTML = '';
|
||||||
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
|
document.getElementById('resultsSection').style.display = 'none';
|
||||||
|
document.getElementById('smartDetection').style.display = 'none';
|
||||||
// 根据标签名找到对应的按钮并激活
|
document.getElementById('recommendationPanel').style.display = 'none';
|
||||||
const tabMapping = {
|
document.getElementById('autoSelectNotice').style.display = 'none';
|
||||||
'smart-results': 0,
|
|
||||||
'all-results': 1,
|
// 清空所有相关数据和结果
|
||||||
'raw-data': 2
|
currentData = [];
|
||||||
};
|
smartAnalysisResults = {};
|
||||||
|
recommendedConfigs = [];
|
||||||
const buttons = document.querySelectorAll('.tab-btn');
|
clearPreviousResults();
|
||||||
const targetIndex = tabMapping[tabName];
|
}
|
||||||
if (targetIndex !== undefined && buttons[targetIndex]) {
|
</script>
|
||||||
buttons[targetIndex].classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById(tabName).classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空之前的结果
|
|
||||||
function clearPreviousResults() {
|
|
||||||
allResults = [];
|
|
||||||
document.getElementById('allResultsGrid').innerHTML = '';
|
|
||||||
document.getElementById('smartResultsGrid').innerHTML = '';
|
|
||||||
document.getElementById('rawDataDisplay').innerHTML = '';
|
|
||||||
document.getElementById('statisticsPanel').style.display = 'none';
|
|
||||||
updateResultCounter(null); // 隐藏计数器
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearInput() {
|
|
||||||
document.getElementById('rawData').value = '';
|
|
||||||
document.getElementById('inputValidation').innerHTML = '';
|
|
||||||
document.getElementById('resultsSection').style.display = 'none';
|
|
||||||
document.getElementById('smartDetection').style.display = 'none';
|
|
||||||
document.getElementById('recommendationPanel').style.display = 'none';
|
|
||||||
document.getElementById('autoSelectNotice').style.display = 'none';
|
|
||||||
|
|
||||||
// 清空所有相关数据和结果
|
|
||||||
currentData = [];
|
|
||||||
smartAnalysisResults = {};
|
|
||||||
recommendedConfigs = [];
|
|
||||||
clearPreviousResults();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user