SZY206-2016水资源监测数据传输规约 基础架构

news2025/3/11 16:45:33
szy_206.h
#ifndef __SZY_206_H__
#define __SZY_206_H__


#include "stdint.h"
#include "stdio.h"
#include "string.h"

#define FRAME_START     (0x68)
#define FRAME_END       (0x16)

// 定义下行帧传输方向的枚举类型(DIR=0)
typedef enum {
    DOWN_SEND_CONFIRM_COMMAND = 0,  // 发送∕确认 命令
    DOWN_QUERY_RESPONSE_RAINFALL = 1,  // 查询∕响应帧 雨量参数
    DOWN_QUERY_RESPONSE_WATER_LEVEL = 2,  // 查询∕响应帧 水位参数
    DOWN_QUERY_RESPONSE_FLOW = 3,  // 查询∕响应帧 流量(水量)参数
    DOWN_QUERY_RESPONSE_FLOW_VELOCITY = 4,  // 查询∕响应帧 流速参数
    DOWN_QUERY_RESPONSE_GATE_POSITION = 5,  // 查询∕响应帧 闸位参数
    DOWN_QUERY_RESPONSE_POWER = 6,  // 查询∕响应帧 功率参数
    DOWN_QUERY_RESPONSE_AIR_PRESSURE = 7,  // 查询∕响应帧 气压参数
    DOWN_QUERY_RESPONSE_WIND_SPEED = 8,  // 查询∕响应帧 风速参数
    DOWN_QUERY_RESPONSE_WATER_TEMPERATURE = 9,  // 查询∕响应帧 水温参数
    DOWN_QUERY_RESPONSE_WATER_QUALITY = 10,  // 查询∕响应帧 水质参数
    DOWN_QUERY_RESPONSE_SOIL_MOISTURE = 11,  // 查询∕响应帧 土壤含水率参数
    DOWN_QUERY_RESPONSE_EVAPORATION = 12,  // 查询∕响应帧 蒸发量参数
    DOWN_QUERY_RESPONSE_ALARM_STATUS = 13,  // 查询∕响应帧 报警或状态参数
    DOWN_QUERY_RESPONSE_INTEGRATED = 14,  // 查询∕响应帧 综合参数
    DOWN_QUERY_RESPONSE_WATER_PRESSURE = 15  // 查询∕响应帧 水压参数
} control_funcode_down_e;

// 定义上行帧传输方向的枚举类型(DIR=1)
typedef enum {
    UP_CONFIRM_APPROVAL = 0,  // 确认 认可
    UP_SELF_REPORT_RAINFALL = 1,  // 自报帧 雨量参数
    UP_SELF_REPORT_WATER_LEVEL = 2,  // 自报帧 水位参数
    UP_SELF_REPORT_FLOW = 3,  // 自报帧 流量(水量)参数
    UP_SELF_REPORT_FLOW_VELOCITY = 4,  // 自报帧 流速参数
    UP_SELF_REPORT_GATE_POSITION = 5,  // 自报帧 闸位参数
    UP_SELF_REPORT_POWER = 6,  // 自报帧 功率参数
    UP_SELF_REPORT_AIR_PRESSURE = 7,  // 自报帧 气压参数
    UP_SELF_REPORT_WIND_SPEED = 8,  // 自报帧 风速参数
    UP_SELF_REPORT_WATER_TEMPERATURE = 9,  // 自报帧 水温参数
    UP_SELF_REPORT_WATER_QUALITY = 10,  // 自报帧 水质参数
    UP_SELF_REPORT_SOIL_MOISTURE = 11,  // 自报帧 土壤含水率参数
    UP_SELF_REPORT_EVAPORATION = 12,  // 自报帧 蒸发量参数
    UP_SELF_REPORT_ALARM_STATUS = 13,  // 自报帧 报警或状态参数
    UP_SELF_REPORT_STATISTICAL_RAINFALL = 14,  // 自报帧 统计雨量
    UP_SELF_REPORT_WATER_PRESSURE = 15  // 自报帧 水压参数
} control_funcode_up_e;


