基于ASCII码的通讯协议解析与数据存储技术——经验之谈

27 篇文章 0 订阅

做了通讯已经有好几年了,一直用QT做开发,对于C的很多东西都不是很训练的运用了,其实做程序开发,就是要讲究简单、高效、稳定,C做为C++的基础,是最容易体现这些特点的!

对于QT进行ASCII码的字符串解析,我们通常是怎样解析的呢?

以NMEA0183协议为例:

"$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n"

首先我们要对这个协议进行分析

/*$GPRMC
    字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
    字段1:UTC时间,hhmmss.sss格式
    字段2:状态,A=定位,V=未定位
    字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
    字段4:纬度N(北纬)或S(南纬)
    字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
    字段6:经度E(东经)或W(西经)
    字段7:速度,节,Knots
    字段8:方位角,度
    字段9:UTC日期,DDMMYY格式
    字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
    字段11:磁偏角方向,E=东W=西
    字段16:校验值*/

然后想办法对它进行解析,这是一个GPRMC协议格式,用QT解析它的时候,我们第一个想到的是字符串分割,如下:

QString gprmcStr = "$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n";
QStringList  gprmcStrList = gprmcStr .split(QRegExp("[,*]"));
for(int i = 0;i< gprmcStrList.size();i++)
{
	switch(i)
	{
	case 0:
		QString headStr = gprmcStrList .at(i);
		break;
	case 1:
		QString dateStr = gprmcStrList .at(i);
		break;
	case 2:
		....
	}
}

用C语言解析,我们怎么解析呢,这个方法以前经常用,但是C++用久了后,尤其QT平台下,我们会渐渐淡忘!

float GPS_MS = 0.0;
    char GPS_FLAG;
    double lat = 0.0;
    char lat_dir;
    double lon = 0.0;
    char lon_dir;
    double speed = 0.0;
    double angle = 0.0;
    long GPS_DAY = 0;
    double M_dec = 0.0;
    char M_dec_dir;
    int crc = 0;
    
    char str[] ="$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n";
    sscanf (str,"$GPRMC,%f,%c,%f,%c,%f,%c,%f,%f,%d,%f,%c*%d\r\n"
                   ,&GPS_MS,&GPS_FLAG,&lat,&lat_dir,&lon,&lon_dir,&speed,&angle,&GPS_DAY,&M_dec,&M_dec_dir,&crc);
     

怎么样,是不是感觉这样子简单明了,还不会出错?对于有些东西数值类型我们是需要注意的,比如时间字段,还需要我们后面再格式转化成字符串!

这里我们用sscanf来解析字符串,我们也可以用sprintf来字符串格式化,进行数据协议重构!

上面是C语言中的用法,C++写C兼容,当然可以用于到C++里去!

不过QT里面,QString也重新实现了sprintf方法,所以在进行时间和日期解析时,我们可以用得到;上面以经说过在QT中解析的方法,但是那样解析不全面,尤其在解析日期和时间时会遇到问题,在开发过程中,我们会遇到两个因为开发平台不同,在数据解析过程中会使得数据缺损,比如时间和日期,A在读取时间和日期时,会直接把协议中的时间分别赋值给float和long,而在使用sprintf重组协议时没有进行格式控制,这样会导致前导0丢失。而在QT中接收并解析时,日期和时间在赋值给QDate和QTime时进行格式化赋值,QDate::fromString(str,"ddMMyy"),QTime::fromString(str,"hhmmss.zzz"),会导致无法解析的情况,我们在分割后只要再进行一下格式控制就可以了,如下:

QString dateStr;
                dateStr.sprintf("%06d",wtdList.at(i).toInt());
                mUTCDate = QDate::fromString(dateStr,"ddMMyy");
                //mUTCDate = QDate::fromString(wtdList.at(i),"ddMMyy");

 QString timeStr;
                timeStr.sprintf("%010.3f",wtdList.at(i).toFloat());
                //mUTCTime = QTime::fromString(wtdList.at(i),"hhmmss.zzz");
                mUTCTime = QTime::fromString(timeStr,"hhmmss.zzz");

以上是我在开发过程中遇到的问题,并总结了一下,这样编写的程序耦合度比较好,程序不容易崩溃!

