Files
test-repo/docs/.vitepress/theme/components/appendix/data-encoding/DataTransmissionDemo.vue
T

371 lines
9.4 KiB
Vue
Raw Normal View History

<template>
<div class="transmission-demo">
<!-- Mode selector -->
<div class="mode-panel">
<div class="mode-label">选择传输方式然后点"发送数据包"</div>
<div class="mode-buttons">
<button
:class="['mode-btn', { active: mode === 'serial' }]"
@click="mode = 'serial'; reset()"
>
串行传输现代
</button>
<button
:class="['mode-btn', { active: mode === 'parallel' }]"
@click="mode = 'parallel'; reset()"
>
并行传输旧时代
</button>
</div>
</div>
<!-- Visualization -->
<div class="vis-area">
<!-- Sender -->
<div class="device sender">
<div class="device-icon">Tx</div>
<div class="device-label">发送方</div>
<div class="data-bits">
<span
v-for="(bit, i) in dataBits"
:key="i"
class="bit"
:class="{ sent: sentBits.includes(i) }"
>{{ bit }}</span>
</div>
</div>
<!-- Wire(s) -->
<div class="wire-container" :class="mode">
<div v-if="mode === 'serial'" class="wire-group serial">
<div class="wire-label">1 条线</div>
<div class="wire">
<span
v-for="(p, i) in particles"
:key="'p' + i"
class="particle"
:style="{ left: p.progress + '%', top: '50%' }"
>{{ p.bit }}</span>
</div>
</div>
<div v-if="mode === 'parallel'" class="wire-group parallel-group">
<div class="wire-label">8 条线</div>
<div v-for="l in 8" :key="l" class="wire">
<span
v-if="parallelParticle && parallelParticle.lane === l - 1"
class="particle"
:style="{ left: parallelParticle.progress + '%', top: '50%' }"
>{{ parallelBits[l - 1] || '·' }}</span>
</div>
</div>
</div>
<!-- Receiver -->
<div class="device receiver">
<div class="device-icon">Rx</div>
<div class="device-label">接收方</div>
<div class="received-bits">
<span
v-for="(bit, i) in receivedBits"
:key="'r' + i"
class="bit received"
>{{ bit }}</span>
</div>
<div v-if="checksumResult !== null" class="checksum-badge" :class="checksumResult ? 'ok' : 'fail'">
{{ checksumResult ? ' 校验通过' : ' 校验失败' }}
</div>
</div>
</div>
<!-- Status bar -->
<div class="status-bar">
<div class="status-item">
<span class="s-label">已发送</span>
<span class="s-val">{{ sentBits.length }} / {{ dataBits.length }} </span>
</div>
<div class="status-item">
<span class="s-label">传输速率</span>
<span class="s-val">{{ mode === 'serial' ? '1 位/次' : '8 位/次' }}</span>
</div>
<div class="status-item">
<span class="s-label">状态</span>
<span class="s-val" :class="statusColor">{{ statusText }}</span>
</div>
</div>
<!-- Send button -->
<button class="send-btn" :disabled="isSending" @click="send">
{{ isSending ? '传输中...' : '发送数据包' }}
</button>
<div class="note-box">
<strong>提示等等串行不是更慢吗</strong><br>
表面上是的但现代串行接口USB 4PCIe传输频率高达每秒 <strong>数百亿次</strong>而并行线路之间会产生 <em>信号串扰Crosstalk</em>反而限制了速度所以高速接口全面转向了串行
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const mode = ref('serial')
const dataBits = ref([1,0,1,1,0,0,1,0]) // "Hello" first byte 0b10110010
const sentBits = ref([])
const receivedBits = ref([])
const particles = ref([])
const parallelParticle = ref(null)
const parallelBits = ref([])
const isSending = ref(false)
const checksumResult = ref(null)
function reset() {
sentBits.value = []
receivedBits.value = []
particles.value = []
parallelParticle.value = null
parallelBits.value = []
checksumResult.value = null
isSending.value = false
}
const statusText = computed(() => {
if (isSending.value) return '传输中...'
if (receivedBits.value.length === dataBits.value.length) return '传输完成 ✓'
if (receivedBits.value.length > 0) return '接收中...'
return '就绪'
})
const statusColor = computed(() => {
if (receivedBits.value.length === dataBits.value.length) return 'green'
if (isSending.value) return 'yellow'
return ''
})
function sleep(ms) { return new Promise(r => setTimeout(r, ms)) }
async function send() {
if (isSending.value) return
reset()
isSending.value = true
if (mode.value === 'serial') {
await sendSerial()
} else {
await sendParallel()
}
// Checksum simulation
await sleep(400)
checksumResult.value = true // always pass in demo
isSending.value = false
}
async function sendSerial() {
for (let i = 0; i < dataBits.value.length; i++) {
sentBits.value.push(i)
const bit = dataBits.value[i]
// animate particle
const p = { bit, progress: 0, id: i }
particles.value.push(p)
for (let prog = 0; prog <= 100; prog += 10) {
p.progress = prog
await sleep(35)
}
particles.value = particles.value.filter(x => x !== p)
receivedBits.value.push(bit)
await sleep(30)
}
}
async function sendParallel() {
sentBits.value = dataBits.value.map((_, i) => i)
parallelBits.value = [...dataBits.value]
for (let prog = 0; prog <= 100; prog += 8) {
parallelParticle.value = { progress: prog, lane: Math.floor(Math.random() * 8) }
await sleep(40)
}
parallelParticle.value = null
receivedBits.value = [...dataBits.value]
}
</script>
<style scoped>
.transmission-demo {
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
background: var(--vp-c-bg-soft);
padding: 1.25rem;
margin: 1rem 0;
display: flex;
flex-direction: column;
gap: 1rem;
}
.mode-label {
font-size: 0.88rem;
font-weight: bold;
margin-bottom: 0.5rem;
}
.mode-buttons {
display: flex;
gap: 0.5rem;
}
.mode-btn {
padding: 0.4rem 0.9rem;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg);
border-radius: 4px;
cursor: pointer;
font-size: 0.85rem;
transition: all 0.2s;
}
.mode-btn.active {
background: var(--vp-c-brand);
color: white;
border-color: var(--vp-c-brand);
}
/* Visualization */
.vis-area {
display: flex;
align-items: center;
gap: 0.5rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 1rem;
min-height: 140px;
}
.device {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.3rem;
flex-shrink: 0;
width: 100px;
}
.device-icon { font-size: 2rem; }
.device-label { font-size: 0.8rem; font-weight: bold; color: var(--vp-c-text-2); }
.data-bits, .received-bits {
display: flex;
flex-wrap: wrap;
gap: 2px;
justify-content: center;
}
.bit {
width: 18px;
height: 18px;
border-radius: 3px;
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-alt);
font-family: monospace;
font-size: 0.7rem;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.bit.sent { background: var(--vp-c-brand-soft); border-color: var(--vp-c-brand); }
.bit.received { background: #d1fae5; border-color: #059669; color: #065f46; }
.checksum-badge {
margin-top: 4px;
font-size: 0.72rem;
padding: 2px 6px;
border-radius: 4px;
font-weight: bold;
}
.checksum-badge.ok { background: #d1fae5; color: #065f46; }
.checksum-badge.fail { background: #fee2e2; color: #991b1b; }
/* Wires */
.wire-container {
flex: 1;
display: flex;
flex-direction: column;
gap: 3px;
padding: 0 0.5rem;
}
.wire-label {
font-size: 0.72rem;
color: var(--vp-c-text-3);
text-align: center;
margin-bottom: 3px;
}
.wire {
position: relative;
height: 14px;
background: var(--vp-c-bg-alt);
border-radius: 2px;
border: 1px solid var(--vp-c-divider);
overflow: hidden;
}
.wire-group.serial .wire { height: 20px; }
.parallel-group { display: flex; flex-direction: column; gap: 2px; }
.particle {
position: absolute;
transform: translate(-50%, -50%);
font-family: monospace;
font-size: 0.65rem;
font-weight: bold;
color: var(--vp-c-brand);
transition: left 0.04s linear;
background: var(--vp-c-brand-soft);
border-radius: 2px;
padding: 1px 3px;
}
/* Status bar */
.status-bar {
display: flex;
gap: 1rem;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 6px;
padding: 0.6rem 0.85rem;
}
.status-item { display: flex; flex-direction: column; gap: 2px; }
.s-label { font-size: 0.72rem; color: var(--vp-c-text-3); }
.s-val { font-size: 0.88rem; font-weight: bold; }
.s-val.green { color: #059669; }
.s-val.yellow { color: #d97706; }
.send-btn {
padding: 0.5rem 1.2rem;
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: bold;
font-size: 0.95rem;
transition: opacity 0.2s;
align-self: flex-start;
}
.send-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.note-box {
background: var(--vp-c-bg-alt);
border-left: 4px solid var(--vp-c-yellow-1);
padding: 0.75rem 1rem;
border-radius: 0 6px 6px 0;
font-size: 0.83rem;
color: var(--vp-c-text-1);
line-height: 1.6;
}
</style>