<strike id="kiyse"></strike>
  • <tr id="kiyse"></tr>
  • <strike id="kiyse"></strike><samp id="kiyse"><tbody id="kiyse"></tbody></samp>
    <strike id="kiyse"><s id="kiyse"></s></strike>
    <tr id="kiyse"></tr>
    <noframes id="kiyse"><code id="kiyse"></code></noframes>
    <th id="kiyse"></th>
    <samp id="kiyse"></samp>
  • <th id="kiyse"><s id="kiyse"></s></th>
  • Linux雙進(jìn)程應(yīng)用示例

     2018-1-9     作者:廖光澤         
    文章標(biāo)簽:C/C++QMLQt

    1、概述


      一臺(tái)典型的工控設(shè)備通常包括若干通訊接口(網(wǎng)絡(luò)、串口、CAN等),以及若干數(shù)字IO、AD通道等。運(yùn)行于設(shè)備核心平臺(tái)的應(yīng)用程序通過(guò)操作這些接口,實(shí)現(xiàn)特定的功能。通常為了高效高精度完成整個(gè)通訊控制流程,應(yīng)用程序采用C/C++語(yǔ)言來(lái)編寫(xiě)。圖1表現(xiàn)了典型工控設(shè)備的組成關(guān)系。


    Linux雙進(jìn)程應(yīng)用示例.gif

    典型工控設(shè)備框圖


      工控設(shè)備的另一個(gè)特點(diǎn)是鑒于設(shè)備大多是24小時(shí)連續(xù)運(yùn)行,且無(wú)人值守,所以基本的工控設(shè)備是無(wú)顯示的。英創(chuàng)的工控主板ESM6800、ESM335x等都大量的應(yīng)用于這類(lèi)無(wú)頭工控設(shè)備之中。


      在實(shí)際應(yīng)用中,部分客戶需要基于已有的無(wú)頭工控設(shè)備,增加顯示界面功能,以滿足新的應(yīng)用需求。顯然保持已有的基本工控處理程序不變,通過(guò)相對(duì)獨(dú)立的技術(shù)手段來(lái)實(shí)現(xiàn)顯示功能,最符合客戶的利益訴求。為此我們發(fā)展了一種雙進(jìn)程的程序設(shè)計(jì)方案來(lái)滿足客戶的這一需求。該方案的第一個(gè)進(jìn)程,以客戶已有的用C/C++寫(xiě)的基礎(chǔ)工控進(jìn)程為基礎(chǔ),僅增加一個(gè)面向本地IP(127.0.0.1)的偵聽(tīng)線程,用于向顯示進(jìn)程提供必要的運(yùn)行工況數(shù)據(jù)。圖2為增添了服務(wù)線程的工控進(jìn)程:


    Linux雙進(jìn)程應(yīng)用示例.gif

    帶有偵聽(tīng)線程的基礎(chǔ)工控進(jìn)程


      方案的第二個(gè)進(jìn)程則主要用于實(shí)現(xiàn)顯示界面,可以采用各種手段來(lái)實(shí)現(xiàn),本文中介紹了使用Qt的QML語(yǔ)言加通訊插件的界面設(shè)計(jì)方法。第二個(gè)進(jìn)程(具體是通訊插件單元)通過(guò)本地IP,以客戶端方式與基礎(chǔ)工控進(jìn)程進(jìn)行Socket通訊,完成進(jìn)程間數(shù)據(jù)交換。顯示進(jìn)程以及與工控進(jìn)程的關(guān)系如圖3所示:


    Linux雙進(jìn)程應(yīng)用示例.gif

    顯示進(jìn)程與工控進(jìn)程


    2、系統(tǒng)設(shè)計(jì)


      鑒于工業(yè)控制領(lǐng)域?qū)ο到y(tǒng)運(yùn)行的穩(wěn)定性要求,控制系統(tǒng)更加傾向于將底層硬件控制部分與上層界面顯示分開(kāi),兩部分以雙進(jìn)程的形式各自獨(dú)立運(yùn)行。底層硬件控制部分將會(huì)監(jiān)控系統(tǒng)硬件,管理外設(shè)等,同時(shí)收集系統(tǒng)的狀態(tài);而上層界面顯示部分主要用于顯示系統(tǒng)狀態(tài),并實(shí)現(xiàn)少量的系統(tǒng)控制功能,方便維護(hù)人員查看系統(tǒng)運(yùn)行狀態(tài)并且根據(jù)當(dāng)前狀態(tài)進(jìn)行系統(tǒng)的調(diào)整。由于顯示界面不一定是所有設(shè)備都配置,而且顯示部分的程序更加復(fù)雜,從而更容易出現(xiàn)程序運(yùn)行時(shí)的錯(cuò)誤,將控制與顯示分開(kāi)能夠避免由于顯示部分的程序問(wèn)題而影響到整個(gè)控制系統(tǒng)的運(yùn)行,而且沒(méi)有配置顯示屏的設(shè)備也可以直接運(yùn)行底層的控制程序,增加了系統(tǒng)程序的兼容性。顯示與控制分離后,由于顯示界面程序不需要處理底層硬件的管理控制,在設(shè)計(jì)時(shí)可以更加注重于界面的美化,而且界面程序可以采用不同的編程語(yǔ)言進(jìn)行開(kāi)發(fā),比如使用Qt C++或者Android java,本文將介紹基于Linux + Qt的雙進(jìn)程示例程序供客戶在實(shí)際開(kāi)發(fā)中參考,關(guān)于Android程序請(qǐng)參考我們官網(wǎng)的另一篇文章:《Android雙應(yīng)用進(jìn)程Demo程序設(shè)計(jì)》


    Linux雙進(jìn)程應(yīng)用示例.gif


      如上圖所示。整個(gè)系統(tǒng)分為控制和顯示兩個(gè)進(jìn)程,底層硬件控制部分可以獨(dú)立運(yùn)行,使用多線程管理不同的硬件設(shè)備,監(jiān)控硬件狀態(tài),將狀態(tài)發(fā)送給socket服務(wù)器,并且從socket服務(wù)器接收命令來(lái)更改設(shè)備狀態(tài)。Socket服務(wù)器也是一個(gè)獨(dú)立的線程,通過(guò)本地網(wǎng)絡(luò)通信集中處理來(lái)自硬件控制線程以及顯示程序的消息。顯示界面需要連接上socket服務(wù)器才能正確的顯示設(shè)備的狀態(tài),同時(shí)提供必須的人工控制接口,供設(shè)備使用過(guò)程中人為調(diào)整設(shè)備運(yùn)行狀態(tài)。目前在ESM6802工控主板上,界面程序可以采用Qt C++編寫(xiě),也可以使用Android java進(jìn)行開(kāi)發(fā),本文僅介紹采用Qt的界面程序。顯示程序界面用QML搭建,與底層通信的部分用獨(dú)立的Qt QML插件實(shí)現(xiàn),這樣顯示部分進(jìn)一部分離為數(shù)據(jù)處理和界面開(kāi)發(fā),使得界面設(shè)計(jì)可以更加快捷。程序的整體界面效果如下圖所示:


    Linux雙進(jìn)程應(yīng)用示例.gif


      目前我們只提供了串口(SERIAL)和GPIO兩部分的例程。下面將集中介紹程序中通過(guò)本地IP實(shí)現(xiàn)兩個(gè)進(jìn)程通信的部分供客戶在實(shí)際開(kāi)發(fā)中參考。


    3、控制端C程序


      控制端程序主要分為兩個(gè)部分,一個(gè)部分用于控制具體的硬件運(yùn)行(下文稱為控制器),另一個(gè)部分為socket服務(wù)器,用于與顯示程序之間進(jìn)行通信。由于本方案主要是為了展示在已有控制程序的基礎(chǔ)上,增加顯示界面功能,以滿足新的應(yīng)用需求,所以我們?cè)诖酥攸c(diǎn)介紹在已有控制程序中加入socket服務(wù)器的部分,不再詳細(xì)介紹各硬件的具體控制的實(shí)現(xiàn)。


      增加本地IP通信的功能,首先需要在控制進(jìn)程中新加入一個(gè)socket服務(wù)器線程,用于消息的集中管理,實(shí)現(xiàn)底層硬件與上層的界面程序的信息交換,socket服務(wù)器線程運(yùn)行的函數(shù)體代碼如下:



    static void *_init_server(void *param)

    {

           int server_sockfd, client_sockfd;

           int server_len;

           struct sockaddr_in server_address;

           struct sockaddr_in client_address;

     

           server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

           server_address.sin_family = AF_INET;

           server_address.sin_addr.s_addr = inet_addr("127.0.0.1");//通過(guò)本地ip通信

           server_address.sin_port = htons(9733);

           server_len = sizeof(server_address);

           bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

          

           listen(server_sockfd, 5);

     

           int res;

           pthread_t client_thread;

           pthread_attr_t attr;

           char id[4];

           client_element *client_t;

     

           while(1)

           {

                  if(!client_has_space(clients))

                  {

                         printf("to many client, wait for one to quit...\n");

                         sleep(2);

                         continue;

                  }

                  printf("server waiting\n");

                  client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, (socklen_t *)&server_len);

     

                  //get and save client id

                  read(client_sockfd, &id, 4);

                  if((id[0]!='I') && (id[1]!='D'))

                  {

                         printf("illegal client id, drop it\n");

                         close(client_sockfd);

                         continue;

                  }

                 

                  client_t = accept_client(clients, id, client_sockfd);

                  printf("client: %s connected\n", id);

     

                  //create a new thread to handle this connection

                  res = pthread_attr_init(&attr);

                  if( res!=0 )

                  {

                         printf("Create attribute failed\n" );

                  }

                  // 設(shè)置線程綁定屬性

                  res = pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );

                  // 設(shè)置線程分離屬性

                  res += pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

                  if( res!=0 )

                  {

                         printf( "Setting attribute failed\n" );

                  }

     

                  res = pthread_create( &client_thread, &attr,

                                (void *(*) (void *))socked_thread_func, (void*)client_t );

                  if( res!=0 )

                  {

                         close( client_sockfd );

                         del_client(clients, client_sockfd);

                         continue;

                  }

                  pthread_attr_destroy( &attr );

           }

    }



      此函數(shù)創(chuàng)建一個(gè)socket用于監(jiān)聽(tīng)(listen)等待顯示程序連接,當(dāng)接受(accept)一個(gè)連接之后創(chuàng)建一個(gè)新的線程用于消息處理,主要用于維護(hù)socket連接的狀態(tài),解析消息的收發(fā)方,并將消息轉(zhuǎn)送到對(duì)應(yīng)的接收方,在顯示程序建立連接之前或者連接斷開(kāi)之后,控制器發(fā)送的消息將不會(huì)進(jìn)行發(fā)送了,而控制器依然在正常運(yùn)行,用于處理消息的新線程如下:



    static void *socked_thread_func(void *p)

    {

           client_element *client_p = (client_element *)p;

           printf("started socked_thread_func for client: %s\n", client_p->id);

           fd_set fdRead;

           int ret, lenth;

           struct timeval     aTime;

           struct msg_head msg_h;

           char *buf = (char *)&msg_h;  //from:2 char to 2 char msglenth:1 int

           buf[0] = client_p->id[2];

           buf[1] = client_p->id[3];

           char msg[100];

           client_element *send_to;

           struct tcp_info info;

           int tcp_info_len=sizeof(info);

           while(1)

           {

                  FD_ZERO(&fdRead);

                  FD_SET(client_p->sockfd, &fdRead);

     

                  aTime.tv_sec = 2;

                  aTime.tv_usec = 0;

     

                  getsockopt(client_p->sockfd, IPPROTO_TCP, TCP_INFO, &info, (socklen_t *)&tcp_info_len);

                  if(info.tcpi_state == 1)

                  {

                         //printf("$$$%d tcp connection established...\n", client_p->sockfd);

                         ;

                  }

                  else

                  {

                         printf("$$$%d tcp connection closed...\n", client_p->sockfd);

                         break;

                  }

     

                  ret = select( client_p->sockfd+1,&fdRead,NULL,NULL,&aTime );

     

                  if (ret > 0)

                  {

                         //判斷是否讀事件

                         if (FD_ISSET(client_p->sockfd, &fdRead))

                         {

                                //data available, so get it!

                                lenth = read( client_p->sockfd, buf+2, 6 );

                                if( lenth != 6 )

                                {

                                       continue;

                                }

                                // 對(duì)接收的數(shù)據(jù)進(jìn)行處理,這里為簡(jiǎn)單的數(shù)據(jù)轉(zhuǎn)發(fā)

                                lenth = read(client_p->sockfd, msg, msg_h.lenth);

                                if(lenth == msg_h.lenth)

                                {

                                       send_to = find_client(clients, msg_h.to);

                                       //printf("try to send to client %s\n", msg_h.to);

                                       if(send_to == NULL)

                                       {

                                              printf("can't find target client\n");

                                              continue;

                                       }

                                       write(send_to->sockfd, &msg_h, sizeof(struct msg_head));

                                       write(send_to->sockfd, msg, lenth);

                                }

                                // 處理完畢

                         }

                  }

           }

           close( client_p->sockfd );

           del_client(clients, client_p->sockfd);

           pthread_exit( NULL );

    }



      這里收到消息后就解析消息頭,發(fā)送到指定的端口去(控制器或者顯示進(jìn)程),由于實(shí)際應(yīng)用中socket傳送數(shù)據(jù)可能存在分包的情況,客戶需要自行定義消息的數(shù)據(jù)格式來(lái)保證數(shù)據(jù)的完整性,以及對(duì)數(shù)據(jù)進(jìn)行更嚴(yán)格的驗(yàn)證。


      另一方面對(duì)于已有的控制器來(lái)說(shuō),需要在原來(lái)的基礎(chǔ)上進(jìn)行修改,在主線程中與socket服務(wù)器建立連接:



    sockedfd = socket(AF_INETSOCK_STREAM0);

    address.sin_family = AF_INET;

    address.sin_addr.s_addr = inet_addr("127.0.0.1");

    address.sin_port = htons(9733);

    len = sizeof(address);


    do

    {

            res = connect(sockedfd, (struct sockaddr *)&address, len);

            if(res == -1)

            {

                    perror("oops: connect error");

            }

    }while(res == -1);

    write(sockedfd"IDG1"4);

    printf("###connected to server\n");



      然后建立兩個(gè)線程分別處理數(shù)據(jù)(data_thread_func)和命令(command_thread_func),其中data_thread_func用于監(jiān)聽(tīng)硬件狀態(tài),并且發(fā)送相應(yīng)的狀態(tài)消息給socket服務(wù)器,而command_thread_func用于監(jiān)聽(tīng)socket服務(wù)器的消息等待命令,用于改變硬件運(yùn)行狀態(tài),不需要界面帶有控制功能的客戶可以不實(shí)現(xiàn)commad_thread_func。以GPIO控制器為例:



    void *gpio_controller::data_thread_func(void* lparam)

    {

           gpio_controller *pSer = (gpio_controller*)lparam;

     

           fd_set fdRead;

           int ret=0;

           struct timeval     aTime;

           unsigned int pinstates = 0;

           struct msg_head buf_h;

     

           while( 1 )

           {

                  FD_ZERO(&fdRead);

                  FD_SET(pSer->interface_fd,&fdRead);

     

                  aTime.tv_sec = 2;

                  aTime.tv_usec = 0;

     

                  //等待硬件消息,這里是GPIO狀態(tài)改變

                  ret = select( pSer->interface_fd+1,&fdRead,NULL,NULL,&aTime );

     

                  if (ret < 0 )

                  {

                         //關(guān)閉

                         perror("select wrong");

                         pSer->close_interface(pSer->interface_fd);

                         break;

                  }

     

                  else

                  {

                         //select超時(shí)或者GPIO狀態(tài)發(fā)生了改變,讀取GPIO狀態(tài),發(fā)送給socket服務(wù)器

                         pinstates = INPINS;

                         ret = GPIO_PinState(pSer->interface_fd, &pinstates);

                         if(ret < 0)

                         {

                                printf("GPIO_PinState::failed %d\n", ret);

                                break;

                         }

                         sprintf((char *)&buf_h.to[0], "D1");

                         buf_h.lenth = sizeof(pinstates);

                         write(pSer->sockedfd, (void *)&buf_h.to[0], 6);

                         write(pSer->sockedfd, (void *)&pinstates, sizeof(pinstates));

                  }

           }

           printf( "ReceiveThreadFunc finished\n");

           pthread_exit( NULL );

    }

     

    void *gpio_controller::command_thread_func(void* lparam)

    {

           gpio_controller *pSer = (gpio_controller*)lparam;

     

           fd_set fdRead;

           int ret, len;

           struct timeval     aTime;

           struct outcom{

                  unsigned int outpin;

                  unsigned int outstate;

           };

           struct outcom out;

           struct msg_head buf_h;

     

           while( 1 )

           {

                  FD_ZERO(&fdRead);

                  FD_SET(pSer->sockedfd,&fdRead);

     

                  aTime.tv_sec = 3;

                  aTime.tv_usec = 300000;

     

    //等待socket服務(wù)器的消息

                  ret = select( pSer->sockedfd+1,&fdRead,NULL,NULL,&aTime );

                  if (ret < 0 )

                  {

                         //關(guān)閉

                         pSer->close_interface(pSer->interface_fd);

                         break;

                  }

     

                  if (ret > 0)

                  {

                         //判斷是否讀事件

                         if (FD_ISSET(pSer->sockedfd,&fdRead))

                         {

                                len = read(pSer->sockedfd, &buf_h, sizeof(buf_h));

                                //獲取socket服務(wù)器發(fā)送的信息,進(jìn)行解析

                                if(len != sizeof(struct outcom))

                                {

                                       printf("###invalid command lenth: %d, terminate\n", len);

                                }

                                len = read(pSer->sockedfd, &out, buf_h.lenth);

     

                                //write command

                                switch(out.outstate)

                                {

                                       case 0:

                                              GPIO_OutClear(pSer->interface_fd, out.outpin);

                                              if(ret < 0)

                                                     printf("GPIO_OutClear::failed %d\n", ret);

                                              //printf("GPIO_OutClear::succeed %d\n", ret);

                                              break;

                                       case 1:

                                              GPIO_OutSet(pSer->interface_fd, out.outpin);

                                              if(ret < 0)

                                                     printf("GPIO_OutSet::failed %d\n", ret);

                                              //printf("GPIO_OutSet::succeed %d\n", ret);

                                              break;

                                       default:

                                              printf("###wrong gpio state %d, no operation\n", out.outstate);

                                              ret = -1;

                                              break;

                                }

                                if(ret < 0)

                                       break;

                         }

                  }

           }

           printf( "ReceiveThreadFunc finished\n");

           pthread_exit( NULL );

    }



      這里兩個(gè)函數(shù)主要任務(wù)都是處理數(shù)據(jù),data_thread_func使用select函數(shù)來(lái)等待輸入GPIO的狀態(tài)改變事件,如果有狀態(tài)改變或者select等待超時(shí)都讀取一次GPIO的狀態(tài),然后發(fā)送給socket服務(wù)器;command_thread_func監(jiān)聽(tīng)服務(wù)器的消息,收到消息后進(jìn)行解析,然后根據(jù)消息來(lái)操作GPIO輸出信號(hào)。


      通過(guò)這兩個(gè)函數(shù)便與socket服務(wù)器建立了消息溝通通道,而socket服務(wù)器會(huì)自動(dòng)將數(shù)據(jù)轉(zhuǎn)發(fā)到顯示進(jìn)程,這種實(shí)現(xiàn)可以使得對(duì)已有程序的改動(dòng)降到很低的程度。實(shí)際實(shí)現(xiàn)中,可以在socket服務(wù)器中增加狀態(tài)機(jī)等其他功能,記錄硬件狀態(tài)信息等。


    4、顯示程序


      顯示部分我們采用Qt來(lái)搭建,主要分為QML搭建的界面以及Qt c++編寫(xiě)的數(shù)據(jù)處理插件。QML是Qt提供的一種描述性的腳本語(yǔ)言,類(lèi)似于css,可以在腳本里創(chuàng)建圖形對(duì)象,并且支持各種圖形特效,以及狀態(tài)機(jī)等,同時(shí)又能跟Qt寫(xiě)的C++代碼進(jìn)行方便的交互,使用起來(lái)非常方便。采用QML加插件的方式主要是為了將界面設(shè)計(jì)與程序邏輯解耦,一般的系統(tǒng)開(kāi)發(fā)中界面設(shè)計(jì)的變動(dòng)往往多于后臺(tái)邏輯,因此采用QML加插件的方式將界面設(shè)計(jì)與邏輯分離有利于開(kāi)發(fā)人員的分工,加速產(chǎn)品迭代速度,降低后期維護(hù)成本。而且QML解釋性語(yǔ)言的特性使得其語(yǔ)法更加簡(jiǎn)單,可以將界面設(shè)計(jì)部分交給專業(yè)的設(shè)計(jì)人員開(kāi)發(fā),而不要求設(shè)計(jì)人員會(huì)c++等編程語(yǔ)言。Qt底層對(duì)QML做了優(yōu)化,將會(huì)優(yōu)先使用硬件圖形加速器進(jìn)行界面的渲染,也針對(duì)觸摸屏應(yīng)用做了優(yōu)化,使用QML能夠更簡(jiǎn)單快捷的搭建流暢、優(yōu)美的界面。QML也支持嵌入Javascript處理邏輯,但是底層邏輯處理使用Qt C++編寫(xiě)插件,能夠更好的控制數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)處理也更加高效,Qt提供了多種方式將C++數(shù)據(jù)類(lèi)型導(dǎo)入QML腳本中,更多詳細(xì)資料可以查看Qt官方的文檔。由于篇幅原因,我們?cè)诹硗庖黄恼拢?a href='http://www.www.jsjflaw.com/article/article20181109.html' target='_blank'>《使用QML進(jìn)行界面開(kāi)發(fā)》中更詳細(xì)地介紹了QML及插件的實(shí)現(xiàn),在此我們還是集中介紹socket消息處理部分。


      本例程中數(shù)據(jù)處理插件的任務(wù)就是連接socket服務(wù)器,與服務(wù)器進(jìn)行通信,接收消息進(jìn)行解析然后提供給QML界面,以及從QML界面獲取消息給socket服務(wù)器發(fā)送命令。插件中通過(guò)socket進(jìn)行通信的部分代碼如下:



    void MsgClient::cServer(void* param)

    {

        MsgClient *client = (MsgClient *)param;

        int ret;

        int len;

        struct sockaddr_in address;

        int sockedfd = socket(AF_INETSOCK_STREAM0);

        printf("sockedfd: %d\n", sockedfd);

        client->sockedfd = sockedfd;

        address.sin_family = AF_INET;

        address.sin_addr.s_addr = inet_addr("127.0.0.1");   //本地IP通信

        address.sin_port = htons(9733);

        len = sizeof(address);

        do

        {

            printf("Client: connecting...\n");

            ret = ::connect(sockedfd, (struct sockaddr *)&address, len); //建立連接

            if(ret == -1)

            {

                perror("oops: connect to server error");

            }

            sleep(2);

        }while(ret == -1);

        write(sockedfd, "IDD1"4);

        printf("Client: connected to server\n");

        emit client->serverConnected();

        fd_set fdRead;

        struct timeval aTime;

        char buf[100];

        unsigned int pinstates;

        struct msg_head buf_h;

        while(!client->exit_flag)

        {

            FD_ZERO(&fdRead);

            FD_SET(sockedfd, &fdRead);

            aTime.tv_sec = 3;

            aTime.tv_usec = 0;

            ret = select(sockedfd+1, &fdRead, NULLNULL, &aTime); //等待消息

            if(ret < 0)

            {

                perror("someting wrong with select");

            }

            if(ret > 0)

            {

                if(FD_ISSET(sockedfd, &fdRead))

                {

                    len = read(sockedfd, &buf_h, sizeof(buf_h));

                    int i;

                    switch (buf_h.from[0]) { //解析消息

                    case 'S':

     //串口信息

                        i = buf_h.from[1] - '0';

                        len = read(sockedfd, buf, buf_h.lenth);

                        client->rmsgQueue[i] << buf;

                        if(i == client->m_interface)

                            emit client->newMsgRcved();

                        memset(buf, 0sizeof(buf));

                        break;

                    case 'G':

    //GPIO信息

                        len = read(sockedfd, &pinstates, buf_h.lenth);

                        printf("get GPIO pinstates\n");

                        client->updateGPIOState(pinstates);

                        break;

                    default:

                        break;

                    }

                }

            }

        }

        close(sockedfd);

        pthread_exit(NULL);

    }



      如代碼所示,插件首先通過(guò)本地IP127.0.0.1與socket服務(wù)器建立連接(connect),然后等待socket服務(wù)器的消息(select),收到消息后進(jìn)行解析,判斷是哪個(gè)硬件控制器發(fā)送的消息,然后更新相應(yīng)的顯示界面,這里的代碼相對(duì)簡(jiǎn)單,只是為了展示通過(guò)本地IP實(shí)現(xiàn)顯示進(jìn)程與控制進(jìn)程之間的通信,實(shí)際使用中客戶需要對(duì)數(shù)據(jù)進(jìn)行更嚴(yán)格的檢驗(yàn)。


      使用QML搭建串口控制界面如下圖所示:


    Linux雙進(jìn)程應(yīng)用示例.gif


      GPIO控制器的顯示效果如下:


    Linux雙進(jìn)程應(yīng)用示例.gif


      由于篇幅原因,我們?cè)诖瞬辉敿?xì)介紹實(shí)現(xiàn)界面的QML腳本了,將會(huì)在另一篇文章中進(jìn)行專門(mén)的介紹,感興趣的用戶可以關(guān)注我們官網(wǎng)上的文章更新,或者向我們要取程序源碼。用戶在實(shí)際開(kāi)發(fā)中可以參考此方式實(shí)現(xiàn)顯示進(jìn)程與控制進(jìn)程之間的通信,從而實(shí)現(xiàn)單獨(dú)的顯示進(jìn)程,對(duì)已有的控制進(jìn)程的更改控制到很小的程度,一方面減少了由于程序修改而造成控制程序的不穩(wěn)定,另一方面使用QML又能快速的搭建界面,解決顯示設(shè)備狀態(tài)的需求。


    5、總結(jié)


      實(shí)際測(cè)試過(guò)程中,我們?cè)贓SM6802工控板上運(yùn)行本文介紹的程序,底層控制程序直接可以開(kāi)機(jī)后臺(tái)運(yùn)行,顯示程序開(kāi)機(jī)后手動(dòng)加載,通過(guò)本地IP地址與控制程序的socket服務(wù)器連接,然后實(shí)時(shí)更新系統(tǒng)狀態(tài),也能及時(shí)響應(yīng)人工控制,如改變輸出GPIO的輸出狀態(tài),關(guān)掉顯示程序之后,控制程序繼續(xù)正常運(yùn)行,之后還可以再次啟動(dòng)顯示程序。


      將底層控制與顯示分開(kāi)后,程序開(kāi)發(fā)分工可以更加細(xì)致,也一定程度上增加了控制系統(tǒng)的穩(wěn)定性,減小了維護(hù)成本。同時(shí)使用QML進(jìn)行界面開(kāi)發(fā)能夠更加方便快速的更新系統(tǒng)的顯示效果,完成產(chǎn)品迭代。由于底層控制與顯示之間采用socket進(jìn)行通信,顯示部分也可以采用其他的開(kāi)發(fā)環(huán)境,比如ESM6802也支持Android開(kāi)發(fā),用戶在產(chǎn)品升級(jí)換代的時(shí)候就能夠直接沿用底層控制部分的程序,而只對(duì)上層顯示部分的程序進(jìn)行調(diào)整。


      有興趣的客戶可以和我們的工程師進(jìn)行溝通獲取更多信息以及程序代碼。


      本文PDF下載:Linux雙進(jìn)程應(yīng)用示例

    文章標(biāo)簽:C/C++QMLQt
    日韩精品免费电影| 久久国产乱子伦精品免费一| 国产成人精品日本亚洲11| 国语自产偷拍精品视频偷蜜芽| 国产精品无码亚洲一区二区三区 | 亚洲精品乱码久久久久久自慰| 国产精品视频一区| 日本尤物精品视频在线看| 亚洲国产精品国自产拍AV| 99久久精品国产高清一区二区| 国产成人啪精品午夜在线播放| 亚洲精品无码aⅴ中文字幕蜜桃| 精品亚洲成a人片在线观看少妇| 亚洲人精品午夜射精日韩| 国产精品美女久久久免费| 国产日韩精品一区二区在线观看| 国产成人精品午夜福利在线播放| 久久精品国产亚洲AV久| 99热这里只/这里有精品| 99久久国语露脸精品国产| 精品国产一级在线观看| 麻豆va在线精品免费播放| 精品日韩二区三区精品视频| 最新露脸国产精品视频| 亚洲精品视频免费看| 91麻豆精品在线观看| 亚洲精品线在线观看| 亚洲av无码国产精品夜色午夜| 国产亚洲精品a在线观看app| 视频一区精品自拍| 色噜噜亚洲精品中文字幕| 国产综合精品久久亚洲 | 国产精品麻豆成人AV电影艾秋 | 国产69精品久久久久999三级| 成人无号精品一区二区三区| 久久久国产精品无码一区二区三区| 99久久国产热无码精品免费| 无码精品尤物一区二区三区| 麻豆麻豆必出精品入口| 国内精品免费麻豆网站91麻豆| 国产网站麻豆精品视频|