在解析数据完成后,我们需要对数据进行存储,存储的形式多种多样,绝大多数会采用数据库来存储数据!接下来我来说说如何来设计程序进行数据存储!

在解析数据后,大家一般会定义各个变量来存储各个指标值,上像上面的程序一样,然后组成SQL语句进行数据上传。这种方式不利于程序模块化!这个时候我们要对数据结构进行封装。

C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。

struct能包含成员函数吗? 能!
struct能继承吗? 能!!
struct能实现多态吗? 能!!!
 

我们定义了一个 struct GPRMC_DATA这样的结构体类型:

struct GPRMC_DATA
{
	float GPS_MS;
	char GPS_FLAG;
	double lat;
	char lat_dir;
	double lon;
	char lon_dir;
	double speed;
	double mAngle;
	long GPS_DAY;
	double M_dec;
	char M_dec_dir;
	int crc;
	GPRMC_DATA();
	GPRMC_DATA(const GPRMC_DATA &gprmcData);
	GPRMC_DATA & operator=(const GPRMC_DATA &gprmcData);
	std::string InsertSQL();
};


GPRMC_DATA::GPRMC_DATA()
{
	GPS_MS = 0.0;
	GPS_FLAG = ' ';
	lat = 0.0;
	lat_dir = ' ';
	lon = 0.0;
	lon_dir = ' ';
	speed = 0.0;
	mAngle = 0.0;
	GPS_DAY = 0;
	M_dec = 0.0;
	M_dec_dir = ' ';
	crc = 0;
};
GPRMC_DATA::GPRMC_DATA(const GPRMC_DATA &gprmcData)
{
	GPS_MS = gprmcData.GPS_MS;
	GPS_FLAG = gprmcData.GPS_FLAG;
	lat = gprmcData.lat;
	lat_dir = gprmcData.lat_dir;
	lon = gprmcData.lon;
	lon_dir = gprmcData.lon_dir;
	speed = gprmcData.speed;
	mAngle = gprmcData.GPS_DAY;
	GPS_DAY = gprmcData.GPS_MS;
	M_dec = gprmcData.M_dec;
	M_dec_dir = gprmcData.M_dec_dir;
	crc = gprmcData.crc;
};

GPRMC_DATA & GPRMC_DATA::operator=(const GPRMC_DATA &gprmcData)
{
	GPS_MS = gprmcData.GPS_MS;
	GPS_FLAG = gprmcData.GPS_FLAG;
	lat = gprmcData.lat;
	lat_dir = gprmcData.lat_dir;
	lon = gprmcData.lon;
	lon_dir = gprmcData.lon_dir;
	speed = gprmcData.speed;
	mAngle = gprmcData.GPS_DAY;
	GPS_DAY = gprmcData.GPS_MS;
	M_dec = gprmcData.M_dec;
	M_dec_dir = gprmcData.M_dec_dir;
	crc = gprmcData.crc;
	return *this;
}

std::string GPRMC_DATA::InsertSQL()
{
	std::ostringstream sql;
	sql << "insert into residential_list(GPS_MS, GPS_FLAG, lat, lat_dir, lon, lon_dir, speed, angle,GPS_DAY,M_dec,M_dec_dir,crc) values("
		<< "\'" << GPS_MS << "\',"
		<< "\'" << GPS_FLAG << "\',"
		<< setprecision(15)
		<< "\'" << lat << "\',"
		<< "\'" << lat_dir << "\',"
		<< setprecision(15)
		<< "\'" << lon << "\',"
		<< "\'" << lon_dir << "\',"
		<< "\'" << speed << "\',"
		<< "\'" << mAngle << "\',"
		<< "\'" << GPS_DAY << "\',"
		<< "\'" << M_dec << "\',"
		<< "\'" << M_dec_dir << "\',"
		<< "\'" << crc << "\'"
		<< ")";
	return sql.str();
}

这样有利于程序模块化,如果要解析GPGGA或GPGLL这样的数据,我们再定义两个这样的数据类型就可以了,在赋完值后,只需执行InsertSQL功能函数便可以方便的获得sql语句。

欢迎大家指正,一起探讨!



  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值