typedef enum {
    LINK_DETECTION = 0x02,  // 链路检测
    SET_TERMINAL_ADDRESS = 0x10,  // 设置遥测终端、中继站地址
    SET_TERMINAL_CLOCK = 0x11,  // 设置遥测终端、中继站时钟
    SET_TERMINAL_WORK_MODE = 0x12,  // 设置遥测终端工作模式
    SET_TERMINAL_CURRENT_RECHARGE_AMOUNT = 0x15,  // 设置遥测终端本次充值量
    SET_TERMINAL_REMAINING_WATER_ALARM_VALUE = 0x16,  // 设置遥测终端剩余水量报警值
    SET_TERMINAL_WATER_LEVEL_BASE_AND_LIMITS = 0x17,  // 设置遥测终端的水位基值、水位上下限
    SET_TERMINAL_WATER_PRESSURE_LIMITS = 0x18,  // 设置遥测终端水压上、下限
    SET_TERMINAL_WATER_QUALITY_PARAMETER_UPPER_LIMIT = 0x19,  // 设置遥测终端水质参数种类、上限值
    SET_TERMINAL_WATER_QUALITY_PARAMETER_LOWER_LIMIT = 0x1A,  // 设置遥测终端水质参数种类、下限值
    SET_TERMINAL_INITIAL_WATER_AMOUNT = 0x1B,  // 设置终端站水量的表底(初始)值
    SET_TERMINAL_FORWARD_RELAY_LEADING_CODE_LENGTH = 0x1C,  // 设置遥测终端转发中继引导码长
    SET_RELAY_FORWARD_TERMINAL_ADDRESS = 0x1D,  // 设置中继站转发终端地址
    SET_RELAY_WORK_MACHINE_AUTO_SWITCH_AND_SELF_REPORT_STATUS = 0x1E,  // 设置中继站工作机自动切换,自报状态
    SET_TERMINAL_FLOW_PARAMETER_UPPER_LIMIT = 0x1F,  // 设置遥测终端流量参数上限值
    SET_TERMINAL_DETECTION_PARAMETER_TRIGGER_THRESHOLD_AND_STORAGE_INTERVAL = 0x20,  // 设置遥测终端检测参数启报阈值及固态存储时间段间隔
    SET_TERMINAL_IC_CARD_FUNCTION_ENABLE = 0x30,  // 置遥测终端IC卡功能有效
    SET_TERMINAL_IC_CARD_FUNCTION_DISABLE = 0x31,  // 取消遥测终端IC卡功能
    FIXED_VALUE_CONTROL_ENGAGE = 0x32,  // 定值控制投入
    FIXED_VALUE_CONTROL_DISENGAGE = 0x33,  // 定值控制退出
    FIXED_VALUE_SETTING = 0x34,  // 定值量设定
    QUERY_TERMINAL_ADDRESS = 0x50,  // 查询遥测终端、中继站地址
    QUERY_TERMINAL_CLOCK = 0x51,  // 查询遥测终端、中继站时钟
    QUERY_TERMINAL_WORK_MODE = 0x52,  // 查询遥测终端工作模式
    QUERY_TERMINAL_SELF_REPORT_DATA_TYPE_AND_INTERVAL = 0x53,  // 查询遥测终端的数据自报种类及时间间隔
    QUERY_TERMINAL_REAL_TIME_DATA_TYPE_TO_QUERY = 0x54,  // 查询遥测终端需查询的实时数据种类
    QUERY_TERMINAL_LATEST_RECHARGE_AMOUNT_AND_CURRENT_REMAINING_WATER = 0x55,  // 查询遥测终端最近成功充值量和现有剩余水量
    QUERY_TERMINAL_REMAINING_WATER_AND_ALARM_VALUE = 0x56,  // 查询遥测终端剩余水量和报警值
    QUERY_TERMINAL_WATER_LEVEL_BASE_AND_LIMITS = 0x57,  // 查询遥测终端水位基值、水位上下限
    QUERY_TERMINAL_WATER_PRESSURE_LIMITS = 0x58,  // 查询遥测终端水压上、下限
    QUERY_TERMINAL_WATER_QUALITY_PARAMETER_UPPER_LIMIT = 0x59,  // 查询遥测终端水质参数种类、上限值
    QUERY_TERMINAL_WATER_QUALITY_PARAMETER_LOWER_LIMIT = 0x5A,  // 查询遥测终端水质参数种类、下限值
    QUERY_TERMINAL_EVENT_RECORDS = 0x5D,  // 查询遥测终端的事件记录
    QUERY_TERMINAL_STATUS_AND_ALARM_STATUS = 0x5E,  // 查询遥测终端状态和报警状态
    QUERY_PUMP_MOTOR_REAL_TIME_WORK_DATA = 0x5F,  // 查询水泵电机实时工作数据
    QUERY_TERMINAL_FORWARD_RELAY_LEADING_CODE_LENGTH = 0x60,  // 查询遥测终端转发中继引导码长
    QUERY_TERMINAL_IMAGE_RECORDS = 0x61,  // 查询遥测终端图像记录
    QUERY_RELAY_FORWARD_TERMINAL_ADDRESS = 0x62,  // 查询中继站转发终端地址
    QUERY_RELAY_WORK_MACHINE_STATUS_AND_SWITCH_RECORDS = 0x63,  // 查询中继站工作机状态和切换记录
    QUERY_TERMINAL_FLOW_PARAMETER_UPPER_LIMIT = 0x64,  // 查询遥测终端流量参数上限值
    RANDOM_SELF_REPORT_ALARM_DATA = 0x81,  // 随机自报报警数据
    MANUAL_SET_NUMBER = 0x82,  // 人工置数
    RESET_TERMINAL_PARAMETERS_AND_STATUS = 0x90,  // 复位遥测终端参数和状态
    CLEAR_TERMINAL_HISTORICAL_DATA_UNIT = 0x91,  // 清空遥测终端历史数据单元
    REMOTE_CONTROL_START_PUMP_OR_VALVE = 0x92,  // 遥控启动水泵或阀门/闸门
    REMOTE_CONTROL_CLOSE_PUMP_OR_VALVE = 0x93,  // 遥控关闭水泵或阀门/闸门
    REMOTE_CONTROL_TERMINAL_OR_RELAY_COMMUNICATION_MACHINE_SWITCH = 0x94,  // 遥控终端或中继站通信机切换
    REMOTE_CONTROL_RELAY_WORK_MACHINE_SWITCH = 0x95,  // 遥控中继站工作机切换
    MODIFY_TERMINAL_PASSWORD = 0x96,  // 修改遥测终端密码
    SET_TERMINAL_REAL_TIME_DATA_TYPE_TO_QUERY = 0xA0,  // 设置遥测站需查询的实时数据种类
    SET_TERMINAL_SELF_REPORT_DATA_TYPE_AND_INTERVAL = 0xA1,  // 设置遥测终端的数据自报种类及时间间隔
    QUERY_TERMINAL_REAL_TIME_VALUE = 0xB0,  // 查询遥测终端实时值
    QUERY_TERMINAL_SOLID_STATE_STORAGE_DATA = 0xB1,  // 查询遥测终端固态存储数据
    QUERY_TERMINAL_MEMORY_SELF_REPORT_DATA = 0xB2,  // 查询遥测终端内存自报数据
    TERMINAL_SELF_REPORT_REAL_TIME_DATA = 0xC0  // 遥测终端自报实时数据
} user_func_e;


