Skip to content

Instantly share code, notes, and snippets.

@district10
Last active February 19, 2016 13:18
Show Gist options
  • Save district10/418b5937664753956e32 to your computer and use it in GitHub Desktop.
Save district10/418b5937664753956e32 to your computer and use it in GitHub Desktop.

封装说明

标签: notes docs bcd

read online:


UI -- Wrapper(Class) -- TCP/IP -- Wrapper(Class)
title: 封装示意图(忽略了界面同步部分)
主UI->主Wrapper: 所有 UI 操作\n由 Wrapper 接管
note right of 主Wrapper: 本机执行?\n是
主Wrapper->主Class: 直接调用
主Class->主Wrapper: 捕获结果
主Wrapper->主UI: 在界面上有所反映
note right of 主Wrapper: 本机执行?\n否
主Wrapper->主Socket: 把调用函数和参数\n传递到另一台电脑
主Socket->从Socket: TCP 通讯\n程序运行时\n已经建立连接
从Socket->从Wrapper: 还原函数和参数,\n调用 Wrapper
从Wrapper->从Class: 直接调用
从Class->从Wrapper: 捕捉结果
note left of 从Wrapper: 异地反映?\n是\n(序列图里忽略\n了本地反映)
从Wrapper->从Socket: 回传
从Socket->主Socket: 
主Socket->主Wrapper: 解析给 Wrapper
主Wrapper->主UI: 在界面上有所反映
  • 主:Master,操作的界面(高性能平台)
  • 从:Slave,另一台电脑(工控机)

程序只有一个,运行在两个电脑上,一个为 Master,一个为 Slave,TCP 连接建立后。操作 Master 的 UI,执行可能在

  • 本机(Master),也可能在
  • 另一台电脑(Slave)。

对 UI 而言,无所谓执行发生在哪台电脑(实现细节被 Wrappers 屏蔽)。

Wrapper 主要做了两件事:

  1. 判断执行应该在本机还是异地,并调用本地或异地的类执行实体
  2. 接受 Class 或者 Socket 传来的结果,并 emit 相应的信号(二传手)

Wrapper 和 Class 有几乎相同的调用接口, 所以原来在单电脑上的程序只要简单修改即可。

对程序的要求:

  1. 要避免直接获取 Class 的返回值(返回值难以跨电脑),而是采用 emit 信号的方式
  2. Class 的信号由 Wrapper 完全接管,重新发射信号(信号名称保持不变)。
  3. UI 的变化,都是通过接受 Wrapper 的信号。

新的预操作流程

主程序运行后,先要选择自己是 Master(高性能平台)还是 Slave(工控机):

