device-ctrl/z-doc/wbs/index.html

351 lines
11 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>地磅协议解析/生成工具</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: "微软雅黑", "Segoe UI", sans-serif;
}
html {
width: 100vw;
height: 100vh;
}
body {
background: #cfe3ee;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.container {
width: 90%;
background: #ffffff;
padding: 30px;
border-radius: 16px;
box-shadow: 0 8px 24px rgba(82, 146, 192, 0.08);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.container:hover {
box-shadow: 0 12px 32px rgba(82, 146, 192, 0.12);
}
h1 {
text-align: center;
margin-bottom: 35px;
font-size: 26px;
font-weight: 600;
padding-bottom: 15px;
border-bottom: 1px solid #bedeed;
color: #355389;
}
/* 新增:卡片左右布局容器 */
.cards-wrapper {
display: flex;
gap: 25px; /* 两个卡片之间的间距 */
flex-wrap: wrap; /* 移动端自动换行,保证响应式 */
}
.section {
/* 调整:左右布局,平分宽度,不换行时占比均等 */
flex: 1;
min-width: 300px; /* 最小宽度,保证移动端显示正常 */
margin-bottom: 0; /* 移除原有底部间距改用wrapper的gap */
padding: 20px;
background: #f5fafe;
border-radius: 12px;
border: 1px solid rgba(82, 146, 192, 0.1);
transition: box-shadow 0.3s ease;
}
.section:hover {
box-shadow: 0 4px 12px rgba(82, 146, 192, 0.05);
}
h2 {
/* 调整:标题居中显示 */
text-align: center;
color: #355389;
font-size: 19px;
margin-bottom: 18px;
padding: 6px 0;
/* 移除左侧边框,改为底部边框,居中更协调 */
border-left: none;
border-bottom: 2px solid #5292c0;
display: inline-block; /* 底部边框适配文字宽度 */
margin-left: auto;
margin-right: auto;
display: flex;
justify-content: center;
}
input[type="text"] {
width: 100%;
padding: 14px 16px;
border: 1px solid #94d3f2;
border-radius: 10px;
font-size: 15px;
resize: vertical;
min-height: 60px;
margin-bottom: 15px;
background: #ffffff;
transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
textarea:focus, input[type="text"]:focus {
outline: none;
border-color: #5292c0;
box-shadow: 0 0 0 3px rgba(148, 211, 242, 0.1);
background: #ffffff;
}
button {
/* 调整:按钮宽度充满父元素 */
width: 100%;
background: #3f689e;
color: #ffffff;
border: none;
padding: 11px 22px;
border-radius: 10px;
font-size: 15px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
box-shadow: 0 4px 12px rgba(63, 104, 158, 0.15);
}
button:hover {
background: #355389;
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(63, 104, 158, 0.2);
}
button:active {
transform: translateY(0);
box-shadow: 0 4px 8px rgba(63, 104, 158, 0.15);
}
.result {
margin-top: 18px;
padding: 16px;
background: #ffffff;
border-radius: 10px;
border: 1px solid rgba(82, 146, 192, 0.1);
min-height: 60px;
white-space: pre-wrap;
font-size: 14px;
line-height: 1.6;
color: #355389;
transition: box-shadow 0.3s ease;
height: 300px;
}
.result:hover {
box-shadow: 0 4px 12px rgba(82, 146, 192, 0.05);
}
@media (max-width: 600px) {
.container {
padding: 20px;
}
h1 {
font-size: 22px;
margin-bottom: 25px;
}
.section {
padding: 15px;
min-width: 100%; /* 移动端独占一行 */
}
/* 移动端按钮保持100%宽度,无需额外修改 */
}
</style>
</head>
<body>
<div class="container">
<h1>地磅协议解析/生成工具</h1>
<!-- 新增:卡片左右布局包裹容器 -->
<div class="cards-wrapper">
<!-- 协议解析区域 -->
<form class="section" id="parseForm">
<h2>1. 协议解析</h2>
<input spellcheck="false" type="text" id="parseInput" placeholder="请输入 16 进制字节序列" autocomplete="off">
<button type="submit">解析</button>
<div class="result" id="parseResult">解析结果将显示在这里</div>
</form>
<!-- 测试数据生成区域 -->
<form class="section" id="generateForm">
<h2>2. 测试数据生成</h2>
<input spellcheck="false" type="text" id="generateInput" placeholder="请输入重量" autocomplete="off">
<button type="submit">生成</button>
<div class="result" id="generateResult">生成结果将显示在这里</div>
</form>
</div>
</div>
<script>
function parseProtocol() {
const input = document.getElementById('parseInput').value.trim();
const resultEl = document.getElementById('parseResult');
if (!input) {
resultEl.textContent = "请输入16进制数据";
return false;
}
const bytes = input.split(/\s+/);
if (bytes.length !== 12) {
resultEl.textContent = "错误数据必须是12个16进制字节";
return false;
}
// 验证起始和结束字节
if (bytes[0] !== '02' || bytes[11] !== '03') {
resultEl.textContent = "错误起始字节必须是02结束字节必须是03";
return false;
}
try {
// 1. 符号位第2字节
const signByte = bytes[1];
const sign = signByte === '2B' ? '+' : signByte === '2D' ? '-' : '';
if (!sign) {
throw new Error("符号位无效应为2B或2D");
}
// 2. 称量数据第3-8字节转为数字字符
const weightChars = bytes.slice(2, 8).map(b => String.fromCharCode(parseInt(b, 16))).join('');
const weightNum = parseFloat(weightChars);
if (isNaN(weightNum)) {
throw new Error("称量数据无效");
}
// 3. 小数点位第9字节从右到左的位数
const decimalByte = bytes[8];
const decimalDigit = parseInt(decimalByte, 16) - 0x30;
if (decimalDigit < 0 || decimalDigit > 4) {
throw new Error("小数点位无效应为0-4");
}
// 计算最终重量
const weight = (sign === '-' ? -1 : 1) * (weightNum / Math.pow(10, decimalDigit));
// 4. 校验异或(可选验证)
const xorHigh = String.fromCharCode(parseInt(bytes[9], 16));
const xorLow = String.fromCharCode(parseInt(bytes[10], 16));
const xorVal = xorHigh + xorLow;
const calcXor = bytes.slice(1, 9).reduce((acc, b) => acc ^ parseInt(b, 16), 0).toString(16).padStart(2, '0').toUpperCase()
const checkPass = xorVal === calcXor;
resultEl.textContent = `解析成功:
协议字节序列:${input}
重量:${weight} 千克
校验结果:${checkPass ? '通过' : '不通过'}
详细信息:
- 起始字节02
- 符号:${sign}${signByte}
- 称量数据:${weightChars}${bytes.slice(2, 8).join(' ')}
- 小数位数:${decimalDigit} 位(${decimalByte}
- 异或校验:${calcXor}${bytes[9]} ${bytes[10]}
- 结束字节03`;
} catch (e) {
resultEl.textContent = `解析失败:${e.message}`;
}
return false;
}
function generateProtocol() {
let input = document.getElementById('generateInput').value.trim();
const resultEl = document.getElementById('generateResult');
if (!input) {
resultEl.textContent = "请输入重量数据";
return;
}
try {
// 1. 符号位
const sign = input.startsWith('-') ? '-' : '+';
const signByte = sign === '+' ? '2B' : '2D';
let rawinput = input
if (input.startsWith('+') || input.startsWith('-')) {
input = input.substring(1);
}
// 2. 处理重量数值(转为字符串,提取整数+小数部分)
const [integerPart, decimalPart = ''] = input.split('.');
console.log(input,integerPart,decimalPart)
const fullNumStr = (integerPart + decimalPart).padStart(6, '0'); // 补0到6位
console.log(fullNumStr,444444)
if (fullNumStr.length !== 6) {
throw new Error("重量数据超出6位有效数字");
}
// 3. 小数点位(从右到左的位数)
const decimalDigit = decimalPart.length;
console.log(decimalDigit);
if (decimalDigit < 0 || decimalDigit > 4) {
throw new Error("小数位数必须在0-4之间");
}
const decimalByte = (0x30 + decimalDigit).toString(16).toUpperCase();
// 4. 构造数据字节第3-8字节每个字符转ASCII的16进制
const dataBytes = fullNumStr.split('').map(c => c.charCodeAt(0).toString(16).toUpperCase());
// 5. 构造前9字节用于计算异或
const preBytes = [signByte, ...dataBytes, decimalByte];
// 计算异或校验值
const xorVal = preBytes.reduce((acc, b) => acc ^ parseInt(b, 16), 0);
// 6. 异或校验转ASCII高4位和低4位分别处理
const xorHigh = (xorVal >> 4) & 0x0F;
const xorLow = xorVal & 0x0F;
const xorHighByte = (xorHigh <= 9 ? 0x30 : 0x37) + xorHigh;
const xorLowByte = (xorLow <= 9 ? 0x30 : 0x37) + xorLow;
const xorBytes = [
xorHighByte.toString(16).toUpperCase(),
xorLowByte.toString(16).toUpperCase()
];
// 7. 完整字节序列
const fullBytes = ['02', ...preBytes, ...xorBytes, '03'];
resultEl.textContent = `生成成功:
原始重量:${rawinput} 千克
协议字节序列:${fullBytes.join(' ')}
详细信息:
- 起始字节02
- 符号:${sign}${signByte}
- 称量数据:${fullNumStr}${dataBytes.join(' ')}
- 小数位数:${decimalDigit} 位(${decimalByte}
- 异或校验:${xorVal.toString(16).toUpperCase().padStart(2, '0')}${xorBytes[0]} ${xorBytes[1]}
- 结束字节03`;
} catch (e) {
resultEl.textContent = `生成失败:${e.message}`;
}
}
document.getElementById('parseForm').addEventListener('submit', function (e) {
e.preventDefault();
parseProtocol();
});
document.getElementById('generateForm').addEventListener('submit', function (e) {
e.preventDefault();
generateProtocol();
});
</script>
</body>
</html>