// 控制域结构体
#pragma pack(1)
typedef struct {
    uint8_t DIR : 1;      // 传输方向位
    uint8_t DIV : 1;      // 拆分标志位
    uint8_t FCB : 2;      // 帧计数位
    uint8_t function_code : 4;   // 功能码
} ControlField;

// 密码PW结构体
#pragma pack(1)
typedef struct {
    uint8_t key_algorithm : 4;   // 密钥算法,采用BCD编码,取值范围0 - 9
    uint16_t key:12;            // 密钥,采用BCD编码,取值范围0 - 999
} Password;

// 时间标签Tp结构体
#pragma pack(1)
typedef struct {
    uint32_t start_frame_send_time;   // 启动帧发送时标,数据格式为BCD码,依次为秒、分、时、日
    uint8_t allow_send_transfer_delay_time;   // 允许发送传输延时时间,BIN编码,单位为min
} TimeTag;


// 统一数据结构体
#pragma pack(1)
typedef struct {
    uint8_t start_frame;   // 起始字符(68H)
    uint8_t length;       // 长度L 控制域、地址域、用户数据域(应用层)的字节总数
    uint8_t start_frame2;  // 起始字符(68H)
    ControlField control;   // 控制域
    uint8_t address[5];   // 地址域A
    uint8_t AFN;          // 应用层功能码AFN
    uint8_t* user_data;    // 数据域指针,用户根据具体功能码分配和填充数据
    Password password;      // 密码PW
    TimeTag time_tag;      // 时间标签Tp
    uint8_t checksum;     // 校验CS   用户数据区的 CRC 校验  控制域+地址域+用户数据
    uint8_t end_char;     // 结束字符(16H)
} SZY206_Frame_T;

         
#define SZY206_FIX_HEAD_LEN         (3)  //起始字符+长度L+起始字符+控制域+地址域A+应用层功能码AFN
#define SZY206_USER_DATA_OFFSET     (SZY206_FIX_HEAD_LEN+sizeof(ControlField)+5+1)//到user_data的偏移地址
#define SZY206_MIN_FRAME_LEN        (SZY206_USER_DATA_OFFSET+2)  //起始字符+长度L+起始字符+控制域+地址域A+应用层功能码AFN+校验CS+结束字符(16H)






static void SZY206_Dec2bcd(uint8_t* bufIn, double dval, uint16_t nb_byte, int nb_dec);
uint8_t calculateCRC8(uint8_t *data, uint16_t length) ;
uint8_t SZY206_IsAux(uint8_t AFN) ;

uint8_t SZY206_FrameFill(uint8_t *buffer, ControlField control, uint8_t *address, uint8_t AFN,
                         Password password, TimeTag time_tag, uint8_t *user_data, uint16_t user_data_length);

uint8_t SZY206_FrameVerification(uint8_t *buffer , uint16_t length);

uint8_t SZY206_Parse(uint8_t *buffer , uint16_t length ,ControlField  *control , uint8_t *AFN,
                    Password *password ,TimeTag *time_tag,uint8_t *user_data);


#endif
szy_206.c
#include "szy_206.h"
#include <stdio.h>


/// @brief 十进制转BCD
/// @param data 
/// @return 
static int dec2bcd(unsigned char data)
{
	unsigned char temp;
	temp = (((data / 10) << 4) + (data % 10));
	return temp;
}
/// @brief double 转换成BCD
/// @param bufIn 
/// @param dval 数据
/// @param nb_byte 总长度
/// @param nb_dec 小数点后位数
static void SZY206_Dec2bcd(uint8_t* bufIn, double dval, uint16_t nb_byte, int nb_dec)
{
	int val = 0;
	int data_len = 0;

	val = (int)(dval * pow(10, nb_dec));
	char tmp[32];
	memset(tmp, 0, 32);
	sprintf_s(tmp, "%d", val);

	data_len = strlen(tmp);

	memset(tmp, 0, 32);

	for (int i = 0; i < data_len; i++)
	{
		if (data_len % 2)
		{
			tmp[0] = 0;
			tmp[i + 1] = (int)(val / pow(10, data_len - i - 1)) % 10;
		}
		else
		{
			tmp[i] = (int)(val / pow(10, data_len - i - 1)) % 10;
		}
	}
	int index_tmp = 0;
	data_len = data_len % 2 == 0 ? data_len / 2 : data_len / 2 + 1;
	// printf("nb_byte:%d  nb_dec:%d val:%d data len:%d \n", nb_byte, nb_dec, val, data_len);

	//数据
	for (int i = nb_byte; i > 0; i--)
	{
		if ((i - data_len) > 0)
		{
			bufIn[nb_byte - i] = 0;
		}
		else
		{
			bufIn[nb_byte - i] = dec2bcd(tmp[0 + index_tmp * 2] * 10 + tmp[1 + index_tmp * 2]);
			index_tmp += 1;
		}
	}
}


// CRC8 多项式,这里使用常见的0x31(x^8 + x^5 + x^4 + 1)
#define POLYNOMIAL 0x31

// 计算CRC8校验值
uint8_t calculateCRC8(uint8_t *data, uint16_t length) 
{
    uint8_t crc = 0;
    for (size_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ POLYNOMIAL;
            } else {
                crc <<= 1;
            }
        }
    }
    return crc;
}

/// @brief 判断是否有扩展域(即是否存在Password TimeTag)
/// @param AFN 
/// @return 存在返回1
uint8_t SZY206_IsAux(uint8_t AFN) 
{
    // 以下功能码对应的帧包含密码和时间标签扩展域
    switch (AFN) {
    case SET_TERMINAL_ADDRESS:
    case SET_TERMINAL_CLOCK:
    case SET_TERMINAL_WORK_MODE:
    case SET_TERMINAL_CURRENT_RECHARGE_AMOUNT:
    case SET_TERMINAL_REMAINING_WATER_ALARM_VALUE:
    case SET_TERMINAL_WATER_LEVEL_BASE_AND_LIMITS:
    case SET_TERMINAL_WATER_PRESSURE_LIMITS:
    case SET_TERMINAL_WATER_QUALITY_PARAMETER_UPPER_LIMIT:
    case SET_TERMINAL_WATER_QUALITY_PARAMETER_LOWER_LIMIT:
    case SET_TERMINAL_INITIAL_WATER_AMOUNT:
    case SET_TERMINAL_FORWARD_RELAY_LEADING_CODE_LENGTH:
    case SET_RELAY_FORWARD_TERMINAL_ADDRESS:
    case SET_RELAY_WORK_MACHINE_AUTO_SWITCH_AND_SELF_REPORT_STATUS:
    case SET_TERMINAL_FLOW_PARAMETER_UPPER_LIMIT:
    case SET_TERMINAL_DETECTION_PARAMETER_TRIGGER_THRESHOLD_AND_STORAGE_INTERVAL:
    case SET_TERMINAL_IC_CARD_FUNCTION_ENABLE:
    case SET_TERMINAL_IC_CARD_FUNCTION_DISABLE:
    case FIXED_VALUE_CONTROL_ENGAGE:
    case FIXED_VALUE_CONTROL_DISENGAGE:
    case FIXED_VALUE_SETTING:
    case MODIFY_TERMINAL_PASSWORD:
    case SET_TERMINAL_REAL_TIME_DATA_TYPE_TO_QUERY:
    case SET_TERMINAL_SELF_REPORT_DATA_TYPE_AND_INTERVAL:
        return 1;  // 存在扩展域
    default:
        return 0;  // 不存在扩展域
    }
}