高性能平台要先打开,并打开 TCP 网络服务器:(点击 Serve

再运行程序,不过这次选择 Slave,然后将工控机连接到高性能平台:(点击 Connect

现在两者连接上后,界面发生变化(一些按钮可以使用,一些按钮不再能使用)。现在点击下方大按钮,可以同步两者的颜色(在红和绿直接切换)。

关闭这两个界面会弹出一个 demo 界面,展示的如何同步两者的 UI 以及运算如何分布在两台电脑上,如图:

可以看到,

  • “加法”运行在高性能平台上,
  • “乘法”运行在工控机上。

同时,界面是同步的。下面介绍如何将原有类改善,实现跨主机通讯、UI 同步。


具体的操作指南(如何修改类和 UI)

先明确几个概念,

类(Class) : 实际的执行实体

包装(Wrapper) : 封装了类,提供几乎一致的接口,屏蔽了跨主机的一些操作。

界面类(UI) : 界面上,调用的虽然是 Wrappers,但和调用 Classes 的方式一致。

这里的修改指南指导了如何在 Class 和 UI 之间插入一个封装,在不改变(几乎)原有类的基础上实现跨主机通信。

--------------------------------H--O--W------------------T--O-------------------?--?--?-----------------------------

比如从 ui->pushButton->setText( class->getString() ) 改起。

首先,在 QString Class::getString( args ) 中 emit 一个信号:

emit wrp_getString( returnValue ) // 要提前定义(signals: void wrp_getString( QString text );)

然后参照 Class 定义一个 Wrapper,提供 void Wrapper::getString( args ) 函数(几乎和原有类 Class 一模一样),

把 Class 的信号和 Wrapper 的槽连接:

connect( class,   SIGNAL(wrp_getString(QString)),
         wrapper, SLOT(onWrp_getString(QString)) );

Wrapper 构造的时候会传入 Class 类实体指针, 如果 Class 在本机运行,Wrapper::getString( args ) 会直接调用 Class::getString( args ), 如果 Class 在异地运行,经过判断 Wrapper::getString( args ) 会把函数名 ID 和参数 args 传到另一台电脑, 由另一台电脑的对应的 Wrapper 的 Class 实体执行 Class::getString( args )

不管哪个电脑上执行了 Class 函数,信号都会被那个电脑的 Wrapper 捕获,然后 emit 一个同样的信号,并让另一台电脑 emit 这个信号。 即 Wrapper::getString( args ) 的大致逻辑为:

// Class 函数对应的 Wrapper 函数
void Wrapper::getString( args )
{
    if ( Class registered on THIS_COMPUTER )
    {
        // 本机执行
        this->class->getString();
    }
    else
    {   
        // 异地执行(把函数名和参数传到另一台电脑)
        send `Class::getString' + `args' to the other computer 
        tell THE_OTHER_COMPUTER to run: class->getString()
    }
}

// 信号所连接的槽
void onWrp_getString( QString returnValue )
{
    // 本机释放信号
    emit wrp_getString( returnValue );
    // 让异地电脑释放信号
    tell THE_OTHER_COMPUTER to: emit Wrapper::wrp_getString( returnValue )
}

现在,两台电脑上,有一台电脑执行了操作(可能是你通过 UI 交互的那一台,或者是另一台), 释放了两个同样的信号,只要把这个信号绑定到各自的 UI,界面就同步了:

connect( wrapper, SIGNAL(wrp_getString(QString)),
         ui,      SLOT(onWrp_getString(QString)) );

最后 UI 得到这个信号,在界面上有所反映:

void Ui::onWrp_getString( QString returnValue )
{
    ui->pushButton->setText( returnValue );
}

现在,

UI 不再直接调用 Class,而是调用 Wrapper 的函数,

需把原来的 ui->pushButton->setText( class->getString() ) 换成 wrapper->getString()

总结一下,原来的一句之间的 setText,变成了间接的:

  • 某一台电脑界面的操作,导致某一台电脑执行 Class 函数
  • Class emit 信号给 Wrapper
  • Wrapper 在两台电脑 emit 信号给 UI
  • 两个 UI 得到信号,同步更新界面

注意:

直接调用 private 成员也不可以了。
--------------------------------E-----N-----D-------------------------------------------------------

其他 1:Bundle

所有的 Wrappers 和 UI 都在一个全局的 Bundle 中绑定。(源码位于 Src/Wrappers/Wrappers.h

/*
 * 获得实例
 */

Bundle::getInstance(); // 获得唯一的、静态的一个实例,包含了所有的大的类实例,UIs,Wrappers(Classes)


/*
 * 本机是高性能平台(Master)还是工控机(Slave)?
 */

// who am I?
Bundle::whoAmI(); // 我是谁?MASTER 还是 SLAVE?

// 可以进行判断
if ( Bundle::whoAmI() == MASTER ) {
    qDebug() << "我是高性能平台(Master)";
}


/*
 * Bundle 的东西有
 */

// 主要模块的“封装”
Bundle::getInstance()->lms              // LMS Wrapper
Bundle::getInstance()->lmsAgent         // Wrapper 用到的界面
Bundle::getInstance()->server           // 网络通信接口
Bundle::getInstance()->client           // 网络通信接口
...

// 也可以获得主要模块的“类实体”(不推荐直接调用)
Bundle::getInstance()->lms->kernel      // LMSReader 实体,可能为 NULL(如果不在本机注册)
...


/*
 * Bundle 的一些函数
 */

// 向另一台电脑发数据
Bundle::getInstance()->send( const QByteArray &msg );  // 发送数据到另一台电脑

// 数据需要满足一定的格式,可通过一个 Moderator(“翻译”)静态类/函数进行转化,如:
// 要在另一台电脑上运行 LMSReader::genNewPath( "D://tmp/" ); (LMS 注册在那台电脑上)
Bundle::getInstance()->send( Moderator::lms_genNewPath( "D://tmp/" ) );

其他 2:TODO

  • 现在 Log 会在两边记录自己的部分,应该改为:退出时,1)如果 Master 退出,那么发出请求让 Slave 把 log 传过来,再重新排序,再退出;2)如果 Slave 退出,那么请求 Master 退出,然后等待 Master 的 log 请求,其余同 1)。 有点 Bug 但功能已经有了。
  • 因为 Log 的时间为两台电脑的时间戳,所以需要校准。连接初期,发出 M->S->M->S->M->S 的请求,得到时间戳的改正,修正 Slave 端的时间戳。(如果电脑都联网的话,就没必要弄这个)
  • 把其他的界面和类改完(这个比较机械,但量多,最近两天的工作

其他 3: Bug fixes

  • ~~现在信号有存在……所以有点混乱。应该不难解决。~~是界面上 TextChanged 信号老被触发的问题。已解决。

其他 4: 类的注册

很简单,修改 Moderator 的一个 HashMap 即可:

// Master: 高性能平台
// Slave : 工控机
wss.insert(  BCD::TYPE_LMS,                             SLAVE  );
wss.insert(  BCD::TYPE_MCU,                             SLAVE  );
wss.insert(  BCD::TYPE_UR,                              SLAVE  );
wss.insert(  BCD::TYPE_ARM,                             SLAVE  );
wss.insert(  BCD::TYPE_SP20000C,                        MASTER );
wss.insert(  BCD::TYPE_MULTIPLICATION_ON_SLAVE,         SLAVE  );
wss.insert(  BCD::TYPE_ADDITION_ON_MASTER,              MASTER );
#!/bin/bash
# pandoc CodingStyleOfChangshaTeam_CVRS.md -o CodingStyleOfChangshaTeam_CVRS.docx
git add -A && git commit -m 'done' && git push
@district10
Copy link
Author

ditched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment