gen_YDN23_protocol_send_cmd

本文介绍了一种名为电总协议的通讯协议,详细解析了如何使用特定程序生成发包指令,并通过实例展示了如何从现场设备的回包中分析协议版本等关键信息。该文还提供了发包与回包的具体格式,以及如何利用这些信息进行后续的数据请求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

今天同事在现场遇到一个使用电总协议的设备,开始要的协议不对,导致没回包。
要来协议一看,是电总协议。要先写测试指令,拿到回包才能分析。
以前同事写过电总协议的回包解析,和别的逻辑连一起,要抽出发包的拼包代码,挺麻烦的。
如果手算,那基本不可能。
花了4,5个小时,写了一个电总协议拼包程序,将发包指令序列打印到控制台上。这样,就可以给现场同事用了。
没做交互的部分(没时间了,凑合着先将前置任务完成了),将参数都写死在测试程序中了。好在对于一个相同的设备,这些发包指令就那么多。不同的只有协议版本,设备地址。

电总协议的好处

这协议看着怪怪的。

好像SOI, EOI不会出现在中间的字节序列中,所以用程序收包分析时,判断包的边界比较容易。

其他好处没看出来,好像此种协议整繁琐了。
通讯包,只要能带校验,保证数据不错就行。

运行效果

这条指令只是验证计算和拼包, 用已知的正确包参数构造一个新包, 能还原原始包,说明拼包算法正确:
7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D
请将回包贴在下面一行:


获取通信协议版本号:
7E 30 30 30 31 32 41 34 46 30 30 30 30 46 44 39 32 0D
请将回包贴在下面一行:


获取设备厂家信息:
7E 30 30 30 31 32 41 35 31 30 30 30 30 46 44 41 36 0D
请将回包贴在下面一行:


获取告警状态:
7E 30 30 30 31 32 41 34 34 30 30 30 30 46 44 41 34 0D
请将回包贴在下面一行:


获取开关输入状态:
7E 30 30 30 31 32 41 34 33 30 30 30 30 46 44 41 35 0D
请将回包贴在下面一行:


获取自定义模拟量量化数据3:
7E 30 30 30 31 32 41 45 33 30 30 30 30 46 44 39 34 0D
请将回包贴在下面一行:


获取自定义模拟量量化数据2:
7E 30 30 30 31 32 41 45 32 30 30 30 30 46 44 39 35 0D
请将回包贴在下面一行:


获取自定义模拟量量化数据1:
7E 30 30 30 31 32 41 45 31 30 30 30 30 46 44 39 36 0D
请将回包贴在下面一行:


获取系统模拟量量化数据:
7E 30 30 30 31 32 41 34 31 30 30 30 30 46 44 41 37 0D
请将回包贴在下面一行:



gen_YDN23_protocol_send_cmd.exe (进程 33988)已退出,返回代码为: 0。
若要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口...

demo工程预览

这回写了400行。

// @file gen_YDN23_protocol_send_cmd.cpp
// @brief gen YDN23 protocol(电总协议) send cmd

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <tchar.h>
#include <math.h>

const unsigned char SOI = 0x7E; // 开始字符是固定的0x7E => ~
const unsigned char EOI = 0x0D; // 结束字符是固定的0x0D

void calc_and_print_YND23_cmd(const char* psz_tip, unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned char* INFO, unsigned short int LENGTH, unsigned char* YND23_cmd);

int gen_YND23_cmd(unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned short int LENGTH, unsigned char* INFO, unsigned char* YND23_cmd);
void uint8_to_2ascii_char(unsigned char uc_in, char& c1, char& c2);
void uint16_to_4ascii_char(unsigned int ui_in, char& c1, char& c2, char& c3, char& c4);
char fn_4bits_to_char(unsigned char uc);
unsigned short int LENGTH_add_LCHKSUM(unsigned short int LENGTH);
unsigned short int calc_YDN23_CHKSUM(unsigned char* YND23_cmd, int index_begin, int index_end);
void print_ary_as_hex(unsigned char* ary, int len);

int main(int argc, char** argv)
{
	int i = 0;

	unsigned char VER = 0;
	unsigned char ADR = 0;
	unsigned char CID1 = 0;
	unsigned char CID2 = 0;
	unsigned short int LENGTH = 0;

	const char* psz_tip = NULL;
	unsigned char VER_real = 0;

	// LENGTH中的低12位 所能带的INFO数据个数为 2^12 = 4096;
	// INFO中的数据为WORD, WORD的最大数量为 4096/2 = 2048;
	unsigned char INFO[4096] = {'\0'}; // 如果LENGTH中的低12位是0, INFO域没内容, 不出现在产生的命令序列中

	// unsigned short int CHKSUM = 0;

	// 最终拼好的命令序列最大长度 = SOI(1bytes) + VER(2bytes) + ADR(2bytes) + CID1(2bytes) + CID2(2bytes) + LENGTH(4bytes) + INFO(4096 max bytes) + CHKSUM(4bytes) + EOI(1byte) = 4906 + 18
	unsigned char YND23_cmd[4906 + 18] = {'\0'}; // 根据参数, 防止最终拼好的命令序列

	do {
		setlocale(LC_ALL, ".936"); ///< 设置中文代码页, 用来显示中文字符

		// 不传参数进来了, 将参数写死,运行一次产生一个发送的命令序列
		// 如果要产生不同的命令, 重新写参数, 然后编译运行一次后, 产生一个新命令

		// 电总协议, 要给定以下6个参数(VER/ADR/CID1/CID2/LENGTH_for_INFO/INFO)

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		psz_tip = "这条指令只是验证计算和拼包, 用已知的正确包参数构造一个新包, 能还原原始包,说明拼包算法正确";
		VER = 0x20; // 对于同一种设备, 通讯协议版本是固定的, 只要发一条命令取一下就行
		ADR = 0x01; // 设备地址在设备仪表上有显示
		CID1 = 0x42; // CID1/CID2的组合说明该命令具体任务
		CID2 = 0x44;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 获取通信协议版本号 2AH 4FH
		psz_tip = "获取通信协议版本号";
		VER = 0x00; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A; // CID1 = 0x2A, CID2 = 0x4F
		CID2 = 0x4F;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		VER_real = VER; // @todo 从回包中取到实际的VER

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取设备厂家信息 2AH 51H
		psz_tip = "获取设备厂家信息";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x51;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取告警状态 2AH 44H
		psz_tip = "获取告警状态";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x44;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取开关输入状态 2AH 43H
		psz_tip = "获取开关输入状态";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x43;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取自定义模拟量量化数据3 2AH E3H
		psz_tip = "获取自定义模拟量量化数据3";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE3;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取自定义模拟量量化数据2 2AH E2H
		psz_tip = "获取自定义模拟量量化数据2";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE2;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取自定义模拟量量化数据1 2AH E1H
		psz_tip = "获取自定义模拟量量化数据1";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0xE1;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);

		// -----------------------------------------------------------------------------
		// 产生YDN23协议的发送命令
		// -----------------------------------------------------------------------------
		// 拿上面取到的协议版本号填入VER, 再执行其他命令, 因为其他命令都和实际的VER有关
		// 获取系统模拟量量化数据 2AH 41H
		psz_tip = "获取系统模拟量量化数据";
		VER = VER_real; // 因为是取通讯协议版本, 所以这里的版本可以随便填
		ADR = 0x01; // 地址要正确
		CID1 = 0x2A;
		CID2 = 0x41;

		LENGTH = 0x0000; // 低12位标识后面的INFO域有多少个字节(最多4096个), 高4位是低12位的校验和
		memset(INFO, 0, sizeof(INFO));

		calc_and_print_YND23_cmd(psz_tip, VER, ADR, CID1, CID2, INFO, LENGTH, YND23_cmd);
	} while (0);

	return EXIT_SUCCESS;
}

void calc_and_print_YND23_cmd(const char* psz_tip, unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned char* INFO, unsigned short int LENGTH, unsigned char* YND23_cmd)
{
	// 一个有效的电总协议发送命令
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // 命令序列最小为18个字节

	// 分析
	// 7E // SOI = 0x7E
	// 32 30 // VER = 0x20
	// 30 31 // ADR = 0x01
	// 34 32 // CID1 = 0x42
	// 34 34 // CID2 = 0x44
	// 30 30 30 30 // LENGTH = 0
	// INFO(no data)
	// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
	// 0D // EOI = 0x0D

	// CHKSUM = 0x0000; // 除了SOI/CHKSUM/EOI之外字节的累加和(4个字节一组进行累加)

	int len_YND23_cmd = 0; // 最终拼好的命令有多少个字节?

	memset(YND23_cmd, 0, sizeof(YND23_cmd)); // 除了SOI/EOI是16进制字节,其他的位置都要将16进制字节表示成2个ASCII字符标识的16进制数(e.g. 0x01 => "01" => 0x30 0x31)

	// 看看给定的参数,是否能还原上面的命令序列

	// 产生出的命令为YND23_cmd,字节长度为len_YND23_cmd
	len_YND23_cmd = gen_YND23_cmd(VER, ADR, CID1, CID2, LENGTH, INFO, YND23_cmd);

	// 打印出YND23_cmd的内容供贴到串口助手做测试
	printf("%s:\n", psz_tip);
	print_ary_as_hex(YND23_cmd, len_YND23_cmd);
	printf("请将回包贴在下面一行:\n");
	printf("\n\n");

	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // run result
	// 7E 32 30 30 31 34 32 34 34 30 30 30 30 46 44 41 46 0D // original cmd, CHKSUM = "FDAF" = 0xFDAF
	// ok :)
}