/// @brief 填充数据
/// @param buffer 缓存
/// @param control 控制域
/// @param address 地址域
/// @param AFN 功能码
/// @param password 密码
/// @param time_tag 时间
/// @param user_data 用户数据
/// @param user_data_length 用户数据长度
/// @return  帧总长度
uint8_t SZY206_FrameFill(uint8_t *buffer, ControlField control, uint8_t *address, uint8_t AFN,
                         Password password, TimeTag time_tag, uint8_t *user_data, uint16_t user_data_length) {
    // 将buffer转换为SZY206_Frame_T指针
    SZY206_Frame_T *frame = (SZY206_Frame_T *)buffer;
    uint8_t *data_for_crc = &frame->control;
    uint16_t length=0;
    uint8_t isAux;
    uint16_t frame_length=0;
    // 起始字符
    frame->start_frame = FRAME_START;
    frame->start_frame2 = FRAME_START;
    // 控制域
    frame->control = control;
    // 地址域
    memcpy(frame->address ,address,5);
    // 应用层功能码
    frame->AFN = AFN;

    // 计算长度L(控制域、地址域、用户数据域、密码、时间标签的字节总数)
    length =  sizeof(ControlField)+5+1+user_data_length;
    if(isAux) 
        length = length +sizeof(Password)+sizeof(TimeTag);
    frame->length = length; 
    // 用户数据域
    // 拷贝用户数据到buffer
    memcpy(frame->user_data,user_data,user_data_length);
    //偏移指针
    frame = frame+user_data_length;
    isAux = SZY206_IsAux(AFN);
    if(isAux) {
        // 密码PW
        frame->password = password;
        // 时间标签Tp
        frame->time_tag = time_tag;
    } else {
        frame =frame-sizeof(Password)+sizeof(TimeTag);
    }

    // 计算校验和CS(CRC8校验)
    uint8_t crc = calculateCRC8(data_for_crc, length);
    frame->checksum = crc;
    // 结束字符
    frame->end_char = FRAME_END;
    frame_length=SZY206_FIX_HEAD_LEN+length+2;  // 返回填充后的帧长度(包括起始字符和结束字符)
    return frame_length;
}

/// @brief 检验帧的正确性
/// @param buffer 缓存
/// @param length 缓存长度
/// @return 
uint8_t SZY206_FrameVerification(uint8_t *buffer , uint16_t length)
{
    if(buffer ==NULL)
        return 0;
    // 最小帧长度为(两个起始字符、控制域、地址域、应用层功能码、校验码和结束字符)
    if (length < SZY206_MIN_FRAME_LEN) 
        return 0;  // 帧长度过短,错误
    // 将buffer转换为SZY206_Frame_T指针
    uint8_t crc=0;
    SZY206_Frame_T *frame = (SZY206_Frame_T *)buffer;
    if(frame->start_frame!=FRAME_START || frame->start_frame2!=FRAME_START)
        return 0;
    crc = calculateCRC8(&frame->control,frame->length);
    if(frame->length > SZY206_USER_DATA_OFFSET-SZY206_FIX_HEAD_LEN)  //是否存在用户数据
        frame = frame+frame->length;
    if(!SZY206_IsAux(frame->AFN)){
        frame = frame-sizeof(Password)+sizeof(TimeTag);
    }
    if(crc != frame->checksum)
        return 0;
    if(frame->end_char != FRAME_END)
        return 0;
    return 1;
}

/// @brief 解析帧
/// @param buffer 
/// @param length 
/// @param control 控制域
/// @param AFN 功能吗
/// @param user_data 用户数据
/// @return 
uint8_t SZY206_Parse(uint8_t *buffer , uint16_t length ,ControlField  *control , uint8_t *AFN,
                    Password *password ,TimeTag *time_tag,uint8_t *user_data)
{
    if(buffer ==NULL)
        return 0;
    SZY206_Frame_T *frame = (SZY206_Frame_T *)buffer;
    *control = frame->control;
    *AFN = frame->AFN;
    *user_data = frame->user_data;
    if(SZY206_IsAux(frame->AFN) && frame->length > SZY206_USER_DATA_OFFSET-SZY206_FIX_HEAD_LEN) {
        frame = frame+frame->length;
        *password = frame->password;
        *time_tag = frame->time_tag;
    }
    return 1;
}


szy_main.c 列程


#include "szy_206.h"
// 示例:查询遥测终端实时值(下行)
void queryTerminalRealTimeValueDown() {
    // 控制域设置
    ControlField control;
    control.DIR = 0;   // 下行帧
    control.DIV = 0;   // 单帧
    control.FCB = 0;   // 初始帧计数位
    control.function_code = DOWN_QUERY_RESPONSE_INTEGRATED;   // 查询综合参数实时值

    // 地址域设置(假设地址为0102030405)
    uint8_t address[5] = {0x01, 0x02, 0x03, 0x04, 0x05};

    // 应用层功能码
    uint8_t AFN = QUERY_TERMINAL_REAL_TIME_VALUE;

    // 其他参数(密码、时间标签等,此处假设为默认值)
    Password password = {0};   // 密码部分根据实际情况填充或保持默认
    TimeTag time_tag = {0};   // 时间标签部分根据实际情况填充或保持默认

    // 用户数据(根据查询类型确定,这里查询综合参数实时值,示例数据为雨量、水位有值,其他参数无值)
    uint8_t user_data[1] = {0b00000011};   // 表示雨量、水位参数存在

    // 计算帧长度并填充帧
    uint16_t user_data_length = sizeof(user_data);
    uint8_t buffer[SZY206_MIN_FRAME_LEN + user_data_length];   // 根据预计最大长度分配缓冲区
    uint8_t frame_length = SZY206_FrameFill(buffer, control, address, AFN, password, time_tag, user_data, user_data_length);

    // 发送帧(这里假设通过串口发送,实际根据通信方式实现)
    sendFrame(buffer, frame_length);   // 发送帧的函数需根据实际通信方式实现
}

