第一篇:iOS 藍(lán)牙使用小結(jié) bluetooth
iOS 藍(lán)牙使用小結(jié) bluetooth
現(xiàn)將創(chuàng)建藍(lán)牙工程的要點(diǎn)總結(jié)一下,由于工程主要涉及中心模式,所以只總結(jié)中心模式的用法 1,引入CoreBluetooth.framework 2,實(shí)現(xiàn)藍(lán)牙協(xié)議,如:.h文件如下
@protocol CBCentralManagerDelegate;
@protocol CBPeripheralDelegate;@interface ViewController : UIViewController.m文件如下
#import “CoreBluetooth/CoreBluetooth.h”
另外還有代理部分請(qǐng)自行添加3,下面是使藍(lán)牙動(dòng)起來(lái)的過(guò)程
3.1創(chuàng)建CBCentralManager實(shí)例
self.cbCentralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
設(shè)置代理,比如:
self.cbCentralMgr.delegate = self;創(chuàng)建數(shù)組管理外設(shè)
self.peripheralArray = [NSMutableArray array];3.2掃描周圍的藍(lán)牙
實(shí)際上周圍的藍(lán)牙如果可被發(fā)現(xiàn),則會(huì)一直往外發(fā)送廣告消息,中心設(shè)備就是通過(guò)接收這些消息來(lái)發(fā)現(xiàn)周圍的藍(lán)牙的
NSDictionary * dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self.cbCentralMgr scanForPeripheralsWithServices:nil options:dic];3.3發(fā)現(xiàn)一個(gè)藍(lán)牙設(shè)備
也就是收到了一個(gè)周圍的藍(lán)牙發(fā)來(lái)的廣告信息,這是CBCentralManager會(huì)通知代理來(lái)處理
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
}
如果周圍的藍(lán)牙有多個(gè),則這個(gè)方法會(huì)被調(diào)用多次,你可以通過(guò)tableView或其他的控件把這些周圍的藍(lán)牙的信息打印出來(lái)
3.4連接一個(gè)藍(lán)牙
[self.cbCentralMgr connectPeripheral:peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
一個(gè)中心設(shè)備可以同時(shí)連接多個(gè)周圍的藍(lán)牙設(shè)備
當(dāng)連接上某個(gè)藍(lán)牙之后,CBCentralManager會(huì)通知代理處理-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { }因?yàn)樵诤竺嫖覀円獜耐庠O(shè)藍(lán)牙那邊再獲取一些信息,并與之通訊,這些過(guò)程會(huì)有一些事件可能要處理,所以要給這個(gè)外設(shè)設(shè)置代理,比如: peripheral.delegate = self;
3.5查詢藍(lán)牙服務(wù)
[peripheral discoverServices:nil];返回的藍(lán)牙服務(wù)通知通過(guò)代理實(shí)現(xiàn)
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {for(CBService* service in peripheral.services){
} }
3.6查詢服務(wù)所帶的特征值
[peripheral discoverCharacteristics:nil forService:service];
返回的藍(lán)牙特征值通知通過(guò)代理實(shí)現(xiàn)-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {for(CBCharacteristic * characteristic in service.characteristics){ } }
3.7給藍(lán)牙發(fā)數(shù)據(jù)
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
這時(shí)還會(huì)觸發(fā)一個(gè)代理事件
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }
3.8處理藍(lán)牙發(fā)過(guò)來(lái)的數(shù)據(jù)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }3.9 retrievePeripheralsWithIdentifiers 使用例子
-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@“"];
NSMutableArray * Identifiers = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){ [Identifiers addObject:peripheral.identifier];}[self addLog:@”[self.cbCentralMgr retrievePeripheralsWithIdentifiers:self.PeripheralIdentifiers]“];
self.retrievePeripherals = [self.cbCentralMgr retrievePeripheralsWithIdentifiers:Identifiers];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@“,peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}3.10 retrieveConnectedPeripheralsWithServices 使用例子-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@”“];
NSMutableArray * services = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){
if(peripheral.isConnected){
for(CBService *service in peripheral.services){ [services addObject:service.UUID];} } }[self addLog:@”[self.cbCentralMgr retrieveConnectedPeripheralsWithServices:peripheral.services]“];
self.retrievePeripherals = [self.cbCentralMgr retrieveConnectedPeripheralsWithServices:services];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@",peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}
第二篇:藍(lán)牙知識(shí)小結(jié)
藍(lán)牙協(xié)議知識(shí)總結(jié)
藍(lán)牙設(shè)備 和 主機(jī)進(jìn)行連接和數(shù)據(jù)通信的 流程如下:外部設(shè)備發(fā)出廣告(帶有UUID信息等其他信息); 主機(jī)(集中器設(shè)備)收到廣告信息,進(jìn)而發(fā)送掃描請(qǐng)求;表示我掃描到你的信息; 3 外部設(shè)備收到掃描請(qǐng)求后,返回掃描回應(yīng),表示我知道你掃描到我的信息; 4 主機(jī)進(jìn)而發(fā)送連接請(qǐng)求信息,表示主機(jī)要跟設(shè)備建立無(wú)線連接; 5 設(shè)備收到連接請(qǐng)求后,發(fā)送相應(yīng)請(qǐng)求回應(yīng);表示已經(jīng)建立連接;
數(shù)據(jù)讀寫流程如下進(jìn)一步(在建立連接的基礎(chǔ)上): 主機(jī)發(fā)送主服務(wù)UUID(設(shè)備的廣告UUID)給設(shè)備;
服務(wù)發(fā)現(xiàn) 7 設(shè)備收到后回應(yīng)服務(wù)信息; 主機(jī)發(fā)送特性UUID;
特性發(fā)現(xiàn) 9 設(shè)備收到后回應(yīng)特性值句柄;(類似于存儲(chǔ)設(shè)備的地址)主機(jī)發(fā)送特性值句柄;
讀信息 11 設(shè)備收到后回應(yīng)特性值; 主機(jī)發(fā)送特性值句柄和要寫入值;
寫信息 13 設(shè)備回應(yīng)寫入成功響應(yīng);
在睡眠狀態(tài),耗電只有1微安(uA),而在連接事件中最高的是10幾個(gè)毫安
連接建立之后,再進(jìn)行安全密鑰的交換配對(duì),進(jìn)而進(jìn)行數(shù)據(jù)的讀寫;
主機(jī)和從機(jī)綁定之后,斷開連接后,可以快速的建立連接并進(jìn)行加密讀寫,而不需要再次配對(duì); 特點(diǎn) 低功耗藍(lán)牙速度只有100bps,傳統(tǒng)藍(lán)牙有3Mbps 2 低功耗藍(lán)牙不需要IOS 的MFI 認(rèn)證,傳統(tǒng)藍(lán)牙必須; 3 低功耗藍(lán)牙能紐扣電池能用1年多,傳統(tǒng)藍(lán)牙不行;
頻道:
2.4G – 2.48G 總共40個(gè)頻段,每2M 一個(gè)頻段;
其中 37(2.40G),38(2.426G),39(2.48G)為 3個(gè)廣播頻道;這3個(gè)頻道避開了wifi 常用的頻道,與wifi可以共存; 其他37個(gè)為連接頻道;
1、BLE中主從機(jī)建立連接,到配對(duì)和綁定的過(guò)程如下圖。
正如上圖所示,最簡(jiǎn)單一次藍(lán)牙通信需要以上相關(guān)步驟,包括discovery device,connect,pairing,bond等4個(gè)主要部分。
1)廣播:廣播包可以包含廣播數(shù)據(jù),廣播包可以無(wú)指定或者對(duì)指定的設(shè)備發(fā)送。可以聲明該器件是可連接的還是不可連接的。在一次廣播中,廣播包可以在三個(gè)廣播通道中同時(shí)發(fā)送。
廣播類型 :1 未指定可連接 2 指定可連接 3 未指定 不可見 4 未指定不可連接 #define GAP_ADTYPE_ADV_IND
0x00 //!< Connectable undirected advertisement #define GAP_ADTYPE_ADV_DIRECT_IND 0x01 //!< Connectable directed advertisement #define GAP_ADTYPE_ADV_DISCOVER_IND
0x02 //!< Discoverable undirected advertisement #define GAP_ADTYPE_ADV_NONCONN_IND
0x03 //!< Non-Connectable undirected advertisement #define GAP_ADTYPE_SCAN_RSP_IND
0x04 //!< Only used in gapDeviceInfoEvent_t 在peripheral.c中
GAPRole_Init(taskID++);進(jìn)行了初始化設(shè)置
還有有以下函數(shù)bStatus_t GAPRole_SetParameter(uint16 param, uint8 len, void *pValue)GAPRole_GetParameter(…..)可以調(diào)用進(jìn)行設(shè)置。2)scanning 1)被動(dòng)掃描: 掃描者監(jiān)聽廣播頻道的廣播包,收到后將其上傳到host層
2)主動(dòng)掃描:掃描者監(jiān)聽廣播頻道的廣播包,當(dāng)收到廣播包后掃描者發(fā)送一個(gè)scan Request包,廣播設(shè)備回應(yīng)一個(gè)scan reponse包
3)Connection 在掃描設(shè)備掃描到一個(gè)可連接的廣播消息后,掃描設(shè)備可以通過(guò)發(fā)送 connection reequst 包給廣播設(shè)備從而成為連接的發(fā)起者
Connection resqust 包含從機(jī)鏈路層一系列的參數(shù),這些參數(shù)聲明連接時(shí)的通道及時(shí)序要求。建立連接
GAPCentralRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,DEFAULT_LINK_WHITE_LIST,addrType, peerAddr);廣播設(shè)備接收了連接請(qǐng)求,就進(jìn)入了連接狀態(tài),發(fā)起者成了主機(jī),廣播設(shè)備成了從機(jī)。兩個(gè)已連接的設(shè)備的所有通信發(fā)生在連接事件中,連接事件周期性的發(fā)生,周期由連接間隔參數(shù)決定。
連接間隔:使用調(diào)頻的間隔;兩個(gè)連接事件之間的時(shí)間間隔,藍(lán)牙傳數(shù)據(jù)是在一個(gè)頻段發(fā)送數(shù)據(jù)后,然后跳到另一個(gè)頻段再傳數(shù)據(jù),從一個(gè)頻道另一個(gè)頻段的時(shí)間間隔就是連接間隔;即使沒(méi)有數(shù)據(jù)發(fā)送,也要調(diào)頻切換測(cè)試包是否連接斷開;所以,連接間隔是定時(shí)的存在;可以認(rèn)為是一個(gè)固定的時(shí)序;每隔一段時(shí)間就自動(dòng)調(diào)到另一個(gè)頻道的去建立連接;這個(gè)時(shí)間中,是很少功耗的,基本沒(méi)有;
單位是1.25毫秒;范圍是 6----3200個(gè)單位;也就是1.25ms到4s的范圍 不同的應(yīng)用 時(shí)間間隔不一樣,時(shí)間間隔長(zhǎng),功耗就低,傳輸數(shù)據(jù)慢; 時(shí)間間隔短,功耗就高,傳輸數(shù)據(jù)就快。
從機(jī)延時(shí): 從機(jī)如果沒(méi)有數(shù)據(jù)發(fā)送,可以跳過(guò)連接間隔,不用頻繁的定時(shí)去建立連接,從而過(guò)一段較長(zhǎng)時(shí)間再去建立連接;這個(gè)時(shí)間就是從機(jī)延時(shí)時(shí)間;從而功耗降低很多;單位是和連接間隔一樣;范圍是 0---499
管理超時(shí)
超過(guò)這個(gè)時(shí)間,還沒(méi)有建立連接,則認(rèn)為是連接丟失,斷開?;氐轿催B接狀態(tài);
單位是10ms,范圍是 10(100ms)-----3200(32s)。超時(shí)值必須比有效連接間隔大;有效連接間隔= 連接間隔×(1+從機(jī)延時(shí))
如果從機(jī)不想使用當(dāng)前的連接參數(shù),可以向主機(jī)發(fā)送連接更新請(qǐng)求,從機(jī)設(shè)備可以在任何時(shí)候發(fā)送連接更新請(qǐng)求,使得從機(jī)可以動(dòng)態(tài)的調(diào)整連接參數(shù)。
GAPCentralRole_UpdateLink(simpleBLEConnHandle,DEFAULT_UPDATE_MIN_CONN_INTERVAL,DEFAULT_UPDATE_MAX_CONN_INTERVAL,DEFAULT_UPDATE_SLAVE_LATENCY,DEFAULT_UPDATE_CONN_TIMEOUT);無(wú)論主機(jī)還是從機(jī),都可以無(wú)條件的終止當(dāng)前連接,一方請(qǐng)求終止,另一方必須在斷開連接狀態(tài)之前響應(yīng)。
連接還可以由超時(shí)而終止。超時(shí)時(shí)間小于32s,大于有效連接間隔(連接間隔×(1+從機(jī)延時(shí)))
終止連接函數(shù):
GAPCentralRole_TerminateLink(simpleBLEConnHandle);主機(jī)和從機(jī)保存各自的超時(shí)計(jì)時(shí)器,每次收到數(shù)據(jù)包就清零,一旦達(dá)到超時(shí)數(shù)值,就認(rèn)為連接已經(jīng)丟失就會(huì)斷開連接。
連接超時(shí)判斷,終止連接在程序中還沒(méi)找到。
2.BLE中的GAP和GATT GAP個(gè)人認(rèn)為就是監(jiān)控上圖中的交互狀態(tài),比如從廣播變成連接,到配對(duì)等。
GATT通俗理解為用于主從機(jī)之間的客戶端和服務(wù)器端的數(shù)據(jù)交互,以Attribute Table來(lái)體現(xiàn)。
GAP Role Profile:在GAP所處的4個(gè)角色:廣播Advertise,主機(jī)central,從機(jī)Peripheral,觀察者Observer。GATT Attribute:通用屬性配置文件。
GAP作為Peripheral Role需要設(shè)置的核心參數(shù)如下 GAPROLE_ADVERT_ENABLED:廣播使能。GAPROLE_ADVERT_DATA:廣播時(shí)的參數(shù),GAPROLE_SCAN_RSP_DATA:從機(jī)掃描響應(yīng),返回的數(shù)據(jù)包
GAPROLE_MIN_CONN_INTERVAL:處于連接狀態(tài)后的設(shè)備,都會(huì)有個(gè)hop,一段時(shí)間內(nèi)進(jìn)行數(shù)據(jù)交互,以保證兩者是連接的。當(dāng)前后兩次交互時(shí),需要等待的最小間隔時(shí)間 GAPROLE_MAX_CONN_INTERVAL:...需要等待的最大間隔時(shí)間
GAPROLE_SLAVE_LATENCY:處于連接后,從機(jī)可以做出不響應(yīng)連接請(qǐng)求的間隔數(shù)目,即跳過(guò)n個(gè)交互的連接。
GAPROLE_TIMEOUT_MULTIPLIER:從上次成功連接到這次連接成功的最大允許延時(shí)。如果規(guī)定時(shí)間內(nèi)未成功則認(rèn)為本次連接失敗,丟棄。該值必須比有效連接的間隔大。GAPROLE_PARAM_UPDATE_ENABLE:請(qǐng)求主機(jī)更新參數(shù),主機(jī)可以接受也可以拒絕。.GATT Server的相關(guān)設(shè)置函數(shù)。
// Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES);// GAP Service GATTServApp_AddService(GATT_ALL_SERVICES);// GATT attributes DevInfo_AddService();// Device Information Service SimpleProfile_AddService(GATT_ALL_SERVICES);// Simple GATT Profile 通常一個(gè)GATT中GAP server和GATT server是必須強(qiáng)制存在的,還有設(shè)備信息服務(wù)以及自己設(shè)計(jì)的profile server.SimpleProfile_AddService就是添加自己設(shè)計(jì)的profile server 在SimpleProfile_AddService函數(shù)中調(diào)用了如下函數(shù)
GATTServApp_RegisterService(simpleProfileAttrTbl, GATT_NUM_ATTRS(simpleProfileAttrTbl),&simpleProfileCBs);
} simpleProfileCBs 的函數(shù)定義如下:
CONST gattServiceCBs_t simpleProfileCBs = { simpleProfile_ReadAttrCB,// Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL
// Authorization callback function pointer };實(shí)際上就是底層讀寫數(shù)據(jù)的函數(shù),主機(jī)讀數(shù)據(jù)時(shí)從機(jī)會(huì)調(diào)用simpleProfile_ReadAttrCB函數(shù),寫數(shù)據(jù)時(shí)從機(jī)會(huì)調(diào)用simpleProfile_WriteAttrCB函數(shù)。這兩個(gè)函數(shù)在simpleGaatprofile.c 中實(shí)現(xiàn)。
注意在simpleProfile_WriteAttrCB,函數(shù)中有如下語(yǔ)句 if((notifyApp!= 0xFF)&& simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange)
{
simpleProfile_AppCBs->pfnSimpleProfileChange(notifyApp);
} 注意函數(shù)指針的用法,實(shí)際是在接收到主機(jī)數(shù)據(jù)發(fā)過(guò)來(lái)的數(shù)據(jù)后調(diào)用simpleProfileChangeCB函數(shù)來(lái)處理接收到的數(shù)據(jù)。這個(gè)函數(shù)在初始化時(shí)注冊(cè),下面會(huì)講到。作為GATT的server和client,主要通過(guò)Attribute來(lái)進(jìn)行交互,當(dāng)client請(qǐng)求server讀取數(shù)據(jù)時(shí),通過(guò)如下注冊(cè)的回調(diào)函數(shù)來(lái)進(jìn)行訪問(wèn)。
// Register callback with SimpleGATTprofile
VOID SimpleProfile_RegisterAppCBs(&simpleBLEPeripheral_SimpleProfileCBs);//給應(yīng)用注冊(cè)回調(diào)函數(shù),這個(gè)函數(shù)非常重要 在回調(diào)函數(shù)中對(duì)數(shù)據(jù)做出處理。
static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs = {
simpleProfileChangeCB
// Charactersitic value change callback };
在SimpleProfile_RegisterAppCBs 函數(shù)中賦值。simpleProfile_AppCBs = simpleProfileChangeCB
在simpleProfileChangeCB函數(shù)中可以啟動(dòng)定時(shí)器來(lái)給主機(jī)發(fā)送Notification數(shù)據(jù)
發(fā)送數(shù)據(jù)函數(shù)為GATT_Notification(noti_cHandle,&pReport,FALSE);
如下:
{ static attHandleValueNoti_t pReport;//聲明attHandleValueNoti_t這個(gè)結(jié)構(gòu)體
uint16 noti_cHandle;//存放handle
pReport.handle = simpleProfileAttrTbl[11].handle;//讀取notification對(duì)應(yīng)的handle
GAPRole_GetParameter(0x30E, ¬i_cHandle);//獲取Connection Handle
pReport.len = 1;//數(shù)據(jù)長(zhǎng)度
pReport.value[0] = 0x03;//賦值
GATT_Notification(noti_cHandle,&pReport,FALSE);}
主機(jī)使能Notification:
這個(gè)handle應(yīng)該是相應(yīng)的characteristic value的handle的后面一個(gè), 就是characteristic value的handle加 1.例子: { attWriteReq_t writeReq;writeReq.handle =;writeReq.len = 2;writeReq.value[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
這里是 0x01 writeReq.value[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
這里是 0x00 writeReq.sig = 0;writeReq.cmd = 0;GATT_WriteCharValue(simpleBLEConnHandle, &writeReq, simpleBLETaskId);}
這兩個(gè)值目的是打開Notification功能.CCC的參數(shù)有兩個(gè), 一個(gè)Notification, 一個(gè)indication.value[0]就是打開關(guān)閉notification, value[1]是打開關(guān)閉indication.主機(jī)數(shù)據(jù)處理:
simpleBLECentral.c 這個(gè)文件, 里面有個(gè)函數(shù):simpleBLECentralProcessGATTMsg()這個(gè)函數(shù)就是處理各種從peripheral過(guò)來(lái)的數(shù)據(jù).但是在示例代碼中并沒(méi)有加入通知, 就是notification的接收, 所以你得自己添加代碼.很簡(jiǎn)單, 類似 if((pMsg->method == ATT_READ_RSP)||........), 添加 else if((pMsg->method == ATT_HANDLE_VALUE_NOTI)||......)就可以處理從機(jī)Notification的數(shù)據(jù)。
value 被寫的時(shí)候首先 simpleProfile_WriteAttrCB()會(huì)被調(diào)到.最后才會(huì)調(diào)用 simpleProfileChangeCB()
GAP通過(guò)在啟動(dòng)設(shè)備事件的任務(wù)處理中啟動(dòng)設(shè)備,其實(shí)主要是向GAP中注冊(cè)回調(diào)函數(shù),讓系統(tǒng)在發(fā)現(xiàn)自身運(yùn)行狀態(tài)變化時(shí),調(diào)用該函數(shù),方便應(yīng)用層進(jìn)行相關(guān)操作。if(events & SBP_START_DEVICE_EVT)
{
// Start the Device VOID GAPRole_StartDevice(&simpleBLEPeripheral_PeripheralCBs);//啟動(dòng)設(shè)備,注冊(cè)回調(diào)函數(shù),用于監(jiān)督設(shè)備的狀態(tài)變化:廣播、連接、配對(duì)、綁定等。
// Start Bond Manager VOID GAPBondMgr_Register(&simpleBLEPeripheral_BondMgrCBs);} simpleBLEPeripheral_PeripheralCBs函數(shù)定義如下
static gapRolesCBs_t simpleBLEPeripheral_PeripheralCBs = {
peripheralStateNotificationCB, // Profile State Change Callbacks
NULL
// When a valid RSSI is read from controller(not used by application)};static void peripheralStateNotificationCB(gaprole_States_t newState)//傳入?yún)?shù)由GPA自己輸入,內(nèi)部調(diào)用回調(diào)函數(shù)給用戶,處理連接狀態(tài)的改變 simpleBLEPeripheral_BondMgrCBs函數(shù)定義如下: static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs = {
ProcessPasscodeCB,// 生成配對(duì)密碼,發(fā)送給主機(jī)
ProcessPairStateCB
//主機(jī)密碼的校驗(yàn)處理。配對(duì)狀態(tài)管理 };
一、修改廣播功率 { #define LL_EXT_TX_POWER_MINUS_23_DBM
0 //-23dbm 功率 最小 #define LL_EXT_TX_POWER_MINUS_6_DBM
//-6dbm
#define LL_EXT_TX_POWER_0_DBM
// 0dbm
#define LL_EXT_TX_POWER_4_DBM // +dbm 功率 最大
HCI_EXT_SetTxPowerCmd(gTxPower);
更新廣播內(nèi)容
GAP_UpdateAdvertisingData(simpleBLEPeripheral_TaskID, TRUE, sizeof(advertData_Ex), advertData_Ex);}
二、數(shù)據(jù)加密解密 { uint8 key[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 需要加密的數(shù)據(jù)
uint8 plaintextData[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 加密后數(shù)據(jù)存放區(qū)
uint8 encryptedData[16];
// 解密后數(shù)據(jù)存放區(qū)
uint8 deccryptedData[16];
// 開始加密
LL_Encrypt(key, plaintextData, encryptedData);
// 開始解密
LL_EXT_Decrypt(key, encryptedData, deccryptedData);}
三、設(shè)置從機(jī)廣播時(shí)間 1)、廣播模式必須是Limited Discoverable mode 在advertData 中加入此AD string: 0x02, GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_LIMITED| GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED 2)、設(shè)置時(shí)間
//Maximum time to remain advertising, when in Limited Discoverable mode.unit is seconds #define USER_DEF_ADV_TIMEOUT
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, USER_DEF_ADV_TIMEOUT);
第三篇:Android藍(lán)牙開發(fā)小結(jié)
學(xué)習(xí)之前先了解兩個(gè)基本概念:
一、RFCOMM協(xié)議:
一個(gè)基于歐洲電信標(biāo)準(zhǔn)協(xié)會(huì)ETSI07.10規(guī)程的串行線性仿真協(xié)議。此協(xié)議提供RS232控制和狀態(tài)信號(hào),如基帶上的損壞,CTS以及數(shù)據(jù)信號(hào)等,為上層業(yè)務(wù)(如傳統(tǒng)的串行線纜應(yīng)用)提供了傳送能力。
RFCOMM是一個(gè)簡(jiǎn)單傳輸協(xié)議,其目的是針對(duì)如何在兩個(gè)不同設(shè)備上的應(yīng)用之間保證一條完整的通信路徑,并在它們之間保持一通信段。
RFCOMM是為了兼容傳統(tǒng)的串口應(yīng)用,同時(shí)取代有線的通信方式,藍(lán)牙協(xié)議棧需要提供與有線串口一致的通信接口而開發(fā)出的協(xié)議。RFCOMM協(xié)議提供對(duì)基于L2CAP協(xié)議的串口仿真,基于ETSI07.10。可支持在兩個(gè)BT設(shè)備之間同時(shí)保持高達(dá)60路的通信連接。
RFCOMM只針對(duì)直接互連設(shè)備之間的連接,或者是設(shè)備與網(wǎng)絡(luò)接入設(shè)備之間的互連。通信兩端設(shè)備必須兼容于RFCOMM協(xié)議,有兩類設(shè)備:DTE(Data Terminal Endpoint,通信終端,如PC,PRINTER)和DCE(Data Circuit Endpoint,通信段的一部分,如Modem)。此兩類設(shè)備不作區(qū)分。
二、MAC硬件地址
MAC(Medium/MediaAccess Control, 介質(zhì)訪問(wèn)控制)MAC地址是燒錄在NetworkInterfaceCard(網(wǎng)卡,NIC)里的.MAC地址,也叫硬件地址,是由48比特長(zhǎng)(6字節(jié)),16進(jìn)制的數(shù)字組成.0-23位叫做組織唯一標(biāo)志符(organizationally unique,是識(shí)別LAN(局域網(wǎng))節(jié)點(diǎn)的標(biāo)識(shí).24-47位是由廠家自己分配。其中第40位是組播地址標(biāo)志位。網(wǎng)卡的物理地址通常是由網(wǎng)卡生產(chǎn)廠家燒入網(wǎng)卡的EPROM(一種閃存芯片,通??梢酝ㄟ^(guò)程序擦寫),它存儲(chǔ)的是傳輸數(shù)據(jù)時(shí)真正賴以標(biāo)識(shí)發(fā)出數(shù)據(jù)的電腦和接收數(shù)據(jù)的主機(jī)的地址。
Android平臺(tái)提供的藍(lán)牙API去實(shí)現(xiàn)藍(lán)牙設(shè)備之間的通信,藍(lán)牙設(shè)備之間的通信主要包括了四個(gè)步驟:設(shè)置藍(lán)牙設(shè)備、尋找局域網(wǎng)內(nèi)可能或者匹配的設(shè)備、連接設(shè)備和設(shè)備之間的數(shù)據(jù)傳輸。以下是建立藍(lán)牙連接的所需要的一些基本類:
BluetoothAdapter類:代表了一個(gè)本地的藍(lán)牙適配器。它是所有藍(lán)牙交互的的入口點(diǎn)。利用它你可以發(fā)現(xiàn)其他藍(lán)牙設(shè)備,查詢綁定了的設(shè)備,使用已知的MAC地址實(shí)例化一個(gè)藍(lán)牙設(shè)備和建立一個(gè)BluetoothServerSocket(作為服務(wù)器端)來(lái)監(jiān)聽來(lái)自其他設(shè)備的連接。
BluetoothDevice類:代表了一個(gè)遠(yuǎn)端的藍(lán)牙設(shè)備,使用它請(qǐng)求遠(yuǎn)端藍(lán)牙設(shè)備連接或者獲取遠(yuǎn)端藍(lán)牙設(shè)備的名稱、地址、種類和綁定狀態(tài)。(其信息是封裝在bluetoothsocket中)。
Bluetoothsocket類:代表了一個(gè)藍(lán)牙套接字的接口(類似于tcp中的套接字),它是應(yīng)用程序通過(guò)輸入、輸出流與其他藍(lán)牙設(shè)備通信的連接點(diǎn)。
Blueboothserversocket類:代表打開服務(wù)連接來(lái)監(jiān)聽可能到來(lái)的連接請(qǐng)求(屬于server端),為了連接兩個(gè)藍(lán)牙設(shè)備必須有一個(gè)設(shè)備作為服務(wù)器打開一個(gè)服務(wù)套接字。當(dāng)遠(yuǎn)端設(shè)備發(fā)起連接連接請(qǐng)求的時(shí)候,并且已經(jīng)連接到了的時(shí)候,Blueboothserversocket類將會(huì)返回一個(gè)bluetoothsocket。
Bluetoothclass類:描述了一個(gè)藍(lán)牙設(shè)備的一般特點(diǎn)和能力。它的只讀屬性集定義了設(shè)備的主、次設(shè)備類和一些相關(guān)服務(wù)。然而,它并沒(méi)有準(zhǔn)確地描述所有該設(shè)備所支持的藍(lán)牙文件和服務(wù),而是作為對(duì)設(shè)備種類來(lái)說(shuō)的一個(gè)小小暗示。下面說(shuō)說(shuō)具體的編程實(shí)現(xiàn) 1.啟動(dòng)藍(lán)牙功能:
首先通過(guò)調(diào)用靜態(tài)方法getDefaultAdapter()獲取藍(lán)牙適配器BluetoothAdapter,以后你就可以使用該對(duì)象了。如果返回為空,the story is over。例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){ // Device does not support Bluetooth } 其次,調(diào)用isEnabled()來(lái)查詢當(dāng)前藍(lán)牙設(shè)備的狀態(tài),如果返回為false,則表示藍(lán)牙設(shè)備沒(méi)有開啟,接下來(lái)你需要封裝一個(gè)ACTION_REQUEST_ENABLE請(qǐng)求到intent里面,調(diào)用startActivityForResult()方法使能藍(lán)牙設(shè)備,例如:
if(!mBluetoothAdapter.isEnabled()){ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}
2.查找設(shè)備:
使用BluetoothAdapter類里的方法,你可以查找遠(yuǎn)端設(shè)備(大概十米以內(nèi))或者查詢?cè)谀闶謾C(jī)上已經(jīng)匹配(或者說(shuō)綁定)的其他手機(jī)了。當(dāng)然需要確定對(duì)方藍(lán)牙設(shè)備已經(jīng)開啟或者已經(jīng)開啟了“被發(fā)現(xiàn)使能”功能(對(duì)方設(shè)備是可以被發(fā)現(xiàn)的是你能夠發(fā)起連接的前提條件)。如果該設(shè)備是可以被發(fā)現(xiàn)的,會(huì)反饋回來(lái)一些對(duì)方的設(shè)備信息,比如名字、MAC地址等,利用這些信息,你的設(shè)備就可以選擇去向?qū)Ψ匠跏蓟粋€(gè)連接。
如果你是第一次與該設(shè)備連接,那么一個(gè)配對(duì)的請(qǐng)求就會(huì)自動(dòng)的顯示給用戶。當(dāng)設(shè)備配對(duì)好之后,他的一些基本信息(主要是名字和MAC)被保存下來(lái)并可以使用藍(lán)牙的API來(lái)讀取。使用已知的MAC地址就可以對(duì)遠(yuǎn)端的藍(lán)牙設(shè)備發(fā)起連接請(qǐng)求。
匹配好的設(shè)備和連接上的設(shè)備的不同點(diǎn):匹配好只是說(shuō)明對(duì)方設(shè)備發(fā)現(xiàn)了你的存在,并擁有一個(gè)共同的識(shí)別碼,并且可以連接。連接上:表示當(dāng)前設(shè)備共享一個(gè)RFCOMM信道并且兩者之間可以交換數(shù)據(jù)。也就是是說(shuō)藍(lán)牙設(shè)備在建立RFCOMM信道之前,必須是已經(jīng)配對(duì)好了的。
3.查詢匹配好的設(shè)備:
在建立連接之前你必須先查詢配對(duì)好了的藍(lán)牙設(shè)備集(你周圍的藍(lán)牙設(shè)備可能不止一個(gè)),以便你選取哪一個(gè)設(shè)備進(jìn)行通信,例如你可以你可以查詢所有配對(duì)的藍(lán)牙設(shè)備,并使用一個(gè)數(shù)組適配器將其打印顯示出來(lái):
Set
4.掃描設(shè)備:
掃描設(shè)備,只需要簡(jiǎn)單的調(diào)用startDiscovery()方法,這個(gè)掃描的過(guò)程大概持續(xù)是12秒,應(yīng)用程序?yàn)榱薃CTION_FOUND動(dòng)作需要注冊(cè)一個(gè)BroadcastReceiver來(lái)接受設(shè)備掃描到的信息。對(duì)于每一個(gè)設(shè)備,系統(tǒng)都會(huì)廣播ACTION_FOUND動(dòng)作。例如: // Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver(){ public void onReceive(Context context, Intent intent){ String action = intent.getAction();// When discovery finds a device if(BluetoothDevice.ACTION_FOUND.equals(action)){ // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// Add the name and address to an array adapter to show in a ListView mArrayAdapter.add(device.getName()+ “n” + device.getAddress());} } };// Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);// Don't forget to unregister during onDestroy 注意:掃描的過(guò)程是一個(gè)很耗費(fèi)資源的過(guò)程,一旦你找到你需要的設(shè)備之后,在發(fā)起連接請(qǐng)求之前,確保你的程序調(diào)用cancelDiscovery()方法停止掃描。顯然,如果你已經(jīng)連接上一個(gè)設(shè)備,啟動(dòng)掃描會(huì)減少你的通信帶寬。
5.使能被發(fā)現(xiàn):Enabling discoverability 如果你想使你的設(shè)備能夠被其他設(shè)備發(fā)現(xiàn),將ACTION_REQUEST_DISCOVERABLE動(dòng)作封裝在intent中并調(diào)用startActivityForResult(Intent, int)方法就可以了。他將在不使你應(yīng)用程序退出的情況下使你的設(shè)備能夠被發(fā)現(xiàn)。缺省情況下的使能時(shí)間是120秒,當(dāng)然你可以可以通過(guò)添加EXTRA_DISCOVERABLE_DURATION字段來(lái)改變使能時(shí)間(最大不超過(guò)300秒,這是出于對(duì)你設(shè)備上的信息安全考慮)。例如: Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);startActivity(discoverableIntent);運(yùn)行該段代碼之后,系統(tǒng)會(huì)彈出一個(gè)對(duì)話框來(lái)提示你啟動(dòng)設(shè)備使能被發(fā)現(xiàn)(此過(guò)程中如果你的藍(lán)牙功能沒(méi)有開啟,系統(tǒng)會(huì)幫你開啟),并且如果你準(zhǔn)備對(duì)該遠(yuǎn)端設(shè)備發(fā)現(xiàn)一個(gè)連接,你不需要開啟使能設(shè)備被發(fā)現(xiàn)功能,因?yàn)樵摴δ苤皇窃谀愕膽?yīng)用程序作為服務(wù)器端的時(shí)候才需要。
6.連接設(shè)備:
在應(yīng)用程序中,想建立兩個(gè)藍(lán)牙設(shè)備之間的連接,必須實(shí)現(xiàn)客戶端和服務(wù)器端的代碼(因?yàn)槿魏我粋€(gè)設(shè)備都必須可以作為服務(wù)端或者客戶端)。一個(gè)開啟服務(wù)來(lái)監(jiān)聽,一個(gè)發(fā)起連接請(qǐng)求(使用服務(wù)器端設(shè)備的MAC地址)。當(dāng)他們都擁有一個(gè)藍(lán)牙套接字在同一RFECOMM信道上的時(shí)候,可以認(rèn)為他們之間已經(jīng)連接上了。服務(wù)端和客戶端通過(guò)不同的方式或其他們的藍(lán)牙套接字。當(dāng)一個(gè)連接監(jiān)聽到的時(shí)候,服務(wù)端獲取到藍(lán)牙套接字。當(dāng)客戶可打開一個(gè)FRCOMM信道給服務(wù)器端的時(shí)候,客戶端獲取到藍(lán)牙套接字。
注意:在此過(guò)程中,如果兩個(gè)藍(lán)牙設(shè)備還沒(méi)有配對(duì)好的,android系統(tǒng)會(huì)通過(guò)一個(gè)通知或者對(duì)話框的形式來(lái)通知用戶。RFCOMM連接請(qǐng)求會(huì)在用戶選擇之前阻塞。如下圖:
7.服務(wù)端的連接:
當(dāng)你想要連接兩臺(tái)設(shè)備時(shí),一個(gè)必須作為服務(wù)端(通過(guò)持有一個(gè)打開的BluetoothServerSocket),目的是監(jiān)聽外來(lái)連接請(qǐng)求,當(dāng)監(jiān)聽到以后提供一個(gè)連接上的BluetoothSocket給客戶端,當(dāng)客戶端從BluetoothServerSocket得到BluetoothSocket以后就可以銷毀BluetoothServerSocket,除非你還想監(jiān)聽更多的連接請(qǐng)求。
建立服務(wù)套接字和監(jiān)聽連接的基本步驟:
首先通過(guò)調(diào)用listenUsingRfcommWithServiceRecord(String, UUID)方法來(lái)獲取BluetoothServerSocket對(duì)象,參數(shù)String代表了該服務(wù)的名稱,UUID代表了和客戶端連接的一個(gè)標(biāo)識(shí)(128位格式的字符串ID,相當(dāng)于PIN碼),UUID必須雙方匹配才可以建立連接。其次調(diào)用accept()方法來(lái)監(jiān)聽可能到來(lái)的連接請(qǐng)求,當(dāng)監(jiān)聽到以后,返回一個(gè)連接上的藍(lán)牙套接字BluetoothSocket。最后,在監(jiān)聽到一個(gè)連接以后,需要調(diào)用close()方法來(lái)關(guān)閉監(jiān)聽程序。(一般藍(lán)牙設(shè)備之間是點(diǎn)對(duì)點(diǎn)的傳輸)
注意:accept()方法不應(yīng)該放在主Acitvity里面,因?yàn)樗且环N阻塞調(diào)用(在沒(méi)有監(jiān)聽到連接請(qǐng)求之前程序就一直停在那里)。解決方法是新建一個(gè)線程來(lái)管理。例如: private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket;public AcceptThread(){ // Use a temporary object that is later assigned to mmServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp = null;try { // MY_UUID is the app's UUID string, also used by theclient code tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch(IOException e){ } mmServerSocket = tmp;} public void run(){ BluetoothSocket socket = null;// Keep listening until exception occurs or a socket is returned while(true){ try { socket = mmServerSocket.accept();} catch(IOException e){ break;} // If a connection was accepted if(socket!= null){ // Do work to manage the connection(in a separate thread)manageConnectedSocket(socket);mmServerSocket.close();break;} } } /** Will cancel the listening socket, and cause the thread to finish */ public void cancel(){ try { mmServerSocket.close();} catch(IOException e){ } } }
8.客戶端的連接:
為了初始化一個(gè)與遠(yuǎn)端設(shè)備的連接,需要先獲取代表該設(shè)備的一個(gè)BluetoothDevice對(duì)象。通過(guò)BluetoothDevice對(duì)象來(lái)獲取BluetoothSocket并初始化連接,具體步驟:
使用BluetoothDevice對(duì)象里的方法createRfcommSocketToServiceRecord(UUID)來(lái)獲取BluetoothSocket。UUID就是匹配碼。然后,調(diào)用connect()方法來(lái)。如果遠(yuǎn)端設(shè)備接收了該連接,他們將在通信過(guò)程中共享RFFCOMM信道,并且connect()方法返回。例如: private class ConnectThread extends Thread { private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device){ // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null;mmDevice = device;// Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch(IOException e){ } mmSocket = tmp;}
public void run(){ // Cancel discovery because it will slow down the connection mAdapter.cancelDiscovery();try { // Connect the device through the socket.This will block // until it succeeds or throws an exception mmSocket.connect();} catch(IOException connectException){ // Unable to connect;close the socket and get out try { mmSocket.close();} catch(IOException closeException){ } return;} // Do work to manage the connection(in a separate thread)manageConnectedSocket(mmSocket);}
注意:conncet()方法也是阻塞調(diào)用,一般建立一個(gè)獨(dú)立的線程中來(lái)調(diào)用該方法。在設(shè)備discover過(guò)程中不應(yīng)該發(fā)起連接connect(),這樣會(huì)明顯減慢速度以至于連接失敗。且數(shù)據(jù)傳輸完成只有調(diào)用close()方法來(lái)關(guān)閉連接,這樣可以節(jié)省系統(tǒng)內(nèi)部資源。
9.管理連接(主要涉及數(shù)據(jù)的傳輸):
當(dāng)設(shè)備連接上以后,每個(gè)設(shè)備都擁有各自的BluetoothSocket?,F(xiàn)在你就可以實(shí)現(xiàn)設(shè)備之間數(shù)據(jù)的共享了。
1> 首先通過(guò)調(diào)用getInputStream()和getOutputStream()方法來(lái)獲取輸入輸出流。然后通過(guò)調(diào)用read(byte[])和write(byte[]).方法來(lái)讀取或者寫數(shù)據(jù)。
2> 實(shí)現(xiàn)細(xì)節(jié):以為讀取和寫操作都是阻塞調(diào)用,需要建立一個(gè)專用現(xiàn)成來(lái)管理。3>
private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket){ mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;// Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch(IOException e){ } mmInStream = tmpIn;mmOutStream = tmpOut;} public void run(){ byte[] buffer = new byte[1024];// buffer store for the stream int bytes;// bytes returned from read()// Keep listening to the InputStream until an exception occurs while(true){ try { // Read from the InputStream bytes = mmInStream.read(buffer);// Send the obtained bytes to the UI Activity mHandler.obtainMessage(MESSAGE_READ, bytes,-1, buffer).sendToTarget();} catch(IOException e){ break;} } } /* Call this from the main Activity to send data to the remote device */ public void write(byte[] bytes){ try { mmOutStream.write(bytes);} catch(IOException e){ } } /* Call this from the main Activity to shutdown the connection */ public void cancel(){ try { mmSocket.close();} catch(IOException e){ } } }
第四篇:sqlldr使用小結(jié)
sqlldr使用小結(jié)
sqlldr userid=lgone/tiger control=a.ctl
LOAD DATA
INFILE ’t.dat’ // 要導(dǎo)入的文件
// INFILE ’tt.date’ // 導(dǎo)入多個(gè)文件
// INFILE * // 要導(dǎo)入的內(nèi)容就在control文件里 下面的BEGINDATA后面就是導(dǎo)入的內(nèi)容
INTO TABLE table_name // 指定裝入的表
BADFILE ’c:\bad.txt’ // 指定壞文件地址
************* 以下是4種裝入表的方式
APPEND // 原先的表有數(shù)據(jù) 就加在后面
// INSERT // 裝載空表 如果原先的表有數(shù)據(jù) sqlloader會(huì)停止 默認(rèn)值
// REPLACE // 原先的表有數(shù)據(jù) 原先的數(shù)據(jù)會(huì)全部刪除
// TRUNCATE // 指定的內(nèi)容和replace的相同 會(huì)用truncate語(yǔ)句刪除現(xiàn)存數(shù)據(jù)
************* 指定的TERMINATED可以在表的開頭 也可在表的內(nèi)部字段部分
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’“’
// 裝載這種數(shù)據(jù): 10,lg,”“"lg”“",”lg,lg“
// 在表中結(jié)果: 10 lg ”lg“ lg,lg
// TERMINATED BY X ’0Array’ // 以十六進(jìn)制格式 ’0Array’ 表示的// TERMINATED BY WRITESPACE // 裝載這種數(shù)據(jù): 10 lg lg
TRAILING NULLCOLS ************* 表的字段沒(méi)有對(duì)應(yīng)的值時(shí)允許為空
************* 下面是表的字段
(col_1 , col_2 ,col_filler FILLER // FILLER 關(guān)鍵字 此列的數(shù)值不會(huì)被裝載
// 如: lg,lg,not 結(jié)果 lg lg)
// 當(dāng)沒(méi)聲明FIELDS TERMINATED BY ’,’ 時(shí)
//(// col_1 [interger external] TERMINATED BY ’,’ ,// col_2 [date ”dd-mon-yyy“] TERMINATED BY ’,’ , // col_3 [char] TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’lg’
//)
// 當(dāng)沒(méi)聲明FIELDS TERMINATED BY ’,’用位置告訴字段裝載數(shù)據(jù)
//(// col_1 position(1:2),// col_2 position(3:10),// col_3 position(*:16), // 這個(gè)字段的開始位置在前一字段的結(jié)束位置
// col_4 position(1:16),// col_5 position(3:10)char(8)// 指定字段的類型
//)
BEGINDATA // 對(duì)應(yīng)開始的 INFILE * 要導(dǎo)入的內(nèi)容就在control文件里
10,Sql,what
20,lg,show
=======================================
//////////// 注意begindata后的數(shù)值前面不能有空格***** 普通裝載
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’”’
(DEPTNO,DNAME,LOC)
BEGINDATA
10,Sales,“"”USA“"”
20,Accounting,“Virginia,USA”
30,Consulting,Virginia
40,Finance,Virginia
50,“Finance”,“",Virginia // loc 列將為空
60,”Finance“,Virginia // loc 列將為空***** FIELDS TERMINATED BY WHITESPACE 和 FIELDS TERMINATED BY x’0Array’ 的情況
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY WHITESPACE
--FIELDS TERMINATED BY x’0Array’
(DEPTNO,DNAME,LOC)
BEGINDATA Sales Virginia ***** 指定不裝載那一列
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’”’
(DEPTNO,F(xiàn)ILLER_1 FILLER, // 下面的 “Something Not To Be Loaded” 將不會(huì)被裝載
DNAME,LOC)
BEGINDATA
20,Something Not To Be Loaded,Accounting,“Virginia,USA” ***** position的列子
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
(DEPTNO position(1:2),DNAME position(*:16), // 這個(gè)字段的開始位置在前一字段的結(jié)束位置
LOC position(*:2Array),ENTIRE_LINE position(1:2Array))
BEGINDATA
10Accounting Virginia,USA
***** 使用函數(shù) 日期的一種表達(dá) TRAILING NULLCOLS的使用
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS // 其實(shí)下面的ENTIRE_LINE在BEGINDATA后面的數(shù)據(jù)中是沒(méi)有直接對(duì)應(yīng)
// 的列的值的 如果第一行改為
10,Sales,Virginia,1/5/2000, 就不用TRAILING NULLCOLS了
(DEPTNO,DNAME “upper(:dname)”, // 使用函數(shù)
LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’, // 日期的一種表達(dá)方式 還有’dd-mon-yyyy’ 等
ENTIRE_LINE “:deptno||:dname||:loc||:last_updated”)
BEGINDATA
10,Sales,Virginia,1/5/2000
20,Accounting,Virginia,21/6/1ArrayArrayArray 30,Consulting,Virginia,5/1/2000
40,Finance,Virginia,15/3/2001 ***** 使用自定義的函數(shù) // 解決的時(shí)間問(wèn)題
create or replace
function my_to_date(p_string in varchar2)return date
as
type fmtArray is table of varchar2(25);
l_fmts fmtArray := fmtArray(’dd-mon-yyyy’, ’dd-month-yyyy’,’dd/mm/yyyy’,’dd/mm/yyyy hh24:mi:ss’);
l_return date;
begin
for i in 1..l_fmts.count
loop
begin
l_return := to_date(p_string, l_fmts(i));
exception
when others then null;
end;EXIT when l_return is not null;
end loop;
if(l_return is null)
then
l_return :=
new_time(to_date(’01011Array70’,’ddmmyyyy’)+ 1/24/60/60 *
p_string, ’GMT’, ’EST’);
end if;
return l_return;
end;
/
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO, DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)” // 使用自定義的函數(shù))
BEGINDATA
10,Sales,Virginia,01-april-2001
20,Accounting,Virginia,13/04/2001
30,Consulting,Virginia,14/04/2001 12:02:02
40,Finance,Virginia,Array872682Array7
50,Finance,Virginia,02-apr-2001
60,Finance,Virginia,Not a date ***** 合并多行記錄為一行記錄
LOAD DATA
INFILE *
concatenate 3 // 通過(guò)關(guān)鍵字concatenate 把幾行的記錄看成一行記錄
INTO TABLE DEPT
replace
FIELDS TERMINATED BY ’,’
(DEPTNO, DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’)
BEGINDATA
10,Sales, // 其實(shí)這3行看成一行 10,Sales,Virginia,1/5/2000
Virginia,1/5/2000
// 這列子用 continueif list=“,” 也可以
告訴sqlldr在每行的末尾找逗號(hào) 找到逗號(hào)就把下一行附加到上一行
LOAD DATA
INFILE *
continueif this(1:1)= ’-’ // 找每行的開始是否有連接字符-有就把下一行連接為一行
// 如-10,Sales,Virginia,// 1/5/2000 就是一行 10,Sales,Virginia,1/5/2000
// 其中1:1 表示從第一行開始 并在第一行結(jié)束 還有continueif next 但continueif list最理想
INTO TABLE DEPT replace
FIELDS TERMINATED BY ’,’
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED date ’dd/mm/yyyy’)
BEGINDATA // 但是好象不能象右面的那樣使用
-10,Sales,Virginia,-10,Sales,Virginia,1/5/2000 1/5/2000
-40, 40,Finance,Virginia,13/04/2001
Finance,Virginia,13/04/2001 ***** 載入每行的行號(hào)
load data
infile *
into table t
replace
(seqno RECNUM //載入每行的行號(hào)
text Position(1:1024))
BEGINDATA fsdfasj //自動(dòng)分配一行號(hào)給載入 表t 的seqno字段 此行為 1
fasdjfasdfl // 此行為 2...Array ***** 載入有換行符的數(shù)據(jù)
注意: unix 和 windows 不同 \\n & /n
< 1 > 使用一個(gè)非換行符的字符
LOAD DATA
INFILE *
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)”,COMMENTS “replace(:comments,’\n’,chr(10))” // replace 的使用幫助轉(zhuǎn)換換行符)
BEGINDATA
10,Sales,Virginia,01-april-2001,This is the Sales\nOffice in Virginia
20,Accounting,Virginia,13/04/2001,This is the Accounting\nOffice in Virginia
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting\nOffice in Virginia
40,Finance,Virginia,Array872682Array7,This is the Finance\nOffice in Virginia
< 2 > 使用fix屬性
LOAD DATA
INFILE demo17.dat “fix 101”
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME “upper(:dname)”,LOC “upper(:loc)”,LAST_UPDATED “my_to_date(:last_updated)”,COMMENTS)
demo17.dat 10,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia
20,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia
40,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia
// 這樣裝載會(huì)把換行符裝入數(shù)據(jù)庫(kù) 下面的方法就不會(huì) 但要求數(shù)據(jù)的格式不同
LOAD DATA
INFILE demo18.dat “fix 101”
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’ OPTIONALLY ENCLOSED BY ’“’
TRAILING NULLCOLS
(DEPTNO, DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)“,COMMENTS)
demo18.dat
10,Sales,Virginia,01-april-2001,”This is the Sales
Office in Virginia“
20,Accounting,Virginia,13/04/2001,”This is the Accounting
Office in Virginia“
30,Consulting,Virginia,14/04/2001 12:02:02,”This is the Consulting
Office in Virginia“
40,Finance,Virginia,Array872682Array7,”This is the Finance
Office in Virginia“
< 3 > 使用var屬性
LOAD DATA
INFILE demo1Array.dat ”var 3“
// 3 告訴每個(gè)記錄的前3個(gè)字節(jié)表示記錄的長(zhǎng)度 如第一個(gè)記錄的 071 表示此記錄有 71 個(gè)字節(jié)
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)“,COMMENTS)
demo1Array.dat
07110,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia
07820,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia
08730,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia
07140,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia
< 4 > 使用str屬性
// 最靈活的一中 可定義一個(gè)新的行結(jié)尾符 win 回車換行 : chr(13)||chr(10)
此列中記錄是以 a|\r\n 結(jié)束的select utl_raw.cast_to_raw(’|’||chr(13)||chr(10))from dual;
結(jié)果 7C0D0A
LOAD DATA
INFILE demo20.dat ”str X’7C0D0A’“
INTO TABLE DEPT
REPLACE
FIELDS TERMINATED BY ’,’
TRAILING NULLCOLS
(DEPTNO,DNAME ”upper(:dname)“,LOC ”upper(:loc)“,LAST_UPDATED ”my_to_date(:last_updated)",COMMENTS)
demo20.dat 10,Sales,Virginia,01-april-2001,This is the Sales
Office in Virginia|
20,Accounting,Virginia,13/04/2001,This is the Accounting
Office in Virginia|
30,Consulting,Virginia,14/04/2001 12:02:02,This is the Consulting
Office in Virginia|
40,Finance,Virginia,Array872682Array7,This is the Finance
Office in Virginia|
================================
象這樣的數(shù)據(jù) 用 nullif 子句
10-jan-200002350Flipper seemed unusually hungry today.10510-jan-20000ArrayArray45Spread over three meals.id position(1:3)nullif id=blanks // 這里可以是blanks 或者別的表達(dá)式
// 下面是另一個(gè)列子 第一行的 1 在數(shù)據(jù)庫(kù)中將成為 null
LOAD DATA INFILE *
INTO TABLE T
REPLACE
(n position(1:2)integer external nullif n=’1’,v position(3:8))
BEGINDATA 10
20lg
-----------------------------
第五篇:GridCtrl使用小結(jié)
http://004km.cn/
GridCtrl使用詳解
CGridCtrl類主要是通過(guò)grid樣式顯示數(shù)據(jù) 在單文檔中的使用方法
步驟一 初始化 在CView類的.h頭文件中包含文件:
#include “Gridctrl.h” 并且手寫加入如下的成員函數(shù):
CGridCtrl * m_pGridCtrl;步驟二 構(gòu)造與析構(gòu) 構(gòu)造函數(shù)中:
m_pGridCtrl = NULL;析構(gòu)函數(shù)中:
if(m_pGridCtrl)
delete m_pGridCtrl;步驟三 如果需要打印功能的話添加同名打印函數(shù)代碼 在CView類的OnBeginPrinting()函數(shù)中添加如下代碼: if(m_pGridCtrl)
m_pGridCtrl->OnBeginPrinting(pDC,pInfo);//簡(jiǎn)單吧,這就是類的好處其它兩個(gè)打印函數(shù)也一樣的做法.步驟四 在OnInitaUpdate()函數(shù)中或者你自己添加的要顯示Grid的消息函數(shù)中如下初始化: //創(chuàng)建非模式對(duì)話框 CDlg *dlg;dlg=new CDlg();dlg->Create(IDD_Dlg,this);
//初始化GridCtrl控件 if(m_pGridCtrl!=NULL){ deletem_pGridCtrl;m_pGridCtrl=NULL;} if(m_pGridCtrl == NULL){ // Create the Gridctrl object m_pGridCtrl = new CGridCtrl;if(!m_pGridCtrl)return 0;// Create the Gridctrl window CRectrect;GetClientRect(rect);m_pGridCtrl->Create(rect, this, 100);// fill it up with stuff m_pGridCtrl->SetEditable(false);m_pGridCtrl->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黃色背景 m_pGridCtrl->EnableDragAndDrop(false);try { m_pGridCtrl->SetRowCount(k);//設(shè)置行數(shù)為k行 m_pGridCtrl->SetColumnCount(4);//k列
m_pGridCtrl->SetFixedRowCount(1);//標(biāo)題行為一行
http://004km.cn/
m_pGridCtrl->SetFixedColumnCount(1);//同上 } catch(CMemoryException* e){ e->ReportError();e->Delete();return 0;} //填充列標(biāo)題 int row=0;for(int col=0;col<4;col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(col==0){ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.strText.Format(_T(“【類別】”),col);} else if(col==1){ Item.nFormat = DT_LEFT|DT_WORDBREAK;Item.strText.Format(_T(“第一列”),col);} else if(col==2){ Item.nFormat = DT_LEFT|DT_WORDBREAK;Item.strText.Format(_T(“第二列”),col);} m_pGridCtrl->SetItem(&Item);} // fill rows/cols with text for(row = 1;row < k;row++)for(col = 0;col < h;col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(col < 1){ //行標(biāo)題頭
Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;Item.strText.Format(_T(“%d”),row);
http://004km.cn/
} else if(col==1){ //第一列的值 Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;str=“aa”;Item.strText.Format(_T(“%s”),str);}else if(col==2){ //第二列第值 Item.nFormat = DT_CENTER|DT_VCENTER |DT_SINGLELINE|DT_END_ELLIPSIS |DT_NOPREFIX;CStringstr;str=“bb”;Item.strText.Format(_T(“%s”),str);} m_pGridCtrl->SetItem(&Item);} m_pGridCtrl->AutoSize();
//--------------設(shè)置行列距------------------for(int a=1;a
步驟五: 添加WM_SIZE消息,調(diào)整控件的界面占屏幕大小
if(m_pGridCtrl->GetSafeHWnd())
{
CRectrect;
GetClientRect(rect);
m_pGridCtrl->MoveWindow(rect);
}
在對(duì)話框中的使用方法 步驟一 創(chuàng)建數(shù)據(jù)顯示表格對(duì)話框
在資源管理器中新創(chuàng)建一個(gè)對(duì)話框,假設(shè)為CDlgTestReportBox。從工具箱中加入Custom Control,就是人頭像的那個(gè),將其區(qū)域拉伸至要顯示數(shù)據(jù)表格的大小,充滿整個(gè)對(duì)話框。
在CDlgTestReportBox類的頭文件中: #include “GridCtrl.h”
http://004km.cn/
再定義成員變量: CGridCtrl* m_pGrid;添加OnShowWindow()消息處理函數(shù)如下:
voidCDlgTestReportBox::OnShowWindow(BOOL bShow, UINT nStatus){ CDialog::OnShowWindow(bShow, nStatus);// TODO: Add your message handler code here if(m_pGrid!=NULL){ deletem_pGrid;m_pGrid=NULL;} if(m_pGrid==NULL){ m_pGrid=new CGridCtrl;CRectrect;GetDlgItem(IDC_ReportAera)->GetWindowRect(rect);//得到顯示區(qū)域 ScreenToClient(&rect);m_pGrid->Create(rect,this,100);m_pGrid->SetEditable(false);m_pGrid->SetTextBkColor(RGB(0xFF, 0xFF, 0xE0));//黃色背景 try { m_pGrid->SetRowCount(10);//初始為10行
m_pGrid->SetColumnCount(11);//初始化為11列 m_pGrid->SetFixedRowCount(1);//表頭為一行 m_pGrid->SetFixedColumnCount(1);//表頭為一列 } catch(CMemoryException* e){ e->ReportError();e->Delete();// return FALSE;} for(int row = 0;row
http://004km.cn/
{ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(“報(bào)表顯示”),col);} else if(row < 1)//設(shè)置0行表頭顯示 { Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(“ 項(xiàng)目%d”),col);} else if(col < 1)//設(shè)置0列表頭顯示 { if(row
步驟二 嵌入上面的對(duì)話框 顯示數(shù)據(jù)
在你需要顯示數(shù)據(jù)的對(duì)話框上的頭文件中,假設(shè)為CDlgTest,加入 #include ”GridCtrl.h“ CDlgTestReportBox* m_pTestReportBox;將數(shù)據(jù)顯示對(duì)話框放入你的對(duì)話框相應(yīng)位置上,在CDlgTest::OnInitDialog()中:
if(!m_pTestReportBox){
m_pTestReportBox=new CDlgTestReportBox(this);} m_pTestReportBox->Create(IDD_DlgTestReportBox,this);
http://004km.cn/
//定義區(qū)域變量 CRectrectDraw;GetDlgItem(IDC_AeraReport)->GetWindowRect(rectDraw);ScreenToClient(&rectDraw);//動(dòng)態(tài)測(cè)試數(shù)據(jù)顯示區(qū)域rectDraw //將對(duì)應(yīng)的對(duì)話框放到指定區(qū)域 m_pTestReportBox->MoveWindow(rectDraw);m_pTestReportBox->ShowWindow(SW_SHOW);自定義填充數(shù)據(jù)的函數(shù):CDlgTest::FillGrid()如下: CGridCtrl* pGrid=m_pTestReportBox->m_pGrid;for(int row = pGrid->GetRowCount()-1;row >= pGrid->GetRowCount()-3;row--){ for(int col = 1;col <= pGrid->GetColumnCount();col++){ GV_ITEM Item;Item.mask = GVIF_TEXT|GVIF_FORMAT;Item.row = row;Item.col = col;if(row==pGrid->GetRowCount()-3&&col>0)//平均值 { if(col==10){ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(” %6.2f “),avjch);} else{ Item.nFormat = DT_CENTER|DT_WORDBREAK;Item.szText.Format(_T(” %6.2f “),av[col-1]);} } pGrid->SetItem(&Item);//提交數(shù)據(jù) if(row==0||col==0){ COLORREF clr = RGB(0, 0, 0);pGrid->SetItemBkColour(row, col, clr);pGrid->SetItemFgColour(row, col, RGB(255,0,0));} }//循環(huán)結(jié)束
pGrid->Invalidate();} CGRIFCTRL原理:
DBGRID和一般的GRID的不同之處在于,一般的GRID并不適合顯示大的數(shù)據(jù)量,如果一個(gè)表中有上萬(wàn)條記錄都要插入到GRID中,這將是一個(gè)很慢的過(guò)程,并且在GRID中移動(dòng)滾動(dòng)條時(shí),它的記錄的滾動(dòng)也是很慢。而DBGRID并不會(huì)真正把這些記錄的數(shù)據(jù)全部插入到控件中,當(dāng)DBGRID的滾動(dòng)條滾動(dòng)時(shí),它會(huì)根據(jù)DBGRID的顯示面積的大小和查詢得到的總記錄數(shù)計(jì)算出當(dāng)前應(yīng)該顯示哪些行,然后插入
http://004km.cn/
到表格中,這樣一來(lái),速度肯定快,而且沒(méi)有數(shù)據(jù)量多少的限制。幸運(yùn)的是,CGridCtrl類已經(jīng)為我們提供了這種機(jī)制,它是采用虛模式實(shí)現(xiàn)的。使用這種方式,即使你向這個(gè)該控件插入一百萬(wàn)條數(shù)據(jù),它并不會(huì)真的生成一百萬(wàn)行,而是隨著你的滾動(dòng)條的滾動(dòng),計(jì)算出在屏幕上要顯示的行和列,然后會(huì)向你提供一個(gè)接口,通過(guò)這個(gè)接口,你可以在這兒設(shè)置你要顯示的數(shù)據(jù)。下面給出使用CGridCtrl控件的虛模式的步驟: 步驟一 初始化
在視圖的初始化函數(shù)里添加如下代碼:
void SetVirtualMode(TRUE)
設(shè)為虛模式
BOOL SetRowCount(intnRows)
設(shè)置總的行數(shù)。
BOOL SetFixedRowCount(intnFixedRows = 1)
設(shè)置固定的行數(shù)據(jù) BOOL SetColumnCount(intnCols)
設(shè)置列數(shù) BOOL SetFixedColumnCount(intnFixedCols = 1)設(shè)置固定的列數(shù) 步驟二 響應(yīng)消息 顯示數(shù)據(jù)
我們假設(shè)CGridCtrl是放在單文檔視圖中,而且它關(guān)聯(lián)的變量是m_GridCtrl,利用ClassWizard添加視圖的OnNotify響應(yīng)函數(shù)。這個(gè)響應(yīng)函數(shù)的寫法是固定的,類似下面的代碼:
BOOL CGridCtrlTestView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult){ if(wParam ==(WPARAM)m_Grid.GetDlgCtrlID()){ *pResult = 1;GV_DISPINFO *pDispInfo =(GV_DISPINFO*)lParam;if(GVN_GETDISPINFO == pDispInfo->hdr.code){ //這是添加的函數(shù),在這個(gè)函數(shù)里設(shè)置當(dāng)前要顯示的數(shù)據(jù) SetGridItem(pDispInfo);return TRUE;} } returnCGridCtrlTestView::OnNotify(wParam, lParam, pResult);} 在上面的代碼中,SetGridItem(pDispInfo)是添加的函數(shù),在這個(gè)函數(shù)里我們?cè)O(shè)置當(dāng)前要顯示的數(shù)據(jù)。pDispInfo是一個(gè)GV_DISPINFO的結(jié)構(gòu)體對(duì)象,它包含了每個(gè)單元格的信息,如行號(hào),列號(hào),有沒(méi)有位圖,背景色,前景色等。CGRIDCTRL會(huì)把當(dāng)前要顯示那個(gè)單元格行號(hào),列號(hào)傳遞給我們,我們只要設(shè)置里面顯示的數(shù)據(jù)就可以了。如下面是一個(gè)顯示數(shù)據(jù)的例子。
voidCGridCtrlTestView::SetGridItem(GV_DISPINFO *pDispInfo){
pDispInfo->item.strText.Format(”row%d,col%d",pDispInfo->it
http://004km.cn/
em.row, pDispInfo->item.col);}