void print_ary_as_hex(unsigned char* ary, int len)
{
	int i = 0;

	for (i = 0; i < len; i++) {
		printf("%2.2X", ary[i]); // 为了贴到串口助手中直接用,不要换行
		if ((i + 1) != len) {
			printf(" ");
		}
		else {
			printf("\n");
		}
	}
}

int gen_YND23_cmd(unsigned char VER, unsigned char ADR, unsigned char CID1, unsigned char CID2, unsigned short int LENGTH, unsigned char* INFO, unsigned char* YND23_cmd)
{
	int len = 0;
	int ipos = 0;
	char c1 = '\0';
	char c2 = '\0';
	char c3 = '\0';
	char c4 = '\0';
	unsigned short int LENGTH_have_ehck_sum = 0;
	unsigned short int YDN23_CHKSUM = 0;
	int i = 0;

	do {
		// @todo 不检查入参了

		// 7E // SOI = 0x7E
// 32 30 // VER = 0x20
// 30 31 // ADR = 0x01
// 34 32 // CID1 = 0x42
// 34 34 // CID2 = 0x44
// 30 30 30 30 // LENGTH = 0
// INFO(no data)
// 46 44 41 46 // CHKSUM = "FDAF" = 0xFDAF
// 0D // EOI = 0x0D

		// SOI
		YND23_cmd[ipos++] = SOI;

		// VER
		uint8_to_2ascii_char(VER, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// ADR
		uint8_to_2ascii_char(ADR, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID1
		uint8_to_2ascii_char(CID1, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// CID2
		uint8_to_2ascii_char(CID2, c1, c2);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;

		// LENGTH
		LENGTH = LENGTH & (~(1 >> 12)); // 只取低12位, 最多4096(2^12)个数据
		LENGTH_have_ehck_sum = LENGTH_add_LCHKSUM(LENGTH);
		uint16_to_4ascii_char(LENGTH_have_ehck_sum, c1, c2, c3, c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;

		// INFO
		for (i = 0; i < LENGTH; i++) {
			uint8_to_2ascii_char(INFO[i], c1, c2);
			YND23_cmd[ipos++] = c1;
			YND23_cmd[ipos++] = c2;
		}

		// CHKSUM

		// test calc_YDN23_CHKSUM
		//ipos = 0;
		//YND23_cmd[ipos++] = SOI;
		//YND23_cmd[ipos++] = '1';
		//YND23_cmd[ipos++] = '2';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '3';
		//YND23_cmd[ipos++] = '4';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '0';
		//YND23_cmd[ipos++] = '4';
		//YND23_cmd[ipos++] = '5';
		//YND23_cmd[ipos++] = '6';
		//YND23_cmd[ipos++] = 'A';
		//YND23_cmd[ipos++] = 'B';
		//YND23_cmd[ipos++] = 'C';
		//YND23_cmd[ipos++] = 'D';
		//YND23_cmd[ipos++] = 'F';
		//YND23_cmd[ipos++] = 'E';
		 1203400456ABCDFE
		//ipos = 16 + 1;
		//YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);

		YDN23_CHKSUM = calc_YDN23_CHKSUM(YND23_cmd, 1 /*不要算SOI*/, ipos - 1 /*不算累加和本身和EOI*/);
		uint16_to_4ascii_char(YDN23_CHKSUM, c1, c2, c3, c4);
		YND23_cmd[ipos++] = c1;
		YND23_cmd[ipos++] = c2;
		YND23_cmd[ipos++] = c3;
		YND23_cmd[ipos++] = c4;

		// EOI
		YND23_cmd[ipos++] = EOI;
		len = ipos;
	} while (0);

	return len;
}

char fn_4bits_to_char(unsigned char uc)
{
	char c_out = '0';
	// 0 => '0'
	// 1 => '1'
	// 10 => 'A'
	// 11 => 'B'
	// 12 => 'C'
	// 13 => 'D'
	// 14 => 'E'
	// 15 => 'F'

	if ((uc >= 0x00) && (uc < 0x0A)) {
		c_out = uc - 0 + '0';
	} else if ((uc >= 0x0A) && (uc <= 0x0F)) {
		c_out = uc - 0x0A + 'A';
	}

	return c_out;
}

void uint8_to_2ascii_char(unsigned char uc_in, char& c1, char& c2)
{
	// 0x12 => '1' '2'

	unsigned char uc1 = (uc_in >> 4) & 0x0f;
	unsigned char uc2 = (uc_in >> 0) & 0x0f;

	c1 = fn_4bits_to_char(uc1);
	c2 = fn_4bits_to_char(uc2);
}

void uint16_to_4ascii_char(unsigned int ui_in, char& c1, char& c2, char& c3, char& c4)
{
	// 0x1234 => '1' '2' '3' '4'

	unsigned char uc1 = (unsigned char)((ui_in >> 12) & 0x0f);
	unsigned char uc2 = (unsigned char)((ui_in >> 8) & 0x0f);
	unsigned char uc3 = (unsigned char)((ui_in >> 4) & 0x0f);
	unsigned char uc4 = (unsigned char)((ui_in >> 0) & 0x0f);

	c1 = fn_4bits_to_char(uc1);
	c2 = fn_4bits_to_char(uc2);
	c3 = fn_4bits_to_char(uc3);
	c4 = fn_4bits_to_char(uc4);
}

unsigned short int LENGTH_add_LCHKSUM(unsigned short int LENGTH)
{
	unsigned short int ui_out = 0;
	unsigned char uc1 = (LENGTH >> 8) & 0x0f;
	unsigned char uc2 = (LENGTH >> 4) & 0x0f;
	unsigned char uc3 = (LENGTH >> 0) & 0x0f;
	unsigned char uc_LCHKSUM = '\0'; // 高4位的电总校验和

	uc_LCHKSUM = uc1 + uc2 + uc3; // 低12位分为3个4位,进行累加
	uc_LCHKSUM %= 0x10; // 模16取余数
	uc_LCHKSUM = ~uc_LCHKSUM; // 取反
	uc_LCHKSUM += 1; // 加1

	ui_out = (uc_LCHKSUM << 12) & (1 << 12); // 将算出的累加和放到高4位
	ui_out |= (LENGTH & (1 << 12)); // 将LENGTH的低12位放到返回值的低12位

	return ui_out;
}

unsigned short int calc_YDN23_CHKSUM(unsigned char* YND23_cmd, int index_begin, int index_end)
{
	// CHKSUM的计算是除SOI、 EOI和CHKSUM外,其他字符ASCII码值累加求和
	// 结果模65535取余数
	// 取反加1

	// YND23_cmd送进来时, 已经刨掉了SOI, CHKSUM, EOI
	int i = 0;
	unsigned int ui_chksum = 0;
	unsigned short int ui_chksum_rc = 0;

	for (i = index_begin; i <= index_end; i++) {
		ui_chksum += YND23_cmd[i];
	}

	ui_chksum %= 0xFFFF;
	ui_chksum = ~ui_chksum;
	ui_chksum += 1;

	ui_chksum_rc = ui_chksum & 0xffff;
	return ui_chksum_rc;
}


从现场拿到1个回包,尝试手工分析

要发给设备的包是取设备协议版本。

发包

发包用测试程序组好的包

7E 30 30 30 31 32 41 34 46 30 30 30 30 46 44 39 32 0D

从现场设备的485串口2得到了回包如下

7E 32 36 30 31 32 41 30 30 30 30 30 30 46 44 41 34 0D

手工初步分析如下

		// 7E // SOI
		// 32 36 // VER = "26" = 2.6
		// printf("%c%c\n", 0x32, 0x36);
		// 30 31 // ADR
		// 32 41 // CID1
		// 30 30 // RTN = 0x00(正常)
		// 30 30 30 30 // LENGTH = 0(NO INFO)
		// 46 44 41 34 // CHKSUM
		// 0D // EOI

从回包分析可知,协议版本是2.6
再给设备发的包,就要以VER = 32 36 (2.6) 为实际值,组其他包发给设备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值