// 示例:解析查询遥测终端实时值的上行帧
void parseQueryTerminalRealTimeValueUp(uint8_t *received_buffer, uint16_t received_length) {
    // 验证帧的正确性
    if (SZY206_FrameVerification(received_buffer, received_length)) {
        // 解析帧
        ControlField control;
        uint8_t AFN;
        Password password;
        TimeTag time_tag;
        uint8_t *user_data;
        if (SZY206_Parse(received_buffer, received_length, &control, &AFN, &password, &time_tag, &user_data)) {
            if (control.DIR == 1 && AFN == QUERY_TERMINAL_REAL_TIME_VALUE) {
                // 根据数据域格式解析数据(这里仅为示例,实际根据文档详细格式解析)
                // 假设解析雨量、水位、流量等参数
                uint8_t rainfall[3];
                uint8_t water_level[4];
                uint8_t flow[5];
                memcpy(rainfall, user_data, 3);
                memcpy(water_level, user_data + 3, 4);
                memcpy(flow, user_data + 7, 5);
                // 处理解析后的数据(打印或存储等操作)
                printf("Rainfall: %.1f mm\n", (float)(rainfall[0] + (rainfall[1] << 8) + (rainfall[2] << 16)) / 10);
                printf("Water Level: %.3f m\n", (float)(water_level[0] + (water_level[1] << 8) + (water_level[2] << 16) + (water_level[3] << 24)) / 1000);
                printf("Flow: %.3f m³/s\n", (float)(flow[0] + (flow[1] << 8) + (flow[2] << 16) + (flow[3] << 24) + (flow[4] << 32)) / 1000);
            }
        }
    } else {
        printf("Received frame is incorrect.\n");
    }
}



// 示例:设置遥测终端工作模式(下行)
void setTerminalWorkModeDown() {
    // 控制域设置
    ControlField control;
    control.DIR = 0;   // 下行帧
    control.DIV = 0;   // 单帧
    control.FCB = 0;   // 初始帧计数位
    control.function_code = DOWN_SEND_CONFIRM_COMMAND;   // 发送/确认命令

    // 地址域设置(假设地址为0102030405)
    uint8_t address[5] = {0x01, 0x02, 0x03, 0x04, 0x05};

    // 应用层功能码
    uint8_t AFN = SET_TERMINAL_WORK_MODE;

    // 密码(假设密码算法为1,密钥为123)
    Password password;
    password.key_algorithm = 1;
    password.key = 123;

    // 时间标签(假设当前时间为2023年10月10日10时10分10秒,允许延时10分钟)
    TimeTag time_tag;
    time_tag.start_frame_send_time = (10 << 24) | (10 << 16) | (10 << 8) | 10;   // 秒、分、时、日的BCD码组合
    time_tag.allow_send_transfer_delay_time = 10;

    // 用户数据(设置工作模式为自报工作状态,01H)
    uint8_t user_data[1] = {0x01};

    // 计算帧长度并填充帧
    uint16_t user_data_length = sizeof(user_data);
    uint8_t buffer[SZY206_MIN_FRAME_LEN + user_data_length];   // 根据预计最大长度分配缓冲区
    uint8_t frame_length = SZY206_FrameFill(buffer, control, address, AFN, password, time_tag, user_data, user_data_length);

    // 发送帧(这里假设通过串口发送,实际根据通信方式实现)
    sendFrame(buffer, frame_length);   // 发送帧的函数需根据实际通信方式实现
}


// 示例:解析设置遥测终端工作模式的上行确认帧
void parseSetTerminalWorkModeUp(uint8_t *received_buffer, uint16_t received_length) {
    // 验证帧的正确性
    if (SZY206_FrameVerification(received_buffer, received_length)) {
        // 解析帧
        ControlField control;
        uint8_t AFN;
        Password password;
        TimeTag time_tag;
        uint8_t *user_data;
        if (SZY206_Parse(received_buffer, received_length, &control, &AFN, &password, &time_tag, &user_data)) {
            if (control.DIR == 1 && AFN == SET_TERMINAL_WORK_MODE) {
                // 根据确认帧数据域格式解析数据(这里假设数据域为1字节,表示终端工作模式确认)
                uint8_t work_mode_confirmation = *user_data;
                if (work_mode_confirmation == 0x01) {
                    printf("Terminal work mode set to self-report successfully.\n");
                } else {
                    printf("Terminal work mode set failed.\n");
                }
            }
        }
    } else {
        printf("Received frame is incorrect.\n");
    }
}


