px4原始碼學習六--uORB模組研究
UORB模組研讀
uORB函式解析:
uORB模組(Micro Object Request Broker,微物件請求代理器)
uORB是Pixhawk系統中關鍵的一個模組,肩負了資料傳輸任務。所有感測器,資料傳輸任務,GPS,PPM訊號從晶片獲取後通過uORB進行傳輸,到各個模組計算處理。(可以理解為資料中心倉庫) uORB是跨程序的IPC通訊模組。實際多個程序開啟同一裝置檔案,通過此檔案節點進行資料互動和共享。 程序間通過命名(匯流排)交換訊息成為topic 一個topic包含一種訊息型別/資料型別。 每個程序可以訂閱/釋出topic,一個程序可以訂閱多個主題,但一條匯流排始終只能有一條訊息。
公告topic
extern int orb_advertise(const struct orb_metadata *meta,const void *data)
meta:uORB元物件,可以看topic的ID,通過ORB_ID賦值。
data:指向一個被初始化,釋出者要釋出的資料儲存變數指標。
釋出更新
extern int orb_publish(const struct orb_metadata *meta, int handle, const void *data)
handle:orb_advertise函式返回的控制代碼
data:指向釋出資料的製作
訂閱topic
要求的滿足條件:
- 呼叫ORB_DEFINE()或ORB_DEFINE_OPTIONAL()巨集(在訂閱標頭檔案中包含他們)
- 釋出到主題的資料結構定義
extern int orb_subscribe(const struct orb_metadata *meta)
取消訂閱
extern int orb_unsubscribe(int handle)
拷貝資料
訂閱者不能引用從ORB中儲存資料或其他訂閱共享的資料,只能拷貝到臨界緩衝區
extern int orb_copy(const struct orb_metadata *meta, int handle, void *buffer)
檢查更新
extern int orb_check(int handle, bool *updataed)
釋出時間戳
extern int orb_stat(int handle,uint64_t *time)
Note:
- 必須要先orb_advertise()+orb_subscribe()後,才能使用orb_copy()
- 所有主題在Firmware/Build_px4fmu-v2_default/Src_Modules/uORB/Topics
- 也可以到Firmware/Msg下找到
uORB原理解析:
實現方式:
- 首先模組入口是uORBMain.cpp,這裡會建立唯一的MasterNode例項,負責內部的裝置節點的控制
- MasterNode簡潔:繼承自CDev,是一個字元裝置,linux中裝置都是檔案,所以會在對應路徑下生成檔案,主要對Cdev的ioctl函式進行多型實現,類內部維護一個Nodemap的資料結構(後面有詳細簡紹),所有的DeviceNode都會加入其中管理。
- 當外部呼叫uORB的方法時候,實際間接呼叫了uORB::Manger類的方法,也可以理解做Manger是外部函式的管理入口,MasterNode是內部節點的管理器。
- 外部函式呼叫uORB時候都會通過uORB::Manger::get_instance()這個函式獲取Manger的例項指標。這個函式是靜態函式,根據判斷名稱空間中_Instance指標是否為空,確保全nameespace中只有這麼一個Manger例項。
- 實際節點,DeviceNode,繼承自CDev,每一個字元裝置都會生成對應的檔案。這個類過載了open,close,read,wirte,ioctl,還有其他一些函式。
- 當外部呼叫orb_advertise時,Manger會做如下的事情:
- 以釋出者的身份呼叫node_open函式,最後返回對應檔案的檔案描述符fd。先確認一下是否這個主題對應例項的序號已經被髮布過,如果是,會重新發布一下,否則(對應檔案不存在,也就是這個主題對應ID的DeviceNode不存在),會呼叫node_advertise函式進行DeviceNode例項的建立。
- (呼叫node_open函式)
- 通過fd呼叫ioctl函式,返回DeiceNode物件的指標。
- 釋出初始化的資料
- 當外部呼叫orb_publish時
- 根據傳入的DeviceNode的指標,呼叫這個類的靜態的釋出資料的方法。就是將資料寫入DeviceNode物件的屬性中。
- 當外部呼叫orb_Subsctibe時,
- 使用Manger類的node_open函式,返回對應主題對應例項的檔案描述符,其他資訊都可以通過呼叫被DeviceNode多型實現過的ioctl函式獲得。
- 取消訂閱就將檔案描述符關閉就可以了。
uORB
這個檔案中所有的方法的實現方式都是呼叫ORB::Manger的方法,只是一個擴充套件介面的包裝檔案
因為這個沒有在任何名稱空間或者類中,這裡將具體的ORB::Manger操作封裝,使得其他類可以呼叫
orb_advertise(*meta, *data) orb_advert_t
引數列表含義:主題名稱,初始資料
作為釋出者做廣播,如果正常,返回一個可以用來發布這個主題的控制代碼
orb_advertise(*meta, *data, *instance, priority) orb_advert_t
引數列表含義:主題名稱,初始資料,例項ID的指標,例項優先順序
一個主題下可以有多個例項,每個例項根據handle區分,最多5個
orb_pubshlist_auto(*meta, *handle, *data, *instance, priority) int
引數列表含義:同上
根據handle是否有效釋出資料
orb_publish(*meta, handle, *data) int
引數列表含義:主題名稱,主題的例項控制代碼,釋出的資料
資料釋出是原子操作,任何等待更新的訂閱者都會被通知。其他沒有等待的可以使用orb_check/orb_stat函式檢查更新
orb_subscribe(*meta) int
引數列表含義: 主題名稱
返回檔案描述符,可以使用poll函式等待主題更新,就像使用topic_read/orb_check/orb_stat一樣
即使這個主題沒有釋出訂閱也可能成功,這種情況下,poll,複製它的值什麼的操作都會失敗,直到這個主題被髮布未知。
如果系統不知道這樣一個主題,那麼會訂閱失敗
orb_subscribe_muti(*meta, instance) int
引數列表含義: 主題名稱, 例項ID
orb_unsubscribe(handle) int
引數列表含義:例項控制代碼
orb_copy(*meta, handle, *buffer) int
引數列表含義:主題名稱,例項控制代碼,緩衝區
這是唯一個可以重置內部主題是否更新標誌的函式。一旦使用poll或者orb_check返回可用更新,必須使用這個方法更新引數
orb_check(handle, *updated) int
引數列表含義:例項的控制代碼,是否被更新的標誌
檢查在上次orb_copy後這個主題是否被重新發布過
更新以每個控制代碼為基礎進行跟蹤;
這個呼叫將一直返回true直到使用相同的控制代碼呼叫orb_copy。
由於stat和copy之間的競爭視窗可能導致錯過更新,所以此介面應優先於呼叫orb_stat。
orb_stat(handle, *time) int
返回上一次更新主題的時間
orb_exists(*meta, instance) int
檢查主題是否存在
orb_group_count(*meta) int
返回主題的例項數量
orb_priority(handle, *priority) int
返回控制代碼的優先順序
orb_set_interval(handle, interval) int
設定最小的更新間隙
uORBCommon
ORBSet
- insert(*node_node) void
- find(*node_name) bool
- erase(*node_name) bool
- unlinkNext(Node*) void
實現方式與ORBMap一毛一樣,唯一的區別就是這個類維護的佇列中的node節點是隻有節點名字,沒有節點裝置。
ORBMap
- insert(*node_name, *node) void
- find(*node_name) bool
- *get(*node) uORB::DeviceNode
- unlinkNext(*a) void
類的實現流程:(太簡單了吧,過分)
類中維護一個佇列,使用物件top,end倆個指標標識佇列,
當有新的資料結點,加入到end後面,查詢、獲取是從頭到尾順序查詢佇列,使用strcmp()比較node_name
uORBDevices_nuttx
namespace uORB
{
class DeviceNode; //具體的節點
class DeviceMaster; //節點管理
}
DeviceNode
繼承device::CDev(一切字元裝置的基類)
DeviceNode(*meta, *name, *path, priority)
open(struct file *filp) int virtual
裝置節點開啟:算是虛擬的裝置檔案雖然繼承與CDev類
根據檔案資訊,讀取許可權:(filp記錄了這次開啟檔案的資訊,比如許可權,PID等資料)
如果檔案寫入許可權,認為是釋出者,裝置節點屬性發布者設為當前PID,使用CDev::open開啟(但實際這個類也沒幹什麼,可以忽略這一句),
如果是讀取許可權,認為是訂閱者,申請SubscriberData給filp->f_priv,記錄這個訂閱物件fd所私有的資料,並且告訴物件屬性又一個訂閱者訂閱了你
close(*filp) int virtual
裝置節點關閉:
如果是釋出者呼叫的關閉,將屬性發布者置為0
如果是訂閱者呼叫,獲取filp中的SubscriberData,從呼叫列表中刪除,移除內部呼叫者
read(*filp, *buffer, buflen) ssize_t virtual
將類中資料_data讀入到buffer中
write(*filp, *buffer, buflen) ssize_t virtual
將buffer的資料讀入到類的_data屬性中
ioctl(*filp, cmd, arg) int virtual
根據cmd的值調整arg的型別,輸出對應資料屬性
publish(*meta, handle, *data) ssize_t static
這是靜態函式,所有例項公用一個
根據傳入的控制代碼(即DeviceNode的指標),將data的資料寫入類的物件屬性_data中
//以下3個函式應該是設計遠端呼叫IChannel介面的,這裡不做討論,因為沒看懂,也沒有示例使用
process_add_subscription(rateInHz) int16_t
process_remove_subscription() int16_t
process_received_message(length, *data) int16_t
將接受的資料寫入_data屬性中
add_internal_subscriber() void
remove_internal_subscriber() void
is_published() bool
//protected:
poll_state(*filp) pollevent_t virtual
判斷主題是否推送給訂閱者,需要推送返回POLLIN,否則0
poll_notify_one(struct pollfd *fds,pollevent_t events )
對於fds來說,如果有更新,呼叫Cdev::poll_notify_one()函式進行更新
appears_updated(SubscriberData *sd) bool
根據sd的資訊,判斷是否有更新(釋出者呼叫這個函式)
update_deferred() void
監控拉的需求
private:
struct SubscriberData {
unsigned generation; /**< last generation the subscriber has seen */
unsigned update_interval; /**< if nonzero minimum interval between updates */
struct hrt_call update_call; /**< deferred wakeup call if update_period is nonzero */
void *poll_priv; /**< saved copy of fds->f_priv while poll is active */
bool update_reported; /**< true if we have reported the update via poll/check */
int priority; /**< priority of publisher */
};
const struct orb_metadata *_meta; /**< object metadata information */
uint8_t *_data; /**< allocated object buffer */
hrt_abstime _last_update; /**< time the object was last updated */
volatile unsigned _generation; /**< object generation count */
pid_t _publisher; /**< if nonzero, current publisher */
const int _priority; /**< priority of topic */
bool _published; /**< has ever data been published */
DeviceMaster
繼承device::CDev,管理裝置節點的類
DeviceMaster(Flavor f)
*GetDeviceNode(*node_name) uORB::DeviceNode
ioctl(*filp, cmd,arg ) int virtual
private:
Flavor _flavor; //是PUBSUB還是OBJ
static ORBMap _node_map; //所有的DeviceNode裝置都在其中
uORBDevices_posix
nuttx是在px4在nuttx作業系統下執行的,這個應該是裸機執行的。
uORBManager
這個類管理著每一個UORB主題和節點,也實現了UORb的API
繼承自IChannelRxHandler類,類中函式大多都在前面見過,我只大概描述一下實現,如果有必要的話
get_instance() Manager* static
獲取生成的uORBManager例項,靜態的,單個程序中只生成一個例項
orb_advertise(*meta,*data) orb_advert_t
orb_advertise_multi(*meta, *data, *instance, priority) orb_advert_t
根據主題和例項編號獲取檔案描述符,根據檔案描述符獲取當前釋出者的指標並返回釋出者的指標
orb_publish(*meta, handle, *data) int
釋出資料,返回成功與否
實現:
呼叫DevicNode的publish方法,DeviceNode::publish是靜態方法,呼叫handle->write()函式(write會使屬性_generation++,標記資料版本的),將資料寫入檔案中
orb_subscribe(*meta) int
orb_subscribe_multi(*meta, instance) int
訂閱的原理就是開啟以只讀開啟檔案,返回fd
(一個檔案可以被開啟多次,返回的fd都不一樣,fd對應這file *filp這個指標(也就是你在DeivceNode類中引數filp一樣,這個指標的結構體引數比較全),所以可以開啟一次,有多個備份的filp)
orb_unsubscribe(handle) int
orb_copy(*meta, handle, buffer) int
從handle中讀取相應長度的資料到buffer中
orb_check(handle, *updated) int
使用ioctl方法(ioctrl方法有DeviceNode類實現)
這個檢查的辦法是對比filp中維護的SubscriberData資料和物件中屬性generation是否一致,雖然filp有多個,但是檔案(或者說DeviceNode物件只有一個),所以可以進行判斷。(更新版本是在寫入時候generation++)
orb_stat(handle, *time) int
使用ioctl方法
orb_exists(*meta, instance) int
主題的對應例項是否有
根據引數生成路徑,然後試圖開啟對應檔案
orb_priority(handle, *priority) int
使用ioctl方法
orb_set_interval(handle, interval) int
使用ioctl方法
set_uorb_communicator(*comm_channel) void
get_uorb_communicator() IChannel*
is_remote_subscriber_present(*messageName) bool
private:
node_advertise(*meta, *instance, priority) int
通過呼叫控制裝置MasterNode的ioctl函式(被過載過),讓控制檔案去建立對應的DeviceNode物件(有了物件,對應路徑下就有了檔案)
node_open(Flavor, *meta, *data, advertiser, *instance, priority) int
根據訂閱釋出的屬性開啟對應的裝置檔案,返回檔案描述符
這個函式讓你遮蔽是否存在這樣的裝置檔案,根據你的需求去解決。
如果你是訂閱者,存在,直接返回。不存在,那就建立一個,方法同下。
如果你是釋出者,存在,關閉fd。重新開啟一次,(這時node_advertise呼叫將自動進入下一個空閒條目)不存在,呼叫node_advertise釋出建立一個DeviceNode物件(有了物件自然會有檔案)
實現:
判斷輸入的邏輯合法性
根據訂閱者還是釋出者,選擇對應讀取許可權開啟檔案
如果是釋出者&&開啟成功,說明這個檔案存在
關閉檔案重新呼叫node_advertise釋出再開啟檔案
返回檔案描述符fd
process_add_subscription(*messageName, msgRate) int16_t
處理訂閱的回撥函式,Manger類實現的
實現邏輯:
將主體加入訂閱主題的集合中
根據對應資訊獲取路徑,再從裝置管理器DeviceMaster獲取對應的裝置節點
具體裝置節點處理訂閱頻率
process_remove_subscription(*messageName) int16_t
處理移除訂閱的回撥函式,Manger類實現
實現邏輯:
將主題移除主題集合
獲取對應的裝置節點
具體裝置節點處理移除事件
process_received_message(*message, length, *data) int16_t
處理接受資訊的回撥函式,Manger類實現
實現邏輯:
獲取對應的裝置節點
具體的裝置節點處理接受的資訊
static Manager *_Instance; //全域性唯一的管理器
// the communicator channel instance.
uORBCommunicator::IChannel *_comm_channel;
ORBSet _remote_subscriber_topics;
uORBUtils
工具類Utils中只有2個同名的靜態函式
int node_mkpath(char* buf, Flavor f, const struct orb_metadata *meta, int *instance)
int node_mkpath(char* buf, Flavor f, const char* orbMsgName)
引數含義:
buf:輸出路徑的字串
f: 標誌,是釋出訂閱還是作為引數(PUBSUB/PARAM)
meta: 通過meta->o_name = orbMsgName
instance: 如果是多個例項,對應相應的,否則預設為0
生成路徑為/(obj?param)/orbMsgName/instance ,應該是節點裝置的檔案路徑
uORBCommunicator
namespace uORBCommunicator
{
//倆個類都為純虛類,可以認為java中的介面類
class IChannel; // 可以理解為這個是客戶端
class IChannelRxHandler; //這個是伺服器端
}
IChannel
啟用遠端訂閱的介面,介面的實現類需要管理通訊通道,快速RPC/TCP/IP
add_subscription(*messageName, msgRateInHz) int16_t
引數列表含義:主題名稱(全域性唯一),最大訊息更新速率
通知有興趣的遠端實體訂閱訊息的介面
remove_subscription(*messageName) int16_t
通知遠端實體移除訂閱的介面
register_handler(IChannelRxHandler *handler) int16_t
註冊訊息控制代碼
send_message(*messageName, length, *data) int16_t
通過通訊連結傳送資料
IChannelRxHandler
通過通訊鏈路回撥的類
process_add_subscription(*messageName, msgRateInHz) int16_t
處理從遠端接受新增的介面函式
process_remove_subscription(*messageName) int16_t
處理遠端移除訂閱的介面
process_received_message(*messageName, length, *data) int16_t
處理遠端傳送資料的類
相關推薦
px4原始碼學習六--uORB模組研究
UORB模組研讀 uORB函式解析: uORB模組(Micro Object Request Broker,微物件請求代理器) uORB是Pixhawk系統中關鍵的一個模組,肩負了資料傳輸任務。所有感測器,資料傳輸任務,GPS,PPM訊號從晶片獲取後通過u
px4原始碼學習五--固定翼位置控制模組
fw_pos_control模組 class landingslope 為固定翼著陸的角度變化模組 calulateSlopeValues() void private 更新H1,H0,d1,根據log(H0/H1)的比例調整 d1 / d1+ del
PX4原始碼學習一--Pix和APM的區別
pixhawk是硬體平臺, PX4是pixhawk的原生韌體,專門為pixhawk開發的 APM(Ardupilot Mega)也是硬體 Ardupilot是APM的韌體,所以稱ArduPilot韌體也叫APM 後來APM硬體效能不太夠,所以APM韌體也
Python原始碼學習(四)-builtins模組的初始化
Module的初始化是從系統預定義的PyModuleDef開始的 typedef struct PyModuleDef{ PyModuleDef_Base m_base; const char* m_name; const char* m_doc; Py_
px4原始碼學習三--px4原始碼結構分析
px4原始碼結構分析 Px4原始碼目錄 cmake: 是存放的 Cmake 編輯指令碼資料夾, 其中 Cmake/Configs 是存放的不同硬體的編譯指令碼, nuttx_mindpx-v2_default 是 PIXHAWK 這個硬體的編輯
RocketMQ NameServer模組 原始碼學習
RocketMQ namesrv 模組中 用到了 MixAll 類, 其中有一個 properties2Object(Properties, Object) 通用方法,把properties 轉換成 簡單的 POJO object。 你可以進一步擴充套件:從.properties
nginx 原始碼學習筆記(二十一)—— event 模組(二) ——事件驅動核心ngx_process_events_and_timers
首先繼續回憶下,之前子執行緒執行操作裡面有一個未涉及的內容ngx_process_events_and_timers,今天我們就來研究下這個函式。 本篇文章來自於:http://blog.csdn.net/lengzijian/article/details/7601730 先來看一下第十九
Python學習筆記(十六)模組
模組是Python程式架構的一個核心概念 模組就好比是工具包,要想使用這個工具包中的工具,就需要匯入import這個模組 每一個以副檔名py結尾的Python原始碼檔案都是一個模組 在模組中定義的全域性變數、函式都是模組能夠提供給外界直接使用的工具 &nb
Pyhton學習筆記六:模組
為了編寫可維護的程式碼,我們把函式分組,分別放到不同的資料夾裡,在python中,一個.py檔案就稱之為一個模組(Module) 使用模組有什麼好處呢:一是:大大提高了程式碼的可維護性,當一個模組編寫完成以後,就可以在其他地方引用這些模組;二是:使用模組避免函
python3語言學習筆記(六:模組+輸入輸出)
引入模組 import 模組名 使用時:模組名.函式名(引數) from 模組名 import 函式名 使用時:函式名(引數) import 模組名.* 使用時:函式名(引數) 格式化字串 方式一:使用str.format() 方式二:使用%格式化字串
tensorflow學習筆記六——keras模組
本文基於tensorflow官網教程(https://tensorflow.google.cn/guide/keras),機器環境為ubuntu14.04LTS+tensorflow1.8.01.Keras模組配置 Keras模組是tensorflow提供的一個
深度學習結合SLAM的研究思路/成果整理之(一)使用深度學習方法替換SLAM中的模組
整理了部分近兩年深度學習結合SLAM的一些研究成果(參考知乎帖子https://www.zhihu.com/question/66006923 和泡泡機器人公眾號,附上論文連結和已找到的原始碼/資料集連結,大多簡單看了一下摘要。僅為自己學習所用,確實翻譯得很爛…
vue的原始碼學習之六——6.非同步元件
1 介紹 版本:2.5.17。 我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  
vue的原始碼學習之六——5.元件註冊
1 介紹 版本:2.5.17。 我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。  
vue的原始碼學習之六——3.patch
1. 介紹 版本:2.5.17。 我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。 &nbs
vue的原始碼學習之六——2.createComponent虛擬DOM元件
1. 介紹 版本:2.5.17。 我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。 &nb
vue的原始碼學習之六——1.元件化(介紹)
1. 介紹 版本:2.5.17。 我們使用vue-vli建立基於Runtime+Compiler的vue腳手架。 &nb
libevent原始碼學習研究(libevent-0.1)
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">想學習研究libevent怎麼設計的,學習它的思想,學習它的設計,奈何自
px4原生原始碼學習-(2)--實時作業系統篇
/************************************************************************************************************** po上我使用到的硬體和開發環境 px4硬體
spring原始碼學習之六 元素處理過程
概述: </property-placeholder> 主要用來讀取配置資訊替換Bean中的${}佔位符, 常用配置如下: <context:property-placeholder ignore-resource-not-found="