uniapp+vue3完成CK通信协议(基于jjc-tcpTools)
1. TCP 服务封装 (tcpService.js)
export
class TcpService {
constructor(
) {
this.connections = uni.requireNativePlugin('jjc-tcpTools'
)
this.clients =
new Map(
) // 存储客户端连接
this.servers =
new Map(
) // 存储服务端实例
}
// 创建 TCP 服务端 (字符串模式)
createStringServer(port, onCreated, onData, onConnect
) {
this.connections.createTcpServer(
port,
(res
) =>
{
console.log('Server created:'
, res)
this.servers.set(port, {
type: 'string'
}
)
onCreated?.(res)
}
,
(res
) =>
{
console.log('Received data:'
, res)
this._addClient(res.ip, res.port)
onData?.(res)
}
,
(res
) =>
{
console.log('New connection:'
, res)
this._addClient(res.ip, res.port)
onConnect?.(res)
}
)
}
// 创建 TCP 服务端 (字节数组模式)
createByteServer(port, onCreated, onData
) {
this.connections.createByteTcpServer(
port,
(res
) =>
{
console.log('Byte server created:'
, res)
this.servers.set(port, {
type: 'byte'
}
)
onCreated?.(res)
}
,
(res
) =>
{
console.log('Received byte data:'
, res)
this._addClient(res.ip, res.port)
onData?.(res)
}
)
}
// 创建 TCP 客户端 (字符串模式)
createStringClient(ip, port, onCreated, onData
) {
this.connections.createTcpClient(
ip,
port,
(res
) =>
{
console.log('Client connected:'
, res)
this.clients.set(`${ip
}:${port
}`
, {
type: 'string'
}
)
onCreated?.(res)
}
,
(res
) =>
{
console.log('Client received:'
, res)
onData?.(res)
}
)
}
// 创建 TCP 客户端 (字节数组模式)
createByteClient(ip, port, onCreated, onData
) {
this.connections.createByteTcpClient(
ip,
port,
(res
) =>
{
console.log('Byte client connected:'
, res)
this.clients.set(`${ip
}:${port
}`
, {
type: 'byte'
}
)
onCreated?.(res)
}
,
(res
) =>
{
console.log('Byte client received:'
, res)
onData?.(res)
}
)
}
// 发送数据 (服务端群发字符串)
sendToAllClients(port, message, callback
) {
this.connections.sendTcpMSG2Client(
port,
message,
(res
) =>
{
console.log('Send to all:'
, res)
callback?.(res)
}
)
}
// 发送数据 (服务端指定客户端发字符串)
sendToClients(port, clients, message, callback
) {
this.connections.sendTcpMSG2Clients(
port,
JSON.stringify(clients)
,
message,
(res
) =>
{
console.log('Send to clients:'
, res)
callback?.(res)
}
)
}
// 发送数据 (客户端发字符串)
sendToServer(ip, port, message, callback
) {
this.connections.sendTcpMSG2Server(
ip,
port,
message,
(res
) =>
{
console.log('Send to server:'
, res)
callback?.(res)
}
)
}
// 发送字节数据 (服务端群发)
sendBytesToAllClients(port, byteArray, callback
) {
this.connections.sendTcpByteMSG2Client(
port,
byteArray,
(res
) =>
{
console.log('Send bytes to all:'
, res)
callback?.(res)
}
)
}
// 发送字节数据 (服务端指定客户端发)
sendBytesToClients(port, clients, byteArray, callback
) {
this.connections.sendTcpByteMSG2Clients(
port,
JSON.stringify(clients)
,
byteArray,
(res
) =>
{
console.log('Send bytes to clients:'
, res)
callback?.(res)
}
)
}
// 发送字节数据 (客户端发)
sendBytesToServer(ip, port, byteArray, callback
) {
this.connections.sendTcpMSGByte2Server(
ip,
port,
byteArray,
(res
) =>
{
console.log('Send bytes to server:'
, res)
callback?.(res)
}
)
}
// 设置客户端重连时间
setReconnectTime(ip, port, interval, callback
) {
this.connections.setReConnectTime(
ip,
port,
interval,
(res
) =>
{
console.log('Set reconnect time:'
, res)
callback?.(res)
}
)
}
// 关闭客户端
closeClient(ip, port, callback
) {
this.connections.closeTcpClient(
ip,
port,
(res
) =>
{
console.log('Client closed:'
, res)
this.clients.delete(`${ip
}:${port
}`
)
callback?.(res)
}
)
}
// 关闭服务端
closeServer(port, callback
) {
this.connections.closeTcpServer(
port,
(res
) =>
{
console.log('Server closed:'
, res)
this.servers.delete(port)
callback?.(res)
}
)
}
// 私有方法:添加客户端记录
_addClient(ip, port
) {
const key = `${ip
}:${port
}`
if (!
this.clients.has(key)
) {
this.clients.set(key, {
ip, port
}
)
}
}
}
// 单例模式导出
export
const tcpService =
new TcpService(
)
2. CK协议适配器 (ckProtocol.js)
import {
tcpService
}
from './tcpService'
import {
crc16
}
from './crc16'
// 命令类型枚举
const CMD_TYPE = {
LOCK_CONTROL: 0x01
,
// ...其他命令类型
}
export
class CKProtocol {
constructor(
) {
this.cabinetNo = 0 // 默认柜号
this.serialNo = 0 // 序列号计数器
}
// 初始化连接
init(ip, port = 5460
) {
this.serverIp = ip
this.serverPort = port
// 创建字节数组模式的客户端连接
tcpService.createByteClient(
ip,
port,
(res
) =>
{
console.log('Connected to CK device:'
, res)
this.isConnected = res.result === 'success'
}
,
(res
) =>
{
this.handleDeviceResponse(res)
}
)
}
// 构建协议帧
buildFrame(cmdType, cmdTag, data =
null
) {
const magic = 0x1799 // 上位机→主板通信
const dataLength = data ? data.length : 0
const buffer =
new ArrayBuffer(11 + dataLength)
const view =
new DataView(buffer)
// 填充帧头
view.setUint16(0
, magic, false
)
view.setUint8(2
,
this.cabinetNo)
view.setUint32(3
,
this.serialNo++
, false
)
view.setUint8(7
, cmdType)
view.setUint8(8
, cmdTag)
view.setUint16(9
, dataLength, false
)
// 填充数据区
if (data && dataLength >
0
) {
for (
let i = 0
; i < dataLength; i++
) {
view.setUint8(11 + i, data[i]
)
}
}
// 计算CRC16
const crc = crc16(
new Uint8Array(buffer.slice(0
, 11 + dataLength)
)
)
// 创建最终字节数组
const fullFrame =
new Uint8Array(13 + dataLength)
fullFrame.set(
new Uint8Array(buffer)
, 0
)
fullFrame.set([crc >>
8
, crc &
0xFF]
, 11 + dataLength)
return fullFrame
}
// 发送命令
sendCommand(cmdType, cmdTag, data =
null
) {
if (!
this.isConnected) {
console.error('Not connected to device'
)
return false
}
const frame =
this.buildFrame(cmdType, cmdTag, data)
tcpService.sendBytesToServer(
this.serverIp,
this.serverPort,
Array.from(frame)
,
(res
) =>
{
console.log('Command sent:'
, res)
}
)
return true
}
// 处理设备响应
handleDeviceResponse(response
) {
try {
// 将响应数据转换为Uint8Array
const data =
new Uint8Array(response.msg.split(','
).map(Number)
)
// 解析响应帧
if (data.length <
12
) {
throw
new Error('Invalid response length'
)
}
const view =
new DataView(data.buffer)
const magic = view.getUint16(0
, false
)
const cabinetNo = view.getUint8(2
)
const serialNo = view.getUint32(3
, false
)
const cmdType = view.getUint8(7
)
const cmdTag = view.getUint8(8
)
const result = view.getUint8(9
)
const dataLength = view.getUint16(10
, false
)
// 验证CRC
const receivedCrc = view.getUint16(12 + dataLength, false
)
const calculatedCrc = crc16(data.slice(0
, 12 + dataLength)
)
if (receivedCrc !== calculatedCrc) {
throw
new Error('CRC check failed'
)
}
// 提取数据区
let responseData =
null
if (dataLength >
0
) {
responseData = data.slice(12
, 12 + dataLength)
}
// 返回解析结果
return {
magic,
cabinetNo,
serialNo,
cmdType,
cmdTag,
result,
dataLength,
data: responseData
}
}
catch (error) {
console.error('Failed to parse device response:'
, error)
return
null
}
}
// 锁控命令
controlLock(lockNo, action
) {
const tagMap = {
open: 0x01
,
close: 0x03
,
status: 0x02
,
openAll: 0x06
}
const tag = tagMap[action]
if (tag ===
undefined
)
return false
const data = action === 'openAll' ?
null :
new Uint8Array([lockNo]
)
return
this.sendCommand(CMD_TYPE.LOCK_CONTROL
, tag, data)
}
// 查询设备信息
queryDeviceInfo(infoType
) {
const tagMap = {
mac: 0x01
,
hardwareVersion: 0x02
,
softwareVersion: 0x03
,
firmwareTime: 0x04
,
uptime: 0x05
}
const tag = tagMap[infoType]
if (tag ===
undefined
)
return false
return
this.sendCommand(CMD_TYPE.QUERY_INFO
, tag)
}
// 其他协议命令...
}
// 单例模式导出
export
const ckProtocol =
new CKProtocol(
)
3. Vue组件中使用 (DeviceControl.vue)
锁控制
设备查询
通信日志
{{ log }}
import { ref } from 'vue'
import { ckProtocol } from '@/api/ckProtocol'
const serverIp = ref('192.168.1.100')
const serverPort = ref('5460')
const isConnected = ref(false)
const logs = ref([])
// 连接设备
const connectDevice = () => {
addLog(`正在连接 ${serverIp.value}:${serverPort.value}...`)
ckProtocol.init(serverIp.value, parseInt(serverPort.value))
// 模拟连接成功 (实际应该通过回调事件)
setTimeout(() => {
isConnected.value = true
addLog('连接成功')
}, 1000)
}
// 断开连接
const disconnectDevice = () => {
tcpService.closeClient(serverIp.value, serverPort.value, (res) => {
isConnected.value = false
addLog('已断开连接')
})
}
// 锁控制命令
const openLock = (lockNo) => {
addLog(`发送开锁命令: 锁${lockNo}`)
ckProtocol.controlLock(lockNo, 'open')
}
const closeLock = (lockNo) => {
addLog(`发送关锁命令: 锁${lockNo}`)
ckProtocol.controlLock(lockNo, 'close')
}
const openAllLocks = () => {
addLog('发送开所有锁命令')
ckProtocol.controlLock(null, 'openAll')
}
// 查询命令
const queryMac = () => {
addLog('发送查询MAC地址命令')
ckProtocol.queryDeviceInfo('mac')
}
const queryVersion = () => {
addLog('发送查询版本命令')
ckProtocol.queryDeviceInfo('softwareVersion')
}
// 添加日志
const addLog = (message) => {
const timestamp = new Date().toLocaleTimeString()
logs.value.unshift(`[${timestamp}] ${message}`)
if (logs.value.length > 100) {
logs.value.pop()
}
}
.device-control {
padding: 20px;
}
.connection-section {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 20px;
}
.connection-section input {
flex: 1;
min-width: 120px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.connection-section button {
min-width: 80px;
}
.command-section {
margin-bottom: 20px;
}
.command-group {
margin-bottom: 15px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 8px;
}
.group-title {
display: block;
font-weight: bold;
margin-bottom: 8px;
}
.command-group button {
margin-right: 10px;
margin-bottom: 8px;
}
.log-section {
margin-top: 20px;
}
.section-title {
font-weight: bold;
margin-bottom: 8px;
}
.log-content {
height: 200px;
padding: 10px;
background-color: #f0f0f0;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
}
.log-item {
margin-bottom: 4px;
padding-bottom: 4px;
border-bottom: 1px solid #e0e0e0;
}
4. CRC16工具 (crc16.js)
// CRC-16/XMODEM (x16 + x12 + x5 + 1)
export
function crc16(data
) {
let crc = 0x0000
for (
let i = 0
; i < data.length; i++
) {
crc ^= data[i] <<
8
for (
let j = 0
; j <
8
; j++
) {
if (crc &
0x8000
) {
crc = (crc <<
1
) ^ 0x1021
}
else {
crc <<= 1
}
}
}
return crc &
0xFFFF
}
关键点说明
完全适配 jjc-tcpTools 插件:
- 严格按照插件提供的 API 进行封装
- 支持字符串和字节数组两种通信模式
- 实现了服务端和客户端的所有基本操作
CK 协议实现:
- 按照文档规范构建协议帧
- 实现了 CRC16 校验
- 封装了常用命令如锁控制、设备查询等
组件集成:
- 提供直观的设备控制界面
- 显示通信日志
- 管理连接状态
错误处理:
- 基本的错误检测和日志记录
- CRC 校验确保数据完整性
扩展性:
- 可以轻松添加更多协议命令
- 支持多设备管理
浙公网安备 33010602011771号