<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何在嵌入式中使用設計模式的思想?

          共 13430字,需瀏覽 27分鐘

           ·

          2021-12-14 02:02

          ????關(guān)注、星標公眾號,直達精彩內(nèi)容

          來源:CSDN

          作者 | 取經(jīng)的孫猴兒

          整理 | 嵌入式應用研究院

          嵌入式

          嵌入式的標簽多為:低配,偏硬件,底層,資源緊張,代碼多以C語言,匯編為主,代碼應用邏輯簡單。但隨著AIOT時代的到來,局面組件改變。芯片的性能資源逐漸提升,業(yè)務邏輯也逐漸變得復雜,相對于代碼的效率而言,代碼的復用可移植性要求越來越高,以獲得更短的項目周期 和更高的可維護性。下面是AIOT時代嵌入式設備的常見的軟件框架。

          設計模式

          設計模式的標簽:高級語言 ,高端,架構(gòu)等。在AIOT時代,設計模式與嵌入式能擦出怎樣的火花?設計模式可描述為:對于某類相似的問題,經(jīng)過前人的不斷嘗試,總結(jié)出了處理此類問題的公認的有效解決辦法。

          嵌入式主要以C語言開發(fā),且面向過程,而設計模式常見于高級語言(面向?qū)ο螅?,目前市面上描述設計模式的書籍多數(shù)使用JAVA 語言,C語言能實現(xiàn)設計模式嗎?設計模式與語言無關(guān),它是解決問題的方法,JAVA可以實現(xiàn),C語言同樣可以實現(xiàn)。同樣的,JAVA程序員會遇到需要用模式來處理的問題,C程序員也可能遇見,因此設計模式是很有必要學習的。

          模式陷阱:設計模式是針對具體的某些類問題的有效解決辦法,不是所有的問題都能匹配到對應的設計模式。因此,不能一味的追求設計模式,有時候簡單直接的處理反而更有效。有的問題沒有合適的模式,可以盡量滿足一些設計原則,如開閉原則(對擴展開放,對修改關(guān)閉)

          觀察者模式

          情景

          在對象之間定義一個一對多的依賴,當一個對象狀態(tài)改變的時候,所有依賴的對象都會自動收到通知。

          實現(xiàn)

          主題對象提供統(tǒng)一的注冊接口,以及注冊函數(shù) 。由觀察者本身實例化observer_intf 接口,然后使用注冊函數(shù),添加到對應的主題列表中,主題狀態(tài)發(fā)生改變,依次通知列表中的所有對象。

          ?struct?observer_ops
          ?{
          ?????void*(handle)(uint8_t?evt);??
          ?};
          ?struct?observer_intf
          ?{
          ?????struct?observer_intf*?next;
          ?????const?char*?name;
          ?????void*?condition;
          ?????const?struct?observer_ops?*ops;
          ?}
          ?int?observer_register(struct?topical*?top?,?struct?observer_intf*?observer);

          當主題狀態(tài)發(fā)生改變,將通知到所有觀察者,觀察者本身也可以設置條件,是否選擇接收通知

          ?struct?observer_intf?observer_list;
          ?????
          ?void?XXXX_topical_evt(uint8_t?evt)
          ?{
          ??????struct?observer_intf*?cur_observer?=?observer_list.next;
          ??????uint8_t*?condition?=?NULL;
          ??????while(cur_observer?!=?NULL)
          ??????{
          ??????????condition?=?(uint8_t*)cur_observer->condition;
          ??????????if(NULL?==?condition?||?(condition?&&?*condition))
          ??????????{
          ??????????????if(cur_observer->ops->handle){
          ??????????????????cur_observer->ops->handle(evt);
          ??????????????}???????
          ??????????}
          ??????????cur_observer?=?cur_observer->next;
          ??????}
          ?}

          實例:嵌入式裸機低功耗框架

          • 設備功耗分布

          其中線路損耗,電源電路等軟件無法控制,故不討論。板載外設,如傳感器可能通過某條命令配置進入低功耗模式,又或者硬件上支持控制外設電源來控制功耗。片內(nèi)外設,及芯片內(nèi)部的外設,通過卸載相關(guān)驅(qū)動,關(guān)閉時鐘配置工作模式來控制功耗。

          • 設備喚醒方式

            當系統(tǒng)某個定時事件到來時,系統(tǒng)被主動喚醒處理事件

            系統(tǒng)處于睡眠,被外部事件喚醒,如串口接收到一包數(shù)據(jù),傳感器檢測到變化,通過引腳通知芯片

            • 被動喚醒
            • 主動喚醒
          • 系統(tǒng)允許睡眠的條件

            • 外設無正在收發(fā)的數(shù)據(jù)

            • 緩存無需要處理的數(shù)據(jù)

            • 應用層狀態(tài)處于空閑(無需要處理的事件)

          • 基于觀察者模式的PM框架實現(xiàn)

          PM組件提供的接口

          struct?pm
          {
          ????struct?pm*?next;
          ????const?char*?name;
          ???void(*init)(void);
          ????void(*deinit(void);
          ????void*?condition;
          };
          static?struct?pm?pm_list;
          static?uint8_t?pm_num;
          static?uint8_t?pm_status;
          ?????????
          int?pm_register(const?struct?pm*?pm?,?const?char*?name)
          {
          ?????struct?pm*?cur_pm?=??&pm_list;
          ?????while(cur_pm->next)
          ?????{
          ?????????cur_pm?=?cur_pm->next;
          ?????}
          ?????cur_pm->next?=?pm;
          ?????pm->next?=?NULL;
          ?????pm->name?=?name;
          ?????pm_num++;
          }
          ?
          void?pm_loop(void)
          {
          ????uint32_t?pm_condition?=?0;
          ????struct?pm*?cur_pm?=??pm_list.next;
          ????static?uint8_t?cnt;
          ????
          ????/*check?all?condition*/
          ????while(cur_pm)
          ????{
          ????????if(cur_pm->condition){
          ?????????pm_condition?|=??*((uint32_t*)(cur_pm->condition));
          ????????}
          ????????cur_pm?=?cur_pm->next;
          ????}
          ????if(pm_condition?==?0)
          ????{
          ??????cnt++;
          ????????if(cnt>=5)
          ????????{
          ????????????pm_status?=?READY_SLEEP;
          ????????}
          ????}
          ????else
          ????{
          ????????cnt?=?0;
          ????}
          ????if(?pm_status?==?READY_SLEEP)
          ????{
          ?????????cur_pm?=??pm_list.next;
          ?????????while(cur_pm)
          ?????????{
          ?????????????if(cur_pm->deinit){
          ????????????????cur_pm->deinit();
          ?????????????}
          ?????????????cur_pm?=?cur_pm->next;
          ?????????}
          ????????pm_status?=?SLEEP;
          ????????ENTER_SLEEP_MODE();
          ????}???
          ????/*sleep--->wakeup*/
          ????if(pm_status?==?SLEEP)
          ????{
          ?????????pm_status?=?NORMAL;
          ?????????cur_pm?=??pm_list.next;
          ?????????while(cur_pm)
          ?????????{
          ?????????????if(cur_pm->init){
          ????????????????cur_pm->init();
          ?????????????}
          ?????????????cur_pm?=?cur_pm->next;
          ?????????}
          ????}
          }

          外設使用PM接口

          struct?uart_dev
          {
          ?...
          ?struct?pm?pm;
          ????uint32_t?pm_condition;?
          };
          struct?uart_dev?uart1;
          ?
          void?hal_uart1_init(void);
          void?hal_uart1_deinit(void);
          ????
          void?uart_init(void)
          {
          ????uart1.pm.init?=??hal_uart1_init;
          ????uart1.pm.deinit?=??hal_uart1_deinit;
          ????uart1.pm.condition?=?&uart1.pm_condition;
          ????pm_register(&uart1.pm?,?"uart1");
          }
          /*接下來串口驅(qū)動檢查緩存?,?發(fā)送?,?接收是否空閑或者忙碌?,?給uart1.pm_condition賦值*/

          結(jié)論

          • PM 電源管理可以單獨形成模塊,當功耗外設增加時,只需實現(xiàn)接口,注冊即可
          • 通過定義段導出操作,可以更加簡化應用層或外設的注冊邏輯
          • 方便調(diào)試,可以很方便打印出系統(tǒng)當前為滿足睡眠條件的模塊
          • 通過條件字段劃分,應該可以實現(xiàn)系統(tǒng)部分睡眠
          職責鏈模式

          情景

          在現(xiàn)實生活中,一個事件(任務)需要經(jīng)過多個對象處理是很常見的場景。如報銷流程,公司員工報銷, 首先員工整理報銷單,核對報銷金額,有誤則繼續(xù)核對整理,直到無誤,將報銷單遞交到財務,財務部門進行核對,核對無誤后,判斷金額數(shù)量,若小于一定金額,則財務部門可直接審批,若金額超過范圍,則報銷單流傳到總經(jīng)理,得到批準后,整個任務才算結(jié)束。類似的情景還有很多,如配置一個WIFI模塊,通過AT指令,要想模塊正確連入WIFI , 需要按一定的順序依次發(fā)送配置指令 , 如設置設置模式 ,設置 需要連接的WIFI名,密碼,每發(fā)送一條配置指令,模塊都將返回配置結(jié)果,而發(fā)送者需要判斷結(jié)果的正確性,再選擇是否發(fā)送下一條指令或者進行重傳。

          總結(jié)起來是,一系列任務需要嚴格按照時間線依次處理的順序邏輯,如下圖所示:

          在存在系統(tǒng)的情況下,此類邏輯可以很容易的用阻塞延時來實現(xiàn),實現(xiàn)如下:

          void?process_task(void)
          {
          ????task1_process();
          ????msleep(1000);
          ????
          ????task2_process();
          ????mq_recv(¶m?,?1000);
          ????
          ????task3_process();
          ????while(mq_recv(¶m?,?1000)?!=?OK)
          ????{
          ????????if(retry)
          ????????{
          ?????????????task3_process();
          ?????????????--try;
          ????????}
          ????}
          }

          在裸機的情況下,為了保證系統(tǒng)的實時性,無法使用阻塞延時,一般使用定時事件配合狀態(tài)機來實現(xiàn):

          void?process_task(void)
          {
          ?????switch(task_state)
          ?????{
          ?????????case?task1:
          ?????????????task1_process();
          ?????????????set_timeout(1000);break;
          ?????????case?task2:
          ?????????????task1_process();
          ?????????????set_timeout(1000);break;
          ?????????case?task3:
          ?????????????task1_process();
          ?????????????set_timeout(1000)break;
          ?????????default:break;
          ?????}
          }
          /*定時器超時回調(diào)*/
          void?timeout_cb(void)
          {
          ????if(task_state?==?task1)
          ????{
          ????????task_state?=?task2;
          ????????process_task();
          ????}
          ????else??//task2?and?task3
          ????{
          ????????if(retry)
          ????????{
          ????????????retry--;
          ?????????????process_task();
          ????????}
          ????}
          }
          /*任務的應答回調(diào)*/
          void?task_ans_cb(void*?param)
          {
          ????if(task==task2)
          ????{
          ????????task_state?=?task3;
          ????????process_task();
          ????}
          }

          和系統(tǒng)實現(xiàn)相比,裸機的實現(xiàn)更加復雜,為了避免阻塞,只能通過狀態(tài)和定時器來實現(xiàn)順序延時的邏輯,可以看到,實現(xiàn)過程相當分散,對于單個任務的處理分散到了3個函數(shù)中處理,這樣導致的后果是:修改,移植的不便。而實際的應用中,類似的邏輯相當多,如果按照上面的方法去實現(xiàn),將會導致應用程序的強耦合。

          實現(xiàn)

          可以發(fā)現(xiàn),上面的情景有以下特點:

          • 任務按順序執(zhí)行,只有當前任務執(zhí)行完了(有結(jié)論,成功或者失敗)才允許執(zhí)行下一個任務
          • 前一個任務的執(zhí)行結(jié)果會影響到下一個任務的執(zhí)行情況
          • 任務有一些特性,如超時時間,延時時間,重試次數(shù)

          通過以上信息,我們可以抽象出這樣一個模型:任務作為節(jié)點, 每一個任務節(jié)點有其屬性:如超時,延時,重試,參數(shù),處理方法,執(zhí)行結(jié)果。當需要按照順序執(zhí)行一系列任務時,依次將任務節(jié)點串成一條鏈,啟動鏈運行,則從任務鏈的第一個節(jié)點開始運行,運行的結(jié)果可以是 OK , BUSY ,ERROR 。若是OK, 表示節(jié)點已處理,從任務鏈中刪除,ERROR 表示運行出錯,任務鏈將停止運行,進行錯誤回調(diào),可以有用戶決定是否繼續(xù)運行下去。BUSY表示任務鏈處于等待應答,或者等待延時的情況。當整條任務鏈上的節(jié)點都執(zhí)行完,進行成功回調(diào)。

          node數(shù)據(jù)結(jié)構(gòu)定義

          /*shadow?node?api?type?for?req_chain?src*/
          typedef?struct?shadow_resp_chain_node
          {
          ?uint16_t?timeout;
          ?uint16_t?duration;
          ?uint8_t?init_retry;
          ?uint8_t?param_type;
          ?uint16_t?retry;
          ?/*used?in?mpool*/
          ???struct?shadow_resp_chain_node*?mp_prev;
          ?struct?shadow_resp_chain_node*?mp_next;
          ?
          ????/*used?resp_chain*/
          ?struct?shadow_resp_chain_node*?next;
          ?
          ?node_resp_handle_fp?handle;
          ?void*?param;
          }shadow_resp_chain_node_t;

          node內(nèi)存池

          使用內(nèi)存池的必要性:實際情況下,同一時間,責任鏈的條數(shù),以及單條鏈的節(jié)點數(shù)比較有限,但種類是相當多的。比如一個支持AT指令的模塊,可能支持幾十條AT指令,但執(zhí)行一個配置操作,可能就只會使用3-5條指令,若全部靜態(tài)定義節(jié)點,將會消耗大量內(nèi)存資源。因此動態(tài)分配是必要的。

          初始化node內(nèi)存池,內(nèi)存池內(nèi)所有節(jié)點都將添加到free_list。當申請節(jié)點時,會取出第一個空閑節(jié)點,加入到used_list , 并且接入到責任鏈。當責任鏈某一個節(jié)點執(zhí)行完,將會被自動回收(從責任鏈中刪除,并從used_list中刪除,然后添加到free_list)

          職責鏈數(shù)據(jù)結(jié)構(gòu)定義

          typedef?struct?resp_chain
          {
          ???bool?enable;???????????????//enble?==?true?責任鏈啟動?
          ?bool??is_ans;??????????????//收到應答,與void*?param?共同組成應答信號
          ?
          ?uint8_t?state;????????????
          ?const?char*?name;
          ?void*?param;
          ?TimerEvent_t?timer;
          ?bool?timer_is_running;
          ?shadow_resp_chain_node_t?node;????????//節(jié)點鏈
          ?void(*resp_done)(void*?result);???????//執(zhí)行結(jié)果回調(diào)
          }resp_chain_t;

          職責鏈初始化

          void?resp_chain_init(resp_chain_t*?chain?,??const?char*?name?,?
          ????????????????????????????????????????????void(*callback)(void*?result))???????????????????
          {
          ???RESP_ASSERT(chain);
          ?/*only?init?one?time*/
          ?resp_chain_mpool_init();
          ?
          ???chain->enable?=?false;
          ?chain->is_ans?=?false;
          ?chain->resp_done?=?callback;
          ?chain->name?=?name;
          ?
          ?chain->state?=?RESP_STATUS_IDLE;
          ?chain->node.next?=?NULL;
          ?chain->param?=?NULL;
          ?
          ?TimerInit(&chain->timer,NULL);
          }

          職責鏈添加節(jié)點

          int?resp_chain_node_add(resp_chain_t*?chain?,?
          ????????????????????????node_resp_handle_fp?handle?,?void*?param)
          {
          ???RESP_ASSERT(chain);
          ?BoardDisableIrq();??
          ?shadow_resp_chain_node_t*?node?=?chain_node_malloc();
          ?if(node?==?NULL)
          ?{
          ????BoardEnableIrq();
          ????RESP_LOG("node?malloc?error?,no?free?node");
          ??return?-2;
          ?}
          ?/*初始化節(jié)點,并加入責任鏈*/
          ?shadow_resp_chain_node_t*?l?=?&chain->node;
          ?while(l->next?!=?NULL)
          ?{
          ??l?=?l->next;
          ?}
          ?l->next?=?node;
          ?node->next?=?NULL;?
          ?node->handle?=?handle;
          ?node->param?=?param;
          ?node->timeout?=?RESP_CHIAN_NODE_DEFAULT_TIMEOUT;
          ?node->duration?=?RESP_CHIAN_NODE_DEFAULT_DURATION;
          ?node->init_retry?=?RESP_CHIAN_NODE_DEFAULT_RETRY;
          ?node->retry?=?(node->init_retry?==?0)??0?:(node->init_retry-1);
          ?BoardEnableIrq();
          ?return?0;
          }

          職責鏈的啟動

          void?resp_chain_start(resp_chain_t*?chain)
          {
          ???RESP_ASSERT(chain);
          ?chain->enable?=?true;
          }

          職責鏈的應答

          void?resp_chain_set_ans(resp_chain_t*?chain?,?void*?param)
          {
          ?RESP_ASSERT(chain);
          ???if(chain->enable)
          ?{
          ??chain->is_ans?=?true;
          ??if(param?!=?NULL)
          ?????chain->param?=?param;
          ??else
          ??{
          ???chain->param?=?"NO?PARAM";
          ??}
          ?}
          }

          職責鏈的運行

          int?resp_chain_run(resp_chain_t*?chain)
          {?
          ?RESP_ASSERT(chain);
          ?if(chain->enable)
          ?{
          ????shadow_resp_chain_node_t*?cur_node?=?chain->node.next;
          ????/*maybe?ans?occur?in?handle,so?cannot?change?state?direct?when?ans?comming*/
          ????if(chain->is_ans)
          ??{
          ???chain->is_ans?=?false;
          ???chain->state?=?RESP_STATUS_ANS;
          ??}??
          ???
          ??switch(chain->state)
          ??{
          ???case?RESP_STATUS_IDLE:
          ???{
          ????if(cur_node)
          ????{
          ???????uint16_t?retry?=?cur_node->init_retry;
          ?????if(cur_node->handle)
          ?????{
          ????????cur_node->param_type?=?RESP_PARAM_INPUT;
          ??????chain->state?=?cur_node->handle((resp_chain_node_t*)cur_node???????????????????????????????????????????????????????????????,cur_node->param);
          ?????}
          ?????else
          ?????{
          ?????????RESP_LOG("node?handle?is?null?,goto?next?node");
          ??????chain->state?=?RESP_STATUS_OK;
          ?????}
          ?????if(retry?!=?cur_node->init_retry)
          ?????{
          ??????cur_node->retry?=?cur_node->init_retry>0?(cur_node-??????????????????????????????????????????????????????>init_retry-1):0;???????????????????????
          ?????}
          ????}
          ????else
          ????{
          ???????if(chain->resp_done)
          ?????{
          ??????chain->resp_done((void*)RESP_RESULT_OK);
          ?????}
          ?????chain->enable?=?0;
          ?????chain->state?=?RESP_STATUS_IDLE;
          ?????TimerStop(&chain->timer);
          ?????chain->timer_is_running??=?false;
          ????}
          ????break;
          ???}
          ???case?RESP_STATUS_DELAY:
          ???{
          ????if(chain->timer_is_running?==?false)
          ????{
          ???????chain->timer_is_running??=?true;
          ?????TimerSetValueStart(&chain->timer?,?cur_node->duration);
          ????}
          ????if(TimerGetFlag(&chain->timer)?==?true)
          ????{
          ?????chain->state?=?RESP_STATUS_OK;
          ?????chain->timer_is_running??=?false;
          ????}
          ?????break;?
          ???}
          ???case?RESP_STATUS_BUSY:
          ???{
          ??????/*waiting?for?ans?or?timeout*/
          ??????if(chain->timer_is_running?==?false)
          ????{
          ???????chain->timer_is_running??=?true;
          ?????TimerSetValueStart(&chain->timer?,?cur_node->timeout);
          ????}
          ????if(TimerGetFlag(&chain->timer)?==?true)
          ????{
          ?????chain->state?=?RESP_STATUS_TIMEOUT;
          ?????chain->timer_is_running??=?false;
          ????}
          ????break;
          ??????}
          ???case?RESP_STATUS_ANS:
          ?????{
          ??????/*already?got?the?ans,put?the?param?back?to?the?request?handle*/
          ??????TimerStop(&chain->timer);
          ????chain->timer_is_running??=?false;
          ????if(cur_node->handle)
          ????{
          ?????cur_node->param_type?=?RESP_PARAM_ANS;
          ?????chain->state?=?cur_node->handle((resp_chain_node_t*)cur_node?,?????????????????????????????????????????????????????????????????chain->param);
          ????}
          ????else
          ????{
          ?????RESP_LOG("node?handle?is?null?,goto?next?node");
          ?????chain->state?=?RESP_STATUS_OK;
          ????}
          ????break;
          ???}
          ???case?RESP_STATUS_TIMEOUT:
          ???{
          ????if(cur_node->retry)
          ????{
          ?????cur_node->retry--;?
          ?????/*retry?to?request?until?cnt?is?0*/
          ?????chain->state?=?RESP_STATUS_IDLE;
          ????}
          ????else
          ????{
          ?????chain->state?=?RESP_STATUS_ERROR;
          ????}
          ????break;
          ???}
          ???case?RESP_STATUS_ERROR:
          ???{
          ??????if(chain->resp_done)
          ????{
          ???????chain->resp_done((void*)RESP_RESULT_ERROR);
          ????}
          ????chain->enable?=?0;
          ????chain->state?=?RESP_STATUS_IDLE;
          ????TimerStop(&chain->timer);
          ????chain->timer_is_running??=?false;
          ????cur_node->retry?=?cur_node->init_retry>0?(cur_node->init_retry-1):0;
          ????chain_node_free_all(chain);
          ????break;
          ???}
          ???case?RESP_STATUS_OK:
          ???{
          ??????/*get?the?next?node*/
          ??????cur_node->retry?=?cur_node->init_retry>0?(cur_node->init_retry-1):0;
          ????chain_node_free(cur_node);
          ????chain->node.next?=?chain->node.next->next;
          ????chain->state?=?RESP_STATUS_IDLE;
          ????break;
          ???}
          ??????default:
          ?????break;
          ??}
          ?}
          ?return?chain->enable;
          }

          測試用例

          • 定義并初始化責任鏈
          void?chain_test_init(void)
          {
          ????resp_chain_init(&test_req_chain?,?"test?request"?,?test_req_callback);
          }
          • 定義運行函數(shù),在主循環(huán)中調(diào)用
          void?chain_test_run(void)
          {
          ????resp_chain_run(&test_req_chain);
          }
          • 測試節(jié)點添加并啟動觸發(fā)函數(shù)
          void?chain_test_tigger(void)
          {
          ????resp_chain_node_add(&test_req_chain?,??node1_req?,NULL);
          ????resp_chain_node_add(&test_req_chain?,??node2_req,NULL);
          ????resp_chain_node_add(&test_req_chain?,??node3_req,NULL);
          ????resp_chain_start(&test_req_chain);
          }
          • 分別實現(xiàn)節(jié)點請求函數(shù)
          ?/*延時1s?后執(zhí)行下一個節(jié)點*/
          ?int?node1_req(resp_chain_node_t*?cfg,?void*?param)
          ?{
          ?????cfg->duration?=?1000;
          ?????RESP_LOG("node1?send?direct?request:?delay?:%d?ms"?,?cfg->duration);
          ?????return?RESP_STATUS_DELAY;
          ?}
          ?/*超時時間1S?,?重傳次數(shù)5次*/?
          ?int?node2_req(resp_chain_node_t*?cfg?,?void*?param)
          ?{
          ?????static?uint8_t?cnt;
          ?????if(param?==?NULL)
          ?????{
          ?????????cfg->init_retry?=?5;
          ?????????cfg->timeout??=?1000;
          ?????????RESP_LOG("node2?send?request?max?retry:%d?,?waiting?for?ans...");
          ?????????return?RESP_STATUS_BUSY;
          ?????}
          ?????RESP_LOG("node2?get?ans:?%d",(int)param);
          ?????return?RESP_STATUS_OK;
          ?}
          ?/*非異步請求*/??
          ?int?node3_req(resp_chain_node_t*?cfg?,?void*?param)
          ?{
          ?????RESP_LOG("node4?send?direct?request");
          ?????return?RESP_STATUS_OK;
          ?}
          ?
          ?void?ans_callback(void*?param)
          ?{
          ?????resp_chain_set_ans(&test_req_chain?,?param);
          ?}

          結(jié)論

          • 實現(xiàn)了裸機處理 順序延時任務
          • 較大程度的簡化了應用程的實現(xiàn),用戶只需要實現(xiàn)響應的處理函數(shù) , 調(diào)用接口添加,即可按時間要求執(zhí)行
          • 參數(shù)為空,表明為請求 ,否則為應答。(在某些場合,請求可能也帶參數(shù),如接下來所說的LAP協(xié)議,此時需要通過判斷參數(shù)的類型)

          設計模式參考

          Head First 設計模式(中文版)

          人人都懂設計模式:從生活中領悟設計模式:Python實現(xiàn)

          設計模式之禪

          設計模式的C語言應用.https://bbs.huaweicloud.com/blogs/113179

          適配器模式

          參考文獻 &?引用

          [1]取經(jīng)的孫猴兒. (2021, June?24). 嵌入式設計模式. https://blog.csdn.net/qq_36969440/article/details/118189387

          版權(quán)歸原作者所有,如有侵權(quán),請聯(lián)系刪除。

          ???????????????? ?END ?????????????????

          關(guān)注我的微信公眾號,回復“加群”按規(guī)則加入技術(shù)交流群。

          歡迎關(guān)注我的視頻號:

          點擊“閱讀原文”查看更多分享,歡迎點分享、收藏、點贊、在看。

          瀏覽 60
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产成人无码Av片小说在线观看 | 逼特逼在线观看视频 | 亚洲精品午夜成人片DY888 | 精品无码人妻一区二区免费蜜桃 | 国产无码操 |