// 示例:遥测终端自报实时数据(上行)
void terminalSelfReportRealTimeDataUp() {
    // 控制域设置
    ControlField control;
    control.DIR = 1;   // 上行帧
    control.DIV = 0;   // 单帧
    control.FCB = 0;   // 初始帧计数位
    control.function_code = UP_SELF_REPORT_RAINFALL;   // 自报雨量数据

    // 地址域设置(假设地址为0102030405)
    uint8_t address[5] = {0x01, 0x02, 0x03, 0x04, 0x05};

    // 应用层功能码
    uint8_t AFN = TERMINAL_SELF_REPORT_REAL_TIME_DATA;

    // 其他参数(密码、时间标签等,此处假设为默认值)
    Password password = {0};   // 密码部分根据实际情况填充或保持默认
    TimeTag time_tag = {0};   // 时间标签部分根据实际情况填充或保持默认

    // 用户数据(假设雨量值为10.5mm,根据数据格式转换为BCD码)
    uint8_t user_data[3];
    SZY206_Dec2bcd(user_data, 10.5, 3, 1);   // 将雨量值转换为BCD码格式

    // 计算帧长度并填充帧
    uint16_t user_data_length = sizeof(user_data);
    uint8_t buffer[SZY206_MIN_FRAME_LEN + user_data_length];   // 根据预计最大长度分配缓冲区
    uint8_t frame_length = SZY206_FrameFill(buffer, control, address, AFN, password, time_tag, user_data, user_data_length);

    // 发送帧(这里假设通过无线通信模块发送,实际根据通信方式实现)
    sendFrame(buffer, frame_length);   // 发送帧的函数需根据实际通信方式实现
}


