Central South University
RFID
课程设计
学院: 信息科学与工程学院
班级:
学号:
导师:
前 言
目前,很多企业仓库管理还是停留在手工操作的基础上,这样的传统的仓库管理,一般依赖于一个非自动化的、以纸张文件为基础的系统来记录、追踪进出的货物,完全由人工实施仓库内部的管理,因此仓库管理的效率极其低下,所能管理的仓库规模也很小。随着计算机的应用普及,目前大多数企业的仓库管理数据资料已开始采用计算机数据系统管理,但数据还是采用先纸张记录、再手工输入计算机的方式进行采集和统计整理。这不仅造成大量的人力资源浪费,而且由于人为的因素,数据录入速度慢、准确率低。随着企业规模的不断发展,仓库管理的物资种类机数量在不断增加、出入库频率剧增,仓库管理作业也已十分复杂和多样化,传统的人工仓库作业模式和数据采集方式已难以满足仓库管理的快速、准确要求,严重影响了企业的运行工作效率,成为制约企业发展的一大障碍。
目前RFID技术正在为供应链领域带来一场巨大的变革,以识别距离远,快速,不易损坏,容量大等条码无法比拟的优势,简化繁杂的工作流程,有效改善供应链的效率和透明度。基于RFID 的仓库管理系统是在现有仓库管理中引入RFID 技术,对仓库到货检验、入库、出库、调拨、移库移位、库存盘点等各个作业环节的数据进行自动化的数据采集,保证仓库管理各个环节数据输入的速度和准确性,确保企业及时准确地掌握库存的真实数据,合理保持和控制企业库存。通过科学的编码,还可方便地对物品的批次、保质期等进行管理。利用系统的库位管理功能,更可以及时掌握所有库存物资当前所在位置,有利于提高仓库管理的工作效率。
一、 实现目标
·在本方案中,速度、效率、正确率、信息的整合是重点追求的目标。主要在于提高仓库管理正确性、管理精度及操作的方便性;
·在仓库管理系统中融入RFID技术,应用货物包装、货位二种电子标签来辅助进行仓库管理,提高企业效率;
·最小包装单位管理,即存储的每一最小包装单位都有唯一标示,为库存精细管理提供支持;
·记录各种零部件入库上架、进出数量、位置等记录;在仓位标签中记录零部件变动信息(种类、数量、等),以跟踪仓库货物,提高入、出、存、移的正确率,减少在日常工作中对人的依赖性,降低人为失误;
·在移库移仓管理上,仓管人员可根据电子标签进行货物快速定位并自动在系统中调整仓位;
·在库存盘点中,脱离单据提高盘点效率,不仅盘点库存数量而且盘点位置并保证货物与位置(仓位)对应得正确率,并能在平时随时进行区域盘点,在日常仓库工作中保证库存的正确,尤其可以在入出正常的时候进行盘点工作;
·有效区分实物库存及列帐库存,有效管理票、物时间差,方便仓库与财务对账。
二、系统构成
RFID系统由“RFID电子标签(RFID Tag)”、“读写器(天线+控制器)”、“PC等高级设备”这三要素构成。“RFID Tag”由可保存大量数据的IC芯片及天线构成,根据“PC等高级设备”的指令,保存在RFID芯片中的数据可由“读写器”读取或擦写。
(1)RFID电子标签
内置全球唯一ID,并拥有96bit/240Bit空间存储用户定制信息;防水,防油污;使用寿命长;读取距离长,标签数据可以加密,且信息防篡改;标签可擦写,可循环利用
(2)远距离读写器
采用串口与电脑通信;RFID标签非接触式的进行读取货物信息,准确率为98高。读写距离远标准、符合FCC条例;读取性能可靠。
(3)管理软件
仓库系统管理软件采用原有SAP管理软件结合新WCS系统,系统中标签数据的读写、相关设备的控制以及与新仓库管理软件的数据接口通过采用国际先进的RFID 中间件实现。
RFID 中间件采用三层结构体系,即虚拟硬件层、数据处理层和应用接口层。
1) 虚拟硬件层:
通过虚拟硬件的方法实现对RFID 硬件设备的参数设定和操作管理。
这一层,RFID 硬件控制器提供了对RFID 读写器以及其它RFID 设备的控制。具体有以下一些功能:
· 支持RFID 设备的在线和离线两种连接方式;
·RFID 硬件设备的驱动以及读写控制。
2) 数据处理层:
这一层实现了对数据的处理工作,对数据的处理工作通过RFID 数据控制器来实现,应用到了数据库等,RFID 数据控制器的作用是:
·从RFID 硬件控制器读取标签数据;
·将数据传送给RFID 硬件控制器;
·进行数据格式的转换、数据重组、过滤和分析等数据处理工作。
3) 应用接口层:
应用接口层是原有仓库管理软件与RFID 系统交互的通道。
这一层的服务有RFID 前置服务、应用集成服务以及EPC 信息服务。它们实现的功能为
RFID 前置服务:为所有的RFID 控制器提供支持,担当了所有RFID 控制器的主控制器功能;
üRFID 是整个仓库RFID 管理系统的数据和网络连接的中心点;RFID 前置服务也担负着对RFID 管理系统的业务流程的监控的功能(类似于EJB对象对业务逻辑的监控);
RFID 前置服务也为用户提供了http以及数据检索等方法的支持;RFID 也担当之于应用集成服务进行数据交互服务的功能。
三、实验流程
(1)入库流程
功能要求
·查询功能
·非计划入库输入功能
·上架单生成功能(越库中转采用虚拟库位)
·上架执行功能
·入库单关闭功能
基本步骤
第一步 到货后仓库验收:事务员等待质保人员验收货物后需要进行产品品种、数量的核对。这部分工作可以由手持终端来完成的。首先将所有本次进货的单据、产品信息下载到手持终端中,手持终端将提示材料管理员输入购货单的号码。材料管理员首先识读这个电子标签ID号,然后手持终端的系统判断这个电子标签是否正确,如果不正确,系统立刻向材料管理员做出警示;如果正确,材料管理员再扫描所购材料单上的项目号,系统随后检查购货单上的项目是否与实际进货相符。接着,材料管理员扫描物料规格信息、批次条码和标识号的电子标签等。这个标识号唯一标识购入的这件物料,作为一个最基本的信息用于以后所有的库存管理环节中。如果有不符合订货要取的物料,系统将给出相应的信息。同时需要将验收后的相关信息如批次、数量、生产日期等传送给RFID系统;
第二步 初始化货物标签:计算机需要根据入数计算共需多少标签生成标签入库指示清单,将根据入库预报单中的供应商代码、货物代码、订单号、批次、数量(验收后的实际数量)等按照最小库存包装单位写入电子货物标签(以便可跟踪具体入库包装货物的采购情况,当然还要考虑标签容量,同时要记录UID号,建立数据关联);同时根据规则指示入库仓位:规则指如能互相替换的物料、经常一起配套出库的物料等尽可能放在一起(需要在基础数据初始化中设置该规则),将指示的仓位同时写入货物标签,以利于上架时比对仓位是否放置正确;将含仓位指示的单据信息(仓位布置单)传给手持终端;写好信息的标签根据清单安装到包装货物包装上;
第三步 上架确认有如下两种方式:
根据指示上架确认:上架工查找仓位(扫描仓位标签,与入库清单中的相同,则表示找到,也可以通过在标签上印刷仓位号,人工查找),找到后将入库货物上架,扫描上架货物电子标签,确认该仓位上上架货物正确,上架确认完毕后在手持机中该标签序号的纪录打上入库标记,防止重复确认。入库数据一般根据代码、批次、质检日期(入库日期)、质检标准、标签UID号等不同在系统中作不同记录(即使在同一仓位上);由于UID号与货物代码、订单号、数量关联,所以可以只比对仓位号、UID号;
第四步 更新仓位标签:指用手持机更新仓位标签中当前记载的该货物编码、数量。
第五步 事务员将手持机中该单据入库数据上传至RFID系统中,完成库存更新及入库流水账记录。
功能要求
·查询功能
·非计划出库输入功能
·拣货单生成功能(越库中转采用虚拟库位)
· 拣货单执行功能
· 出库确认功能(门式设备完成,连接PC终端)
基本步骤
第一步 生成拣货单数据:操作员在RFID系统中根据出库单生成拣选单(仓位按出库路线排列),含领料单号、拣选仓位、货物代码、供应商代码、批号、整包装数量(整包装指RFID管理的最小包装单位)、散数[散数与整包装数为两条纪录即使同一仓位、同一货物]。仓位的拣选按照仓库类型进行先入先出控制(根据入库日期、批次或质检日期等)。
第二步 领料拣选:拣选工将拣选单下载到手持机中,通过扫描货物标签,将扫描到的货物标签仓位信息、代码信息与拣选单中的比对,如一致,则表示拣选正确,分为:整包装取货,则直接从拣选单的整包装数量中扣除该标签中纪录数量,同时该序号的标签在手持机中被打上标记,防止重复扫描,该标签回收;拆零拣货,则按照拣选单的散数确认,同时将散数从货物标签记载的数量中扣除。
第三步 更新仓位标签:指取货后更新仓位标签中的该货物的当前数量。要求工作人员进行写货位标签的操作,将更改后的货物数据写回到货位标签。直至完成此货位拣货的全部操作;
出库确认:工作人员进行出库检验,扫描配送箱或容器上的电子标签(单号条码、货物条码、数量条码、客户条码等)或电子标签,确认与配送单(出库单一致),完成出库的确认工作(贝岭成品库可能需要,也是一般企业需要的最后一步)。
最后,操作员将手持机中的拣选确认单数据上传至RFID系统中,完成库存更新及出库流水账记录。成品库可以根据拣选确认结果打印出库装箱单(配送单)及外包装电子标签。
(3)盘点流程
功能要求
·查询功能
·货物盘点启动功能
·盘点执行功能
· 盘点差异查询打印功能
·盘点复核确认功能
基本步骤
下载盘点数据至盘点机(即离线手持终端):扫描仓位标签:由于仓位标签中记载该仓位上所有货物信息,平时可以随时随地通过扫描货物标签及仓位标签,来快速盘点货物的位置及数量是否正确;当正式盘点时扫描货物标签以标签中货物的数量作理论数量(不显示),人工盘点实物后在手持机上记录实际数量;同时扫描过的货物标签在手持终端中做好标记,防止重复扫描比对处理。
(假设货物标签中不记录数量,由于对每一个货位中的货物,其数量在每次更改后都会记录在货位标签中,因此货位标签中记录的数据与系统后端数据库中的数据是一致的。所以盘点工作流程可以为:
(1)工作人员通过手持设备扫描货位标签取得此货位中货物的理论数量(此数据不会显示),然后系统将提示盘点员进行下一步操作;
(2)盘点员使用手持设备读取货物的标签或标签取得货物编码,盘点实物以后输入其数量;
(3)手持系统会将此数据与理论数量进行核对,从而可以立刻获得此仓位的货物数量是否正确;)
操作员根据初次盘点数据过滤出差异表:如果仅是仓位放置错误,数量正确,则根据情况决定是否需要调整(如移仓);如果数量出现错误,则需要过滤出出错的仓位、货物列表以便复盘。
盘点工根据差异表复盘:一般复盘需要手工盘点,直至盘点结果得到确认。
事务员开立调整单:将实际盘点的结果传输到RFID系统中,经确认发现系统库存与实际库存发生不一致,若在ERP中需要调整履历的,则直接在RFID中处理。
若货架中的每件货物(库存最小单位)上都贴RFID标签,要求每个货位都有其专用的天线(智能货架)。则可以:
(1)在后端系统中开始一个新的盘点操作。
(2)管理系统通过开启指定货位的专用天线,得到该货位中实际存放的货物的明细信息。
(3)后端管理系统通过核对系统数据和实际数据完成对指定货位的盘点。
(4)其他模块(系统维护及基本信息处理)
该模块基本包括以下功能:
·入出库履历查询
· 库存查询
·库存分布查询
·封仓管理
因为某种原因,该库位上货物进行封仓处理,不能进行拣货处理直至解封。
·库位查询
根据货物查该货物的库位分布;
根据库位区域查该库位区域中货物的分布;
·B/S结构图形化库存、库位查询功能
在内部网络中用浏览器方式加上用图形化的表现方式展现仓库中库位、货物的分布信息,方便各级人员查询操作。
·库存成本计算、价格查询功能
·数据上下传、同步功能
· 基本信息维护模块
货物基本信息、库位信息、库位、安全库存设定、库位最大存放量、库位与货物绑定设置等只影响本系统的信息维护。
·标签标识打印功能
打印标签标识,与RFID封装在一起,方便人工辨识,该标识必须易于更换。
·标签损坏处理功能
当货物标签不能正常读取时,系统支持重新生成新标签替换的功能。
·与终端设备的接口
通过专用接口及中间件完成与设备的集成。
·身份认证
主要是人员角色定义,权限控制,登陆认证等功能。
四、实验源代码
Sale函数
#include "stdafx.h"
#include "SqlUtil.h"
#include "Sales.h"
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <iomanip>
#include <cstdio>
#include <ctime>
#include <math.h>
#include "CommandLib.h"
using namespace std;
SaleManager::SaleManager()
{
sqlUtil=new ADOConn();
sqlUtil->OnInitADOConn();
cout<<"开始打开数据库连接..."<<endl;
UHFreader=new CommandLib("\\\\.\\COM4");
UHFreader->setUserFreq(902.750, 927.250, 0.500, -40, 0, 1, 400, 0x03);
}
/*
获取RFID的EPC
*/
int SaleManager::getRfieEpc(unsigned char** &EPC,int* &EPCLen)
{
unsigned char lenc = 0;
unsigned char *tagInfo=0;
int tagNum;
//int* EPCLen;
int ret=1;
while(!UHFreader->getInventory(EPC, tagNum, EPCLen));
system("cls");
cout<<"读到"<<tagNum<<"张卡..."<<endl;
cout<<"len:"<<EPCLen[0]<<endl;
for(int i=0;i<tagNum;i++)
{
cout<<"第"<<i+1<<"张卡EPC号为: ";
for(int j=0;j<EPCLen[i];j++)
printf("%.2x ",EPC[i][j]);
cout<<endl;
}
return tagNum;
}
/*
添加物品
*/
bool SaleManager::addProduct()
{
bool flag=false;
unsigned char** EPC=0;
int* EPCLen=0;
int tagNum=this->getRfieEpc(EPC,EPCLen);
_bstr_t sql="";
char sqltmp[200];
char name[50];
char buf[20];
float price;
char EPCOne[50];
char birthday[50];
for(int i=0;i<tagNum;i++)
{
cout<<"请输入商品名:"<<endl;
cin>>name
cout<<"请输入商品价格:"<<endl;
cin>>buf;
price=atof(buf);
cout<<"生产日期:"<<endl;
cin>>birthday;
/*time_t now;
time(&now);
struct tm tmTmp;
localtime_s(&tmTmp,&now);
sprintf_s(birthday,"%d-%d-%d %d:%d:%d.000\n",tmTmp.tm_year+1900,tmTmp.tm_mon+1,tmTmp.tm_mday,tmTmp.tm_hour,tmTmp.tm_min,tmTmp.tm_sec);
cout<<birthday<<endl;
for(j=0;j<EPCLen[i];j++)
{
printf("%02x",EPC[i][j]);
EPCOne[j]=EPC[i][j]+48;
}*/
sprintf_s(EPCOne,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",EPC[i][0],EPC[i][1],EPC[i][2],EPC[i][3],EPC[i][4],EPC[i][5],EPC[i][6],EPC[i][7],EPC[i][8],EPC[i][9],EPC[i][10],EPC[i][11])
sprintf_s(sqltmp,"insert into product(EPC,name,price,pdate,num) values('%s','%s',%f,'%s',1)",EPCOne,name,price,birthday);
//cout<<sqltmp<<endl;
sql=_bstr_t(sqltmp);
flag=this->sqlUtil->ExecuteSQL(sql);
cout<<"执行结束!!!"<<endl;
if(flag)
{
cout<<"插入数据成功!!!"<<endl;
}else
{
cout<<"插入数据失败。。。"<<endl;
}
}
return true;
}
/*
销售物品
*/
bool SaleManager::saleProduct()
{
char buf[100];
unsigned char** EPC=NULL;
char EPCOne[50];
int* EPCLen=0;
char name[30];
cout<<"请输入售货员的姓名:"<<endl;
cin>>name;
int tagNum=this->getRfieEpc(EPC,EPCLen);
for(int i=0;i<tagNum;i++)
{
sprintf_s(EPCOne,"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",EPC[i][0],EPC[i][1],EPC[i][2],EPC[i][3],EPC[i][4],EPC[i][5],EPC[i][6],EPC[i][7],EPC[i][8],EPC[i][9],EPC[i][10],EPC[i][11]);
sprintf_s(buf,"select * from product where num=1 and EPC='%s'",EPCOne);
_bstr_t sql=_bstr_t(buf);
_RecordsetPtr rsp=sqlUtil->GetRecordSet(sql);
this->showProductData(rsp);
sprintf_s(buf,"insert into sale values('%s','%s',getdate())",EPCOne,name);
this->sqlUtil->ExecuteSQL(buf);
sprintf_s(buf,"update product set num=0 where EPC='%s'",EPCOne);
//cout<<buf<<endl;
this->sqlUtil->ExecuteSQL(buf);
}
cout<<"交易完成!!!"<<endl;
return true;
}
/*
获取当前系统时间
*/
void SaleManager::getCurrentDateTime(char* &dateTimeStr)
{
time_t now;
time(&now);
// 定义两个变量,存储转换结果
struct tm tmTmp;
char stTmp[50];
localtime_s(&tmTmp,&now);
sprintf_s(stTmp,"%d-%d-%d %d:%d:%d.000\n",tmTmp.tm_year+1900,tmTmp.tm_mon+1,tmTmp.tm_mday,tmTmp.tm_hour,tmTmp.tm_min,tmTmp.tm_sec);
dateTimeStr=stTmp;
memcpy(dateTimeStr,stTmp,sizeof(stTmp));
cout<<"生成的日期是:"<<dateTimeStr<<endl;
}
/*
显示商品库存量
*/
bool SaleManager::displayProduct()
{
_bstr_t sql="select * from product where num=1";
_RecordsetPtr rsp=sqlUtil->GetRecordSet(sql);
showProductData(rsp);
return true;
}
void SaleManager::showProductData(_RecordsetPtr rsp)
{
try {
rsp->MoveFirst();
while(!rsp->adoEOF)
{
string epc=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("EPC"))->Value);
string name=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("name"))->Value);
string price=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("price"))->Value);
string birthday=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("pdate"))->Value);
cout<<"产品名:"<<name<<setw(2)<<"价格:"<<price<<setw(2)<<"生产日期:"<<birthday<<endl;
rsp->MoveNext();
}
}
catch (_com_error &e)
{
cout << e.Description()<<endl;
}
}
/*
显示销售额
*/
bool SaleManager::displaySale()
{
_bstr_t sql="select name,price,pdate from product where num=0";
//_bstr_t sql1="select name,count(price) from product where num =0 group by name";
_RecordsetPtr rsp=sqlUtil->GetRecordSet(sql);
//_RecordsetPtr rsp1=sqlUtil->GetRecordSet(sql1);
cout<<"查询结果:"<<endl;
try {
rsp->MoveFirst();
while(!rsp->adoEOF)
{
string epc=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("product.Epc"))->Value);
string name=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("name"))->Value);
string price=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("price"))->Value);
string saleDay=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("saledate"))->Value);
string user=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("saleMan"))->Value);
string birthday=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("pdate"))->Value);
cout<<"产品名:"<<name<<setw(3)<<"价格:"<<price<<setw(3)<<endl;
rsp->MoveNext();
}
}
catch (_com_error &e)
{
sqlUtil->ExitConnect();
cout << e.Description()<<endl;
}
/*try {
rsp1->MoveFirst();
while(!rsp1->adoEOF)
{
string name=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("name"))->Value);
string price=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("sum(price)"))->Value);
cout<<"name:"<<name<<"price:"<<price<<endl;
rsp1->MoveNext();
}
}
catch (_com_error &e)
{
sqlUtil->ExitConnect();
cout << e.Description()<<endl;
}*/
return true;
}
/*
显示主菜单
*/
int SaleManager::displayMenu()
{
printf("\n*************************************************************************\n");
printf("**************** 主菜单 ************ **** **** \n");
printf("**** 1.添加商品 | 2.销售商品 **** \n");
printf("**** 3.查看商品 | 4.查看销售 **** \n");
printf("**** 5.清空屏幕 | 6.退出程序 **** \n");
printf("**************************************************************************\n");
int choice=0;
cin>>choice;
return choice;
}
接口函数
//////////////////////////////////////////////////////////////////////////
/// COPYRIGHT NOTICE
/// Copyright (c) 2011, 上海交通大学 物联网实验室 (版权声明)
/// All rights reserved.
///
/// @file SerialPort.cpp
/// @brief 串口通信类的实现文件
///
/// 本文件为串口通信类的实现代码
///
/// @version 1.0
/// @author 陈杰
/// @E-mail:chjie@sjtu.edu.cn
/// @date 2011/04/19
///
/// 修订说明:
//////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SerialPort.h"
#include <process.h>
#include <iostream>
using namespace std;
/** 线程退出标志 */
bool CSerialPort::s_bExit = false;
///** heartBeat线程退出标志 */
//bool CSerialPort::s_bHBExit = false;
/** 当串口无数据时,sleep至下次查询间隔的时间,单位:秒 */
const UINT SLEEP_TIME_INTERVAL = 5;
//char buffer[100];
char *buffer;
int accum;
//bool ocpMark;
//bool flag = false;
//bool invFlag = false;
CSerialPort::CSerialPort(const char *portName)
: m_hListenThread(INVALID_HANDLE_VALUE)//类给成员变量赋值
{
// if(pro_Flag!=0){
// return;}
m_hComm = INVALID_HANDLE_VALUE;//INVALID_HANDLE_VALUE
m_hListenThread = INVALID_HANDLE_VALUE;
o_eCode = 0;
buffer = NULL;
InitializeCriticalSection(&m_csCommunicationSync);
if (!InitPort(portName))
{
if(5==o_eCode){
OcpMark::instance()->setVal(true);
}
cout << "initPort fail !" << endl;
// exit(-1);
}
else
{
cout << "initPort success !" << endl;
}
if (!OpenListenThread())
{
cout << "OpenListenThread fail !" << endl;
}
else
{
cout << "OpenListenThread success !" <<endl;
}
}
CSerialPort::~CSerialPort(void)//对应构造函数,称为析构函数,在对象销毁时调用
{
CloseListenTread();
ClosePort();
delete [] buffer;
OcpMark::instance()->setVal(false);
DeleteCriticalSection(&m_csCommunicationSync);
}
bool CSerialPort::InitPort( const char *portName /*= 1*/,UINT baud /*= CBR_115200*/,char parity /*= 'N'*/,
UINT databits /*= 8*/, UINT stopsbits /*= 1*/,DWORD dwCommEvents /*= EV_RXCHAR*/ )
{
/** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */
char szDCBparam[50];
sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);
/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
if (!openPort(portName))
{
return false;
}
/** 进入临界段 */
EnterCriticalSection(&m_csCommunicationSync);
/** 是否有错误发生 */
BOOL bIsSuccess = TRUE;
/** 在此可以设置输入输出的缓冲区大小,如果不设置,则系统会设置默认值.
* 自己设置缓冲区大小时,要注意设置稍大一些,避免缓冲区溢出
*/
/*if (bIsSuccess )
{
bIsSuccess = SetupComm(m_hComm,10,10);
}*/
/** 设置串口的超时时间,均设为0,表示不使用超时限制 */
COMMTIMEOUTS CommTimeouts;
CommTimeouts.ReadIntervalTimeout = 0;
CommTimeouts.ReadTotalTimeoutMultiplier = 3;
CommTimeouts.ReadTotalTimeoutConstant = 2000;
CommTimeouts.WriteTotalTimeoutMultiplier = 3;
CommTimeouts.WriteTotalTimeoutConstant = 2000;
if ( bIsSuccess)
{
bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);
}
DCB dcb;
if ( bIsSuccess )
{
// 将ANSI字符串转换为UNICODE字符串
DWORD dwNum = MultiByteToWideChar (CP_ACP, 0, szDCBparam, -1, NULL, 0);
wchar_t *pwText = new wchar_t[dwNum] ;
if (!MultiByteToWideChar (CP_ACP, 0, szDCBparam, -1, pwText, dwNum))
{
bIsSuccess = TRUE;
}
/** 获取当前串口配置参数,并且构造串口DCB参数 */
bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(pwText, &dcb) ;
/** 开启RTS flow控制 */
dcb.fRtsControl = RTS_CONTROL_ENABLE;
/** 释放内存空间 */
delete [] pwText;
}
if ( bIsSuccess )
{
/** 使用DCB参数配置串口状态 */
bIsSuccess = SetCommState(m_hComm, &dcb);
}
/** 清空串口缓冲区 */
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
/** 离开临界段 */
LeaveCriticalSection(&m_csCommunicationSync);
return bIsSuccess==TRUE;
}
bool CSerialPort::InitPort( const char *portName ,const LPDCB& plDCB )
{
/** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
if (!openPort(portName))
{
return false;
}
/** 进入临界段 */
EnterCriticalSection(&m_csCommunicationSync);
/** 配置串口参数 */
if (!SetCommState(m_hComm, plDCB))
{
return false;
}
/** 清空串口缓冲区 */
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);
/** 离开临界段 */
LeaveCriticalSection(&m_csCommunicationSync);
return true;
}
void CSerialPort::ClosePort()
{
/** 如果有串口被打开,关闭它 */
if( m_hComm != INVALID_HANDLE_VALUE )
{
CloseHandle( m_hComm );
m_hComm = INVALID_HANDLE_VALUE;
}
}
bool CSerialPort::openPort( const char *portName )
{
/** 进入临界段 */
EnterCriticalSection(&m_csCommunicationSync);
/** 打开指定的串口 */
m_hComm = CreateFileA(portName, /** 设备名,COM1,COM2等 */
GENERIC_READ | GENERIC_WRITE, /** 访问模式,可同时读写 */
0, /** 共享模式,0表示不共享 */
NULL, /** 安全性设置,一般使用NULL */
OPEN_EXISTING, /** 该参数表示设备必须存在,否则创建失败 */
0,
0);
/** 如果打开失败,释放资源并返回 */
if (m_hComm == INVALID_HANDLE_VALUE || m_hComm == ((HANDLE)-1))
{
o_eCode = GetLastError();
LeaveCriticalSection(&m_csCommunicationSync);
// cout<<"exit3"<<endl;
return false;
}
/** 退出临界区 */
LeaveCriticalSection(&m_csCommunicationSync);
return true;
}
bool CSerialPort::OpenListenThread()
{
/** 检测线程是否已经开启了 */
if (m_hListenThread != INVALID_HANDLE_VALUE)
{
/** 线程已经开启 */
return false;
}
s_bExit = false;
/** 线程ID */
UINT threadId;
/** 开启串口数据监听线程 */
m_hListenThread = (HANDLE)_beginthreadex(NULL, 0, ListenThread, this, 0, &threadId);
if (!m_hListenThread)
{
return false;
}
/** 设置线程的优先级,高于普通线程 */
if (!SetThreadPriority(m_hListenThread, THREAD_PRIORITY_ABOVE_NORMAL))
{
return false;
}
return true;
}
bool CSerialPort::CloseListenTread()
{
if (m_hListenThread != INVALID_HANDLE_VALUE)
{
/** 通知线程退出 */
s_bExit = true;
/** 等待线程退出 */
Sleep(10);
/** 置线程句柄无效 */
CloseHandle( m_hListenThread );
m_hListenThread = INVALID_HANDLE_VALUE;
}
return true;
}
//bool CSerialPort::OpenHeartBeatThread()
//{
// /** 检测线程是否已经开启了 */
// if (m_hHeartBeatThread != INVALID_HANDLE_VALUE)
// {
// /** 线程已经开启 */
// return false;
// }
// s_bExit = false;
// /** 线程ID */
// UINT threadId;
// /** 开启串口数据监听线程 */
// m_hHeartBeatThread = (HANDLE)_beginthreadex(NULL, 0, HeartBeatThread, this, 0, &threadId);
// if (!m_hHeartBeatThread)
// {
// return false;
// }
// /** 设置线程的优先级,高于普通线程 */
// if (!SetThreadPriority(m_hHeartBeatThread, THREAD_PRIORITY_ABOVE_NORMAL))
// {
// return false;
// }
// return true;
//}
//bool CSerialPort::CloseHeartBeatThread()
//{
// if (m_hHeartBeatThread != INVALID_HANDLE_VALUE)
// {
// /** 通知线程退出 */
// s_bHBExit = true;
// /** 等待线程退出 */
// Sleep(10);
// /** 置线程句柄无效 */
// CloseHandle( m_hHeartBeatThread );
// m_hHeartBeatThread = INVALID_HANDLE_VALUE;
// }
// return true;
//}
UINT CSerialPort::GetBytesInCOM()
{
DWORD dwError = 0; /** 错误码 */
COMSTAT comstat; /** COMSTAT结构体,记录通信设备的状态信息 */
memset(&comstat, 0, sizeof(COMSTAT));
UINT BytesInQue = 0;
/** 在调用ReadFile和WriteFile之前,通过本函数清除以前遗留的错误标志 */
if ( ClearCommError(m_hComm, &dwError, &comstat) )
{
BytesInQue = comstat.cbInQue; /** 获取在输入缓冲区中的字节数 */
}
return BytesInQue;
}
UINT WINAPI CSerialPort::ListenThread( void* pParam )
{
/** 得到本类的指针 */
CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);
/** 读取输入缓冲区中的数据并输出显示 */
char cRecved = 0x00;
accum = -1;
//char command;
// char length = 0;
// char tagNum;
// char num;
//char accLen = 0;
//char commandLength = 0;
if(buffer != NULL){
delete []buffer;
}
buffer = new char[1000];
// 线程循环,轮询方式读取串口数据
while (!pSerialPort->s_bExit)
{
UINT BytesInQue = pSerialPort->GetBytesInCOM();
/** 如果串口输入缓冲区中无数据,则休息一会再查询 */
/** 如果Inventory标志位为true,等一轮结束再接受*/
//if ( BytesInQue == 0 && flag1 ==false)
if ( BytesInQue == 0)
{
Sleep(SLEEP_TIME_INTERVAL);
continue;
}
do
{
cRecved = 0x00;
if(pSerialPort->ReadChar(cRecved))
{
accum++;
buffer[accum] = cRecved;
}
}while(--BytesInQue);
//accLen+=(m+1);
//cout<<"BytesInQue"<<BytesInQue<<endl;
/*即使break剩余的字节还会在串口中取得*/
//UINT BytesInQue1 = pSerialPort->GetBytesInCOM();
//if(m == length -1 && invFlag ==false){
// m = -1;
// flag = true;
//}
//if(invFlag ==true){
// if(accLen<buffer[1])
// continue;
// tagNum =buffer[2];
// for(;tagNum>0;tagNum--){
// //length = buffer[1];
// commandLength+=buffer[1+commandLength];
// num = buffer[2+commandLength];
// if((tagNum-1) !=num)
// break;
// }
//
// if((tagNum-1) != num &&(tagNum!=0&&num!=0))
// continue;
// //if(commandLength <m+1){
// if(accLen<commandLength)
// continue;
// m=-1;
// flag=true;
//}
}
return 0;
}
//UINT WINAPI CSerialPort::ListenThread( void* pParam )
//{
// /** 得到本类的指针 */
// CSerialPort *pSerialPort = reinterpret_cast<CSerialPort*>(pParam);
// /** 读取输入缓冲区中的数据并输出显示 */
// char cRecved = 0x00;
// accum = -1;
// if(buffer != NULL){
// delete []buffer;
// }
// buffer = new char[1000];
// // 线程循环,轮询方式读取串口数据
// while (!pSerialPort->s_bExit)
// {
// UINT BytesInQue = pSerialPort->GetBytesInCOM();
// /** 如果串口输入缓冲区中无数据,则休息一会再查询 */
// /** 如果Inventory标志位为true,等一轮结束再接受*/
// //if ( BytesInQue == 0 && flag1 ==false)
// if ( BytesInQue == 0)
// {
// Sleep(SLEEP_TIME_INTERVAL);
// continue;
// }
// do
// {
// cRecved = 0x00;
// if(pSerialPort->ReadChar(cRecved))
// {
// accum++;
// buffer[accum] = cRecved;
// }
// }while(--BytesInQue);
// }
// return 0;
//}
//void CSerialPort::flush(){
// m=-1;
// rdLen = 0;
//}
//
//bool CSerialPort::read(int beg, int len, char arr[]){
// while(len<m+1)
// Sleep(1);
// //arr = new char[len];
// for(int i=beg;i<beg+len;i++)
// arr[i] = buffer[i+rdLen];
// rdLen+=len;
//}
bool CSerialPort::ReadChar( char &cRecved )//这里cRecved是引用
{
BOOL bResult = TRUE;
DWORD BytesRead = 0;
if(m_hComm == INVALID_HANDLE_VALUE)
{
return false;
}
/** 临界区保护 */
EnterCriticalSection(&m_csCommunicationSync);
/** 从缓冲区读取一个字节的数据 */
bResult = ReadFile(m_hComm, &cRecved, 1, &BytesRead, NULL);
if ((!bResult))
{
/** 获取错误码,可以根据该错误码查出错误原因 */
//DWORD dwError = GetLastError();
/** 清空串口缓冲区 */
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
LeaveCriticalSection(&m_csCommunicationSync);
return false;
}
/** 离开临界区 */
LeaveCriticalSection(&m_csCommunicationSync);
return (BytesRead == 1);
}
bool CSerialPort::WriteData( unsigned char* pData, unsigned int length )
{
//for(int i=0;i<sizeof(buffer);i++)
// buffer[i] ='\0';
BOOL bResult = TRUE;
DWORD BytesToSend = 0;
if(m_hComm == INVALID_HANDLE_VALUE)
{
return false;
}
/** 临界区保护 */
EnterCriticalSection(&m_csCommunicationSync);
/** 向缓冲区写入指定量的数据 */
bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);
if (!bResult)
{
//DWORD dwError = GetLastError();
/** 清空串口缓冲区 */
PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
LeaveCriticalSection(&m_csCommunicationSync);
return false;
}
/** 离开临界区 */
LeaveCriticalSection(&m_csCommunicationSync);
return true;
}
数据库连接函数
#include "stdafx.h"
#include "SqlUtil.h"
#include <iostream>
#include <string>
using namespace std;
void ADOConn::OnInitADOConn()
{
::CoInitialize(NULL);// 初始化OLE/COM库环境,为访问ADO接口做准备
HRESULT hr;
try
{
hr = m_pConnection.CreateInstance("ADODB.Connection");///创建Connection对象
// cout<<"create Instance success"<<endl;
_bstr_t strConnect="Driver={sql server};server=(local);uid=;pwd=;database=Manager;";
hr = m_pConnection->Open( strConnect,"", "", adModeUnknown);
cout<<"open database successs"<<endl;
if(SUCCEEDED(hr))
//cout<<"connect success"<<endl;
cout<<"connect database sucess"<<endl;
}
catch(_com_error e)///捕捉异常
{
cout<<"failure to connect database"<<e.Description()<<endl;
}
}
void ADOConn::displayResultSet(_RecordsetPtr rsp)
{
try {
rsp->MoveFirst();
while(!rsp->adoEOF)
{
string bookname=(char*)(_bstr_t)(rsp->Fields->GetItem(_variant_t("vToyName"))->Value);
cout<<bookname<<endl;
rsp->MoveNext();
}
}
catch (_com_error &e)
{
cout << e.Description()<<endl;
}
}
_RecordsetPtr & ADOConn::GetRecordSet(_bstr_t bstrSQL)
{
try
{
//连接数据库,如果connection对象为空,则重新连接数据库
if(m_pConnection==NULL)
OnInitADOConn();
//创建记录集对象
m_pRecordset.CreateInstance(_uuidof(Recordset));
m_pRecordset->Open(bstrSQL,m_pConnection.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
catch(_com_error e)
{
cout<<e.Description()<<endl;
}
//返回记录集
return m_pRecordset;
}
bool ADOConn::ExecuteSQL(_bstr_t bstrSQL)//("sleect")
{
try
{
//是否已经连接数据库
if(m_pConnection ==NULL)
OnInitADOConn();
m_pConnection->Execute(bstrSQL,NULL,adCmdText);
return true;
}
catch(_com_error e)
{
//AfxMessageBox(e.Description());
cout<<e.Description()<<endl;
return false;
}
}
void ADOConn::ExitConnect()
{
//关闭记录集和连接
if(m_pRecordset !=NULL)
m_pRecordset->Close();
m_pConnection->Close();
//释放环境
::CoUninitialize();//关闭ole/com库,释放资源
}
主函数
// RFIDKCSJ.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "time.h"
#include "CommandLib.h"
#include <cstdio>
#include "string.h"
#include "iostream"
#include <windows.h>
#include <dbt.h>
#include "SqlUtil.h"
#include "Sales.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char** EPC=0;
int flag=0;
SaleManager* sale=new SaleManager();
while(1)
{
flag=sale->displayMenu();
switch(flag)
{
case 1:
sale->addProduct();
break;
case 2:
sale->saleProduct();
break;
case 3:
sale->displayProduct();
break;
case 4:
sale->displaySale();
break;
case 6:
exit(0);
break;
default:
system("cls");
break;
}
}
return 0;
}
五、实验总结
实验的时候遇到了不少问题:如数据库连接出错,总是连接不上。经过反复的揣摩研究,发现一方面是软件版本和兼容问题,另一方面,是数据库连接的语言贫乏,在请教同学和查阅资料后,数据库才连接成功。还有其他方面的问题,如各种窗口,界面的设计,开始的时候功能不够齐全,视图也有点单调,在经过多次的调试改正后,取得了有效的进展。
这次的试验,综合运用个各个学科的知识,如RFID,数据库,C++等,更进一步锻炼了自己的编程能力。
通过这次试验,加深了我对RFID这门课程或者说这一新兴技术的了解,见识到RFID这项技术的强大的应用功能和广阔的发展前景。
六、参考文献
《RFID技术》李卫东 教育出版社
《C++面向对象程序设计》(第一版)谭浩强 北京:清华大学出版社 20##年6月
《数据库系统概论》王珊 高等教育出版社
课程设计是培养学生综合运用所学知识,发现,提出,分析和解决实际问题,锻炼实践能力的重要环节,是对学生实际工作能力的具体训练和考察过…
课程设计总结经过一个学期对《C++程序设计》的学习,我学习到了基本的理论知识,了解到了C++语言程序设计的思想,这些知识都为我的课…
《化工原理课程设计》总结本学期顺利完成了化学工程与工艺专业共100名同学的化工原理课程设计,总体来看学生的工艺计算、过程设计及绘图…
PLC自动门课程设计心得体会:通过此次C语言程序设计实践本人实在是获益不浅!C语言是上个学期开的课程所以这个学期并没怎么看过当要开…
课程设计心得体会在初学C语言的一个学期后,学校组织我们进行了C语言实训,尝试编写一些有难度的程序。在为期两周的时间中,同组同学共同…
宁波大学机械工程与力学学院工业工程系课程设计报告20XX20XX学年第1学期课程名称:现代物流设施与规划设计题目:RFID射频识别…
武汉理工大学华夏学院武汉理工大学华夏学院课程设计报告课程名称射频识别基础课程设计题目高频数据块写入专业信息工程系班级学号姓名成绩指…
《RFID课程设计报告》学生姓名**指导教师**学院信息科学与工程学院专业班级物联网1202班学号**完成时间20**年4月28日…
工程论文参考文献参考3篇工程论文参考文献参考一1王仁祥电力新技术概论M北京中国电力出版社20xx2戈东方电力工程电气设计手册第1册…
RFID原理及技术课程设计设计方案题目基于RFID的个人身份识别系统系别计算机与信息工程学院班级13物联网20xx061101学号…