1;由于艾莫迅云组态只支持500个变量,所以我要非常节的使用变量。
2;双字节和四字节与位的关系,代码如下, MW200=M201+M200 ;

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>西门子位选计算器 | 16位/32位双模式 | 紧凑布局</title>
<style>
* {
box-sizing: border-box;
user-select: none;
}
body {
background: linear-gradient(145deg, #eef2f7 0%, #d0d9e4 100%);
font-family: 'Segoe UI', 'Roboto', 'Helvetica Neue', sans-serif;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
/* 主卡片 */
.card {
max-width: 1400px;
width: 100%;
background: #ffffffea;
border-radius: 48px;
box-shadow: 0 25px 40px rgba(0, 0, 0, 0.2), 0 4px 12px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: all 0.2s;
}
/* 头部 */
.header {
background: #13293D;
color: white;
padding: 18px 28px 16px 28px;
text-align: center;
position: relative;
}
.header h1 {
margin: 0;
font-weight: 700;
font-size: 1.7rem;
letter-spacing: -0.3px;
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
.header h1 span {
background: #2C7DA0;
padding: 0 14px;
border-radius: 60px;
font-size: 1rem;
font-family: 'Courier New', monospace;
font-weight: 500;
}
.sub {
margin-top: 8px;
font-size: 0.8rem;
opacity: 0.8;
font-family: monospace;
background: #00000040;
display: inline-block;
padding: 3px 16px;
border-radius: 40px;
}
.mode-switch-area {
position: absolute;
top: 18px;
right: 28px;
}
.btn-mode {
background: #f39c12;
border: none;
padding: 6px 18px;
border-radius: 40px;
font-weight: 700;
font-size: 0.85rem;
cursor: pointer;
transition: 0.1s;
color: #1e2a36;
box-shadow: 0 2px 6px rgba(0,0,0,0.2);
}
.btn-mode:hover {
background: #e67e22;
transform: scale(0.97);
color: white;
}
/* 控制栏 */
.control-bar {
background: #f4f7fc;
padding: 12px 28px;
border-bottom: 1px solid #dce5ef;
display: flex;
gap: 16px;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
}
.btn-group {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.btn {
border: none;
padding: 6px 20px;
border-radius: 40px;
font-weight: 600;
font-size: 0.85rem;
cursor: pointer;
transition: 0.12s linear;
box-shadow: 0 1px 2px rgba(0,0,0,0.1);
background: white;
}
.btn-primary {
background: #1e6f5c;
color: white;
border: none;
}
.btn-primary:hover {
background: #0e5a49;
transform: scale(0.97);
}
.btn-secondary {
background: #eef2f8;
color: #2c5a74;
border: 1px solid #cbdbe0;
}
.btn-secondary:hover {
background: #e2e8f2;
}
.info-badge {
background: #e9edf2;
padding: 5px 14px;
border-radius: 32px;
font-size: 0.75rem;
font-family: monospace;
color: #1f5068;
}
/* 位选择面板 - 更紧凑 */
.bits-panel {
padding: 16px 24px 16px 24px;
background: #ffffff;
transition: 0.2s;
max-height: none;
}
.bit-row-compact {
background: #fbfdff;
border-radius: 28px;
padding: 12px 18px;
margin-bottom: 18px;
border: 1px solid #e2edf7;
box-shadow: 0 1px 4px rgba(0,0,0,0.02);
}
.row-header {
display: flex;
justify-content: space-between;
align-items: baseline;
flex-wrap: wrap;
margin-bottom: 12px;
padding-left: 6px;
}
.row-title {
font-weight: 800;
font-size: 1rem;
color: #1f5068;
background: #e9f0f5;
padding: 3px 14px;
border-radius: 30px;
}
.weight-note {
font-size: 0.65rem;
font-family: 'Courier New', monospace;
color: #4d708b;
background: #eef3fa;
padding: 2px 10px;
border-radius: 20px;
}
.checkbox-line {
display: flex;
flex-wrap: wrap;
gap: 8px;
justify-content: flex-start;
align-items: center;
}
/* 更紧凑的位块 */
.bit-item {
display: flex;
flex-direction: column;
align-items: center;
background: #ffffff;
border-radius: 24px;
padding: 5px 8px;
min-width: 62px;
cursor: pointer;
border: 1px solid #cfdfef;
transition: all 0.1s;
}
.bit-item:hover {
background: #eef3fc;
border-color: #2C7DA0;
transform: translateY(-1px);
}
.bit-item input {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #1e6f5c;
margin-bottom: 4px;
}
.bit-label {
font-weight: 700;
font-size: 0.75rem;
color: #1f3b4c;
}
.bit-addr {
font-size: 0.55rem;
font-family: monospace;
color: #6f8eaa;
margin-top: 2px;
}
/* 结果区域 */
.result-area {
background: #eef3fc;
margin: 0 24px 24px 24px;
border-radius: 34px;
padding: 14px 24px;
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 16px;
border: 1px solid #cde0ef;
}
.result-card {
background: #13293D;
border-radius: 28px;
padding: 8px 20px;
flex: 1;
min-width: 140px;
text-align: center;
transition: 0.1s;
}
.clickable-copy {
cursor: pointer;
}
.clickable-copy:hover {
background: #1e405b;
transform: scale(1.01);
}
.result-label {
font-size: 0.65rem;
text-transform: uppercase;
letter-spacing: 1px;
color: #bdd4e8;
}
.result-value {
font-size: 1.6rem;
font-weight: 800;
font-family: 'Courier New', monospace;
color: #f5d742;
background: #00000030;
display: inline-block;
padding: 0 10px;
border-radius: 28px;
line-height: 1.2;
}
.result-hex {
font-size: 0.9rem;
font-family: monospace;
background: #2e4a62;
padding: 4px 14px;
border-radius: 28px;
color: #c9f0ff;
}
.toast-msg {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%) scale(0.9);
background: #1e2f3c;
color: #d4f1f9;
padding: 8px 20px;
border-radius: 60px;
font-family: monospace;
font-weight: 500;
font-size: 0.85rem;
z-index: 1000;
opacity: 0;
transition: opacity 0.2s, transform 0.2s;
pointer-events: none;
white-space: nowrap;
}
.toast-msg.show {
opacity: 1;
transform: translateX(-50%) scale(1);
}
.footer-note {
font-size: 0.65rem;
text-align: center;
padding: 12px 20px 18px;
color: #4b6e8c;
background: #f8fafd;
border-top: 1px solid #e2ecf5;
}
@media (max-width: 1000px) {
.mode-switch-area {
position: static;
text-align: center;
margin-top: 8px;
}
.header {
display: flex;
flex-direction: column;
align-items: center;
}
.result-value {
font-size: 1.3rem;
}
.bit-item {
min-width: 56px;
padding: 4px 6px;
}
.bit-label {
font-size: 0.7rem;
}
}
@media (max-width: 760px) {
.checkbox-line {
gap: 6px;
}
.bit-item {
min-width: 52px;
}
}
</style>
</head>
<body>
<div class="card">
<div class="header">
<h1>
⚙️ S7-1500 位选计算器
<span id="modeBadge">双字节(16位)</span>
</h1>
<div class="sub" id="subDesc">第1~16位 · 自动计算 INT (MW200)</div>
<div class="mode-switch-area">
<button class="btn-mode" id="toggleModeBtn">🔄 换四字节</button>
</div>
</div>
<div class="control-bar">
<div class="btn-group">
<button class="btn btn-primary" id="allOnBtn">🔆 全开</button>
<button class="btn btn-secondary" id="allOffBtn">⛔ 全关</button>
</div>
<div class="info-badge" id="formulaHint">📐 公式: 高位(1~8) ×256 + 低位(9~16)</div>
</div>
<div class="bits-panel" id="bitsPanel">
<!-- 动态渲染位区域 16位模式 或 32位模式(优化紧凑布局) -->
</div>
<div class="result-area">
<div class="result-card clickable-copy" id="copyDecCard">
<div class="result-label" id="decLabel">📟 十进制 (DEC) 👆 点击复制</div>
<div class="result-value" id="decValue">0</div>
</div>
<div class="result-card">
<div class="result-label">🔢 十六进制 (HEX)</div>
<div class="result-hex" id="hexValue">0x0000</div>
</div>
</div>
<div class="footer-note">
✅ 点击任意位自动计算数值。单击十进制区域复制数值。<br>
💡 <strong>32位模式 (DWord) 采用双行布局(每行16位),无需滚动即可完整勾选</strong>
</div>
</div>
<div id="copyToast" class="toast-msg">📋 已复制到剪贴板</div>
<script>
// ---------- 模式管理 ----------
let currentMode = "16bit"; // "16bit" 或 "32bit"
// ----- 16位模式定义 (基于MW200) -----
const highBits16 = [
{ id: 1, label: "第1位", addr: "M201.0", weight: 1, group: "high" },
{ id: 2, label: "第2位", addr: "M201.1", weight: 2, group: "high" },
{ id: 3, label: "第3位", addr: "M201.2", weight: 4, group: "high" },
{ id: 4, label: "第4位", addr: "M201.3", weight: 8, group: "high" },
{ id: 5, label: "第5位", addr: "M201.4", weight: 16, group: "high" },
{ id: 6, label: "第6位", addr: "M201.5", weight: 32, group: "high" },
{ id: 7, label: "第7位", addr: "M201.6", weight: 64, group: "high" },
{ id: 8, label: "第8位", addr: "M201.7", weight: 128, group: "high" }
];
const lowBits16 = [
{ id: 9, label: "第9位", addr: "M200.0", weight: 1, group: "low" },
{ id: 10, label: "第10位", addr: "M200.1", weight: 2, group: "low" },
{ id: 11, label: "第11位", addr: "M200.2", weight: 4, group: "low" },
{ id: 12, label: "第12位", addr: "M200.3", weight: 8, group: "low" },
{ id: 13, label: "第13位", addr: "M200.4", weight: 16, group: "low" },
{ id: 14, label: "第14位", addr: "M200.5", weight: 32, group: "low" },
{ id: 15, label: "第15位", addr: "M200.6", weight: 64, group: "low" },
{ id: 16, label: "第16位", addr: "M200.7", weight: 128, group: "low" }
];
// ----- 32位模式定义 (为了紧凑布局: 第1~16位一行,第17~32位一行) -----
// 第1~8位 (M201.0~M201.7)
const bits1_8 = [...highBits16];
// 第9~16位 (M200.0~M200.7)
const bits9_16 = [...lowBits16];
// 第17~24位 (M203.0~M203.7)
const bits17_24 = [
{ id: 17, label: "第17位", addr: "M203.0", weight: 1, group: "ext_high" },
{ id: 18, label: "第18位", addr: "M203.1", weight: 2, group: "ext_high" },
{ id: 19, label: "第19位", addr: "M203.2", weight: 4, group: "ext_high" },
{ id: 20, label: "第20位", addr: "M203.3", weight: 8, group: "ext_high" },
{ id: 21, label: "第21位", addr: "M203.4", weight: 16, group: "ext_high" },
{ id: 22, label: "第22位", addr: "M203.5", weight: 32, group: "ext_high" },
{ id: 23, label: "第23位", addr: "M203.6", weight: 64, group: "ext_high" },
{ id: 24, label: "第24位", addr: "M203.7", weight: 128, group: "ext_high" }
];
// 第25~32位 (M202.0~M202.7)
const bits25_32 = [
{ id: 25, label: "第25位", addr: "M202.0", weight: 1, group: "ext_low" },
{ id: 26, label: "第26位", addr: "M202.1", weight: 2, group: "ext_low" },
{ id: 27, label: "第27位", addr: "M202.2", weight: 4, group: "ext_low" },
{ id: 28, label: "第28位", addr: "M202.3", weight: 8, group: "ext_low" },
{ id: 29, label: "第29位", addr: "M202.4", weight: 16, group: "ext_low" },
{ id: 30, label: "第30位", addr: "M202.5", weight: 32, group: "ext_low" },
{ id: 31, label: "第31位", addr: "M202.6", weight: 64, group: "ext_low" },
{ id: 32, label: "第32位", addr: "M202.7", weight: 128, group: "ext_low" }
];
// 合并第1~16位为一个数组 (顺序显示)
const firstHalf32 = [...bits1_8, ...bits9_16];
// 合并第17~32位为一个数组
const secondHalf32 = [...bits17_24, ...bits25_32];
let currentCheckboxes = [];
let bitsContainer = document.getElementById('bitsPanel');
const decSpan = document.getElementById('decValue');
const hexSpan = document.getElementById('hexValue');
const modeBadge = document.getElementById('modeBadge');
const subDesc = document.getElementById('subDesc');
const formulaHint = document.getElementById('formulaHint');
const toggleBtn = document.getElementById('toggleModeBtn');
// Toast
function showToast(msg, duration=1500) {
const toast = document.getElementById('copyToast');
toast.textContent = msg;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), duration);
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
showToast(`📋 已复制: ${text}`);
} catch(e) {
let ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showToast(`📋 已复制: ${text}`);
}
}
// 计算当前模式下的数值
function computeCurrentValue() {
if (currentMode === "16bit") {
let highSum = 0, lowSum = 0;
currentCheckboxes.forEach(cb => {
if (!cb.checked) return;
let weight = parseInt(cb.dataset.weight);
let grp = cb.dataset.group;
if (grp === 'high') highSum += weight;
else if (grp === 'low') lowSum += weight;
});
return (highSum * 256) + lowSum;
}
else { // 32bit 紧凑模式: 第1~16位(高字MW200),第17~32位(低字MW202)
let highWordSum = 0; // 第1~16位权重组合出的实际数值
let lowWordSum = 0; // 第17~32位权重组合出的实际数值
// 分别计算第1~16位: 按照规则 high组(1-8)权重和*256 + low组(9-16)权重和
let highSum16 = 0, lowSum16 = 0;
let highExtSum = 0, lowExtSum = 0;
currentCheckboxes.forEach(cb => {
if (!cb.checked) return;
let w = parseInt(cb.dataset.weight);
let grp = cb.dataset.group;
if (grp === 'high') highSum16 += w;
else if (grp === 'low') lowSum16 += w;
else if (grp === 'ext_high') highExtSum += w;
else if (grp === 'ext_low') lowExtSum += w;
});
let highWord = (highSum16 * 256) + lowSum16; // MW200 0~65535
let lowWord = (highExtSum * 256) + lowExtSum; // MW202 0~65535
// MD200 = highWord * 65536 + lowWord
return (highWord * 65536) + lowWord;
}
}
function updateDisplay() {
let val = computeCurrentValue();
decSpan.innerText = val;
let hexStr = val.toString(16).toUpperCase();
while (hexStr.length < (currentMode === "16bit" ? 4 : 8)) hexStr = '0' + hexStr;
hexSpan.innerText = '0x' + hexStr;
}
// 渲染单行(通用,用于16bit两行 或 32bit两行)
function renderRow(title, note, bitsArray, groupName) {
const rowDiv = document.createElement('div');
rowDiv.className = 'bit-row-compact';
const headerDiv = document.createElement('div');
headerDiv.className = 'row-header';
headerDiv.innerHTML = `<span class="row-title">${title}</span><span class="weight-note">${note}</span>`;
rowDiv.appendChild(headerDiv);
const lineDiv = document.createElement('div');
lineDiv.className = 'checkbox-line';
bitsArray.forEach(bit => {
const item = document.createElement('div');
item.className = 'bit-item';
const cb = document.createElement('input');
cb.type = 'checkbox';
cb.dataset.weight = bit.weight;
cb.dataset.group = bit.group || groupName; // 使用bit内定义的group,兼容新旧
// 确保group正确传递: 对于16位模式 highBits16自带group, lowBits16自带group; 32位组合需要保留原group
if (!bit.group && groupName) cb.dataset.group = groupName;
else if (bit.group) cb.dataset.group = bit.group;
cb.id = `cb_${cb.dataset.group}_${bit.id}`;
const labelSpan = document.createElement('span');
labelSpan.className = 'bit-label';
labelSpan.innerText = bit.label;
const addrSpan = document.createElement('span');
addrSpan.className = 'bit-addr';
addrSpan.innerText = bit.addr;
item.appendChild(cb);
item.appendChild(labelSpan);
item.appendChild(addrSpan);
item.addEventListener('click', (e) => {
if (e.target !== cb) {
cb.checked = !cb.checked;
const ev = new Event('change', {bubbles: true});
cb.dispatchEvent(ev);
}
});
lineDiv.appendChild(item);
currentCheckboxes.push(cb);
});
rowDiv.appendChild(lineDiv);
bitsContainer.appendChild(rowDiv);
}
// 渲染界面(16位两行,32位两行紧凑)
function renderByMode() {
bitsContainer.innerHTML = '';
currentCheckboxes = [];
if (currentMode === "16bit") {
modeBadge.innerText = "MW200 (16位)";
subDesc.innerText = "第1~16位 · 自动计算 INT (MW200)";
formulaHint.innerHTML = "📐 公式: 高位(1~8) ×256 + 低位(9~16)";
// 两行:第1-8位,第9-16位
renderRow("🔹 第 1 ~ 8 位", "高字节 M201.0~M201.7 权重:1,2,4,8,16,32,64,128", highBits16, "high");
renderRow("🔸 第 9 ~ 16 位", "低字节 M200.0~M200.7 权重:1,2,4,8,16,32,64,128", lowBits16, "low");
}
else {
modeBadge.innerText = "MD200 (32位 DWord)";
subDesc.innerText = "第1~32位 · 自动计算 DWord (MD200) 紧凑双行";
formulaHint.innerHTML = "📐 32位公式: MD200 = MW200(第1~16位) ×65536 + MW202(第17~32位)";
// 紧凑布局:第一行显示第1~16位,第二行显示第17~32位
renderRow("🟦 第 1 ~ 16 位", "MW200 (M201.0~M201.7 / M200.0~M200.7) 权重累加后组合", firstHalf32, "");
renderRow("🟩 第 17 ~ 32 位", "MW202 (M203.0~M203.7 / M202.0~M202.7) 权重累加后组合", secondHalf32, "");
}
// 绑定变化事件
currentCheckboxes.forEach(cb => {
cb.addEventListener('change', () => updateDisplay());
});
updateDisplay();
}
// 全开/全关
function allOn() {
currentCheckboxes.forEach(cb => cb.checked = true);
updateDisplay();
}
function allOff() {
currentCheckboxes.forEach(cb => cb.checked = false);
updateDisplay();
}
// 切换模式
function toggleMode() {
if (currentMode === "16bit") {
currentMode = "32bit";
toggleBtn.innerText = "🔄 换双字节 (16位)";
} else {
currentMode = "16bit";
toggleBtn.innerText = "🔄 换四字节 (32位)";
}
renderByMode();
}
// 复制十进制
function setupCopy() {
const copyCard = document.getElementById('copyDecCard');
copyCard.addEventListener('click', async () => {
let val = computeCurrentValue();
await copyToClipboard(val.toString());
});
}
// 初始化
function init() {
renderByMode();
setupCopy();
document.getElementById('allOnBtn').addEventListener('click', allOn);
document.getElementById('allOffBtn').addEventListener('click', allOff);
toggleBtn.addEventListener('click', toggleMode);
}
init();
</script>
</body>
</html>