// 示例:解析遥测终端自报实时数据的下行确认帧
void parseTerminalSelfReportRealTimeDataDown(uint8_t *received_buffer, uint16_t received_length) {
    // 验证帧的正确性
    if (SZY206_FrameVerification(received_buffer, received_length)) {
        // 解析帧
        ControlField control;
        uint8_t AFN;
        Password password;
        TimeTag time_tag;
        uint8_t *user_data;
        if (SZY206_Parse(received_buffer, received_length, &control, &AFN, &password, &time_tag, &user_data)) {
            if (control.DIR == 0 && AFN == TERMINAL_SELF_REPORT_REAL_TIME_DATA) {
                // 根据确认帧数据域格式解析数据(这里假设数据域为1字节,表示终端工作模式确认)
                uint8_t work_mode_confirmation = *user_data;
                if (work_mode_confirmation == 0x01) {
                    printf("Terminal is in self-report work mode.\n");
                } else if (work_mode_confirmation == 0x02) {
                    printf("Terminal is in query/response work mode.\n");
                } else {
                    printf("Terminal work mode confirmation error.\n");
                }
            }
        }
    } else {
        printf("Received frame is incorrect.\n");
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2271535.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

driftingblues6靶场攻略

首先 打开kali&#xff0c;扫描主机 地址是192.168.111.143 访问网站 主页源码看一看&#xff0c;没啥用 老套路&#xff0c; 用nmap扫描一下开放端口 用dirsearch扫描一下目录 如果说扫描不到&#xff0c;那就可能是字典不行&#xff0c;换工具就完了 nmap -sV 192.168.…

【顶刊TPAMI 2025】多头编码(MHE)之Part 6:极限分类无需预处理

目录 1 标签分解方法的消融研究2 标签分解对泛化的影响3 讨论4 结论 论文&#xff1a;Multi-Head Encoding for Extreme Label Classification 作者&#xff1a;Daojun Liang, Haixia Zhang, Dongfeng Yuan and Minggao Zhang 单位&#xff1a;山东大学 代码&#xff1a;https:…

vue视频录制 限制大小,限制时长

<template><div style"height: 100vh;background: #000;"><span style"color: #fff;font-size: 18px;">切换数量&#xff1a;{{ devices.length }}</span><video ref"video" autoplay muted playsinline></vid…

毕业项目推荐:基于yolov8/yolov5的行人摔倒检测识别系统(python+卷积神经网络)

文章目录 概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式&#xff09;功能6 支持切换检测到的目标查看 二、数据集三、算法介绍1. YO…

高等数学学习笔记 ☞ 无穷小比较与等价无穷小替换

1. 无穷小比较 1. 本质&#xff1a;就是函数的极限趋于0时的速度&#xff0c;谁快谁慢的问题。 2. 定义&#xff1a;若是在同一自变量的变化过程中的无穷小&#xff0c;且&#xff0c;则&#xff1a; ①&#xff1a;若&#xff0c;则称是比的高阶无穷小&#xff0c;记作&…

基于Spring Boot的智能笔记的开发与应用

一、项目背景与意义 智能笔记系统旨在为用户提供便捷、高效的笔记记录与管理服务。通过智能化的功能&#xff0c;如自动分类、标签管理、全文搜索等&#xff0c;用户可以更加轻松地管理和查找自己的笔记。同时&#xff0c;基于Spring Boot的开发使得系统具有高度的可扩展性和可…

word中插入zotero引用

1、参考文献末尾没有文献&#xff1f; 在文献条目要显示的地方点击“refresh” 2、参考文献条目没有悬挂缩进&#xff1f; 把“书目”添加到样式库中&#xff0c;修改样式为悬挂缩进1.5字符 3、交叉引用&#xff1f; 宏 新建一个宏 粘贴下面代码 Public Sub ZoteroLinkCita…

简历_专业技能_熟悉Redis常用数据结构及其操作命令

系列博客目录 文章目录 系列博客目录1.Redis通用命令2.String类型3.Hash类型4.List类型5.Set类型6.Sorted类型7.StringRedisTemplate 1.Redis通用命令 通用指令是部分数据类型的&#xff0c;都可以使用的指令&#xff0c;常见的有&#xff1a; KEYS&#xff1a;查看符合模板的…

快速将索尼手机联系人导出为 HTML 文件

我想将 Sony Xperia 手机上的联系人导出到计算机上进行备份&#xff0c;并在需要时进行编辑。这可以做到吗&#xff1f;如何做到&#xff1f;作为助手我需要下载什么工具吗&#xff1f; 当您的 Android 手机上存储了如此多的重要联系人&#xff0c;而您又不想丢失它们时&#…

爬虫案例-爬取某度文档

文章目录 1、第三方库的安装和pytesseract安装2、爬取某度文档的代码3、效果图 1、第三方库的安装和pytesseract安装 #以下是安装http请求的第三方库 pip install requests #以下是安装处理文档的第三方库 pip install python-docx #以下是安装处理图片的第三方库 pip install…

《塑战核心》V1.0.0.9952官方中文版

体验打击感满分的近距离战斗。击败蜂拥而至的敌人&#xff0c;每次击杀都会让你变得更强。 《塑战核心》官方中文版https://pan.xunlei.com/s/VODW7effpagQN1JU0UpBQQ5uA1?pwdmr8g#

电子邮件对网络安全的需求

&#xff08; 1&#xff09;机密性&#xff1a;传输过程中不被第三方阅读到邮件内容&#xff0c;只有真正的接收方才可以阅读邮件。&#xff08; 1.5 分&#xff09; &#xff08; 2&#xff09;完整性&#xff1a;支持在邮件传输过程中不被篡改&#xff0c;若发生篡改&#…

【嵌入式硬件】直流电机驱动相关

项目场景&#xff1a; 驱动履带车&#xff08;双直流电机&#xff09;前进、后退、转弯 问题描述 电机驱动MOS管烧毁 电机驱动采用IR2104STRH1R403NL的H桥方案&#xff08;这是修改之后的图&#xff09; 原因分析&#xff1a; 1.主要原因是4路PWM没有限幅&#xff0c;修改…

用户注册模块(芒果头条项目进度4)

1 创建⽤户模块⼦应⽤ 1.1 在项⽬包⽬录下 创建apps的python包。 1.2 在apps包下 创建应⽤userapp $ cd 项⽬包⽬录/apps $ python ../../manage.py startapp userapp 1.3 配置导包路径 默认情况下导包路径指向项⽬根⽬录 # 通过下⾯语句可以打印当前导包路径 print(sys.pa…

element输入框及表单元素自定义前缀

如图所示&#xff1a; <el-input class"custom-input" placeholder"请输入" prefix-icon"prefix" v-model"form.name" clearable></el-input> :deep(.custom-input) {.el-input__icon {display: inline-block;width: 40…

使用MPTCP+BBR进行数据传输,让网络又快又稳

1.前言 在前文《链路聚合技术——多路径传输Multipath TCP(MPTCP)快速实践》中我们使用mptcpize run命令实现了两个节点间通信使用MPTCP协议进行传输&#xff0c;并实现了传输速率的聚合。 实际应用中更推荐原生支持mptcp的应用&#xff0c;在MPTCP官网中可以看到如TCPDump、…

电商Google广告:2025年提升转化率的5种策略

展望 2025 年&#xff0c;Google 广告领域将迎来一系列显著变化&#xff0c;这些趋势对于提升广告转化率至关重要&#xff0c;值得我们提前关注与布局。 智能化程度持续加深&#xff0c;用户搜索习惯愈发精细&#xff0c;广告格式推陈出新&#xff0c;视频广告势头正猛...那么…

基于Java的敬老院管理系统的设计和实现【源码+文档+部署讲解】

基于Java的敬老院管理系统设计和实现 摘 要 新世纪以来,互联网与计算机技术的快速发展,我国也迈进网络化、集成化的信息大数据时代。对于大众而言,单机应用早已成为过去&#xff0c;传统模式早已满足不了当下办公生活等多种领域的需求,在一台电脑上不联网的软件少之又少&#x…

如何使用OpenCV进行抓图-多线程

前言 需求&#xff1a; 1、如何使用OpenCV捕抓Windows电脑上USB摄像头的流、 2、采用多线程 3、获知当前摄像头的帧率。 这个需求&#xff0c;之前就有做了&#xff0c;但是由于出现了一个问题&#xff0c;人家摄像头的帧率目前都可以达到60帧/s 了&#xff0c;而我的程序…

Unity 中计算射线和平面相交距离的原理

有此方法 能够计算射线和平面是否相交以及射线起点到平面交点的距离 代码分析 var dot Vector3.Dot(ray.direction, plane.normal);计算射线和平面法线的点积&#xff0c;如果大于等于0&#xff0c;则说明射线和平面没有相交&#xff0c;否则&#xff0c;说明射线和平面相交…