03_软件实现功能详解

本部分将详细介绍,在软件代码的编写工程中所实现的各种功能,并给出实现功能的代码片段

1. 与硬件的通信

该功能是使用python中的第三方库ZeroMQ。

注解

  1. ZeroMQ(简称ZMQ)是一个基于消息队列的多线程网络库,其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字。
  2. ZMQ是网络通信中新的一层,介于应用层和传输层之间(按照TCP/IP划分),其是一个可伸缩层,可并行运行,分散在分布式系统间。
  3. ZMQ不是单独的服务,而是一个嵌入式库,它封装了网络通信、消息队列、线程调度等功能,向上层提供简洁的API,应用程序通过加载库文件,调用API函数来实现高性能网络通信。

实现的代码片段如下(注释参见源码64行处):

class Slot_serial(object):
    def __init__(self, address, port):
        self.port = port
        self.address = address
        self.context = zmq.Context()
        self.socket = self.context.socket(zmq.REQ)
        self.socket.connect("tcp://%s:%s" % (self.address, self.port))
        self.poller = zmq.Poller()
        self.poller.register(self.socket, zmq.POLLIN)

使用ZMQ能够和轻松的构建一个客户端-服务端架构。在代码片段中,使用了 zmq.Context() 创建了可供通信的上下文环境,然后使用 self.socket = self.context.socket(zmq.REQ) 创建了socket套接字, 之后就是使用 self.socket.connect("tcp://%s:%s" % (self.address, self.port)) 对指定的IP地址的端口进行连接,它们之间是建立的TCP/IP协议。所以传递的信息是完全安全并且不会重发的。

2. 显示框实时反馈软件操作

利用向函数传入信息,通过函数内部的判断并将需要显示的信息于显示框中显示。具体代码片段实现如下(注释参见源码228行处):

def con_serial_v(self, message):
    a = self.tabWidget.currentIndex()
    starttime = time.strftime("%H:%M:%S - ", time.localtime(time.time()))
    if a == 0:
        self.textBrowser_BB84_show.append('{0}{1}'.format(starttime, message))
        textcursor = QtGui.QTextCursor(self.textBrowser_BB84_show.textCursor())
        self.textBrowser_BB84_show.moveCursor(textcursor.atStart())
    elif a == 1:
        self.textBrowser_COW_show.append('{0}{1}'.format(starttime, message))
        textcursor = QtGui.QTextCursor(self.textBrowser_COW_show.textCursor())
        self.textBrowser_COW_show.moveCursor(textcursor.atStart())
    self.logger.logger.info(message)
    text = self.serialer.request_serial(message)
    try:
        text = unicode(text, 'gb2312')
        text = text.encode('utf8')
    except:
        pass
    endtime = time.strftime("%H:%M:%S - ", time.localtime(time.time()))
    if a == 0:
        self.textBrowser_BB84_show.append('{0}{1}'.format(endtime, text))
        textcursor = QtGui.QTextCursor(self.textBrowser_BB84_show.textCursor())
        self.textBrowser_BB84_show.moveCursor(textcursor.atStart())
    elif a == 1:
        self.textBrowser_COW_show.append('{0}{1}'.format(endtime, text))
        textcursor = QtGui.QTextCursor(self.textBrowser_COW_show.textCursor())
        self.textBrowser_COW_show.moveCursor(textcursor.atStart())
    self.logger.logger.info(text)
    return text

在上述代码中,使用了 self.textBrowser_BB84_show.append() 函数对信息进行实时显示。通过 textcursor = QtGui.QTextCursor(self.textBrowser_COW_show.textCursor()) 可以将显示框中的滑块随着反馈信息的更新进行自动下拉。

3. 诱骗态占比随机调节

实现诱骗态占比的随机调节是根据用户输入的数据进行不同的处理,相应处理方式是:

注解

  1. 输入的为0~100内的数值,那么会根据输入的数据对随机数范围进行标定
  2. 输入的为小于0或大于100的数值,那么显示框会反馈让用户重新输入数据
  3. 输入的是非数字字符,那么显示框会反馈让用户重新输入
  4. 不输入任何数值,那么会默认诱骗态占比为30%

实现的代码片段如下(注释参见源码260行):

def con_serial_zidingyi(self, message,flage=0):
    global ADDRESS
    a = self.tabWidget.currentIndex()
    if flage==0:
        starttime = time.strftime("%H:%M:%S - ", time.localtime(time.time()))
        if a == 0:
            self.textBrowser_zidingyi.append('{0}{1}'.format(starttime, message))
            self.textBrowser_zidingyi.moveCursor(textcursor.atStart())
        elif a == 1:
            self.textBrowser_zidingyi_cow.append('{0}{1}'.format(starttime, message))
            textcursor = QtGui.QTextCursor(self.textBrowser_zidingyi_cow.textCursor())
            self.textBrowser_zidingyi_cow.moveCursor(textcursor.atStart())
        text = self.serialer.request_serial(message)
        try:
            text = unicode(text, 'gb2312')
            text = text.encode('utf8')
        except:
            pass
        endtime = time.strftime("%H:%M:%S - ", time.localtime(time.time()))
        if a == 0:
            self.textBrowser_zidingyi.append('{0}{1}'.format(endtime, text))
            textcursor = QtGui.QTextCursor(self.textBrowser_zidingyi.textCursor())
            self.textBrowser_zidingyi.moveCursor(textcursor.atStart())
        elif a == 1:
            self.textBrowser_zidingyi_cow.append('{0}{1}'.format(endtime, text))
            textcursor = QtGui.QTextCursor(self.textBrowser_zidingyi_cow.textCursor())
            self.textBrowser_zidingyi_cow.moveCursor(textcursor.atStart())
    elif flage==1:
        path = '/'.join(ADDRESS.split('/')[0:-1])

        if not os.path.exists(path):
            os.makedirs(path)

        if a == 0:
            if '.txt' in ADDRESS.split('/')[-1]:
                ADDRESS = path + '/' + ADDRESS.split('/')[-1]
            else:
                time_end = time.strftime("-%Y-%m-%d-%H:%M:%S", time.localtime(time.time()))
                filename = 'BB84' + time_end + '.txt'
                ADDRESS = path + '/' + filename
            with open(ADDRESS, 'w') as f:
                pass
            self.textBrowser_BB84_show.append(u'{0}{1}\n目录文件设置成功:{2} '.format(time.strftime("%H:%M:%S - ", time.localtime(time.time())), message,ADDRESS))
            textcursor = QtGui.QTextCursor(self.textBrowser_BB84_show.textCursor())
            self.textBrowser_BB84_show.moveCursor(textcursor.atStart())
        elif a == 1:
            if '.txt' in ADDRESS.split('/')[-1]:
                ADDRESS = path + '/' + ADDRESS.split('/')[-1]
            else:
                time_end = time.strftime("-%Y-%m-%d-%H:%M:%S", time.localtime(time.time()))
                filename = 'COW' + time_end + '.txt'
                ADDRESS = path + '/' + filename
            with open(ADDRESS, 'w') as f:
                pass
            self.textBrowser_COW_show.append(u'{0}{1}\n目录文件设置成功:{2} '.format(time.strftime("%H:%M:%S - ", time.localtime(time.time())), message,ADDRESS))
            textcursor = QtGui.QTextCursor(self.textBrowser_COW_show.textCursor())
            self.textBrowser_COW_show.moveCursor(textcursor.atStart())
        return 1
    return text

4. 参数查询和设置

在这部分,是利用pyqt中的槽和信号的绑定来实现的,通过点击按钮就可以执行装饰器装饰的函数,显示或者设置相应的参数。 通过这样的方式可以很轻松的复用多个按钮。实现的代码片段如下(注释参见源码329行处):

@QtCore.pyqtSignature("")
def on_pushButton_range_clicked(self):
    range = unicode(self.lineEdit_qrng_range.text(), 'utf8', 'ignore').encode('gb2312')
    if not range == '':
        try:
            answer = self.con_serial(float(range))
        except ValueError:
            a = u'诱骗态占比设置:' + range
            anster = self.con_serial(a)
    else:
        answer = self.con_serial(30)

这里通过调节诱骗态占比的按钮举例,通过装饰器 @QtCore.pyqtSignature("") 可以对下面的函数进行装饰,装饰后的函数就具有了槽和信号的功能(注意:被装饰的函数的函数名是具有固定的写法)。 这样,点击相应的按钮后就能够执行对应的函数,在不同的函数中执行不同的操作就可以实现不同的功能。

5. 实现随机数的实时加载显示

实现这个功能所需要的操作是把执行UI界面的程序逻辑和执行随机数实时加载的程序逻辑进行分开处理,所以这里用到了多线程的形式。 使用多线程能够避免两个逻辑在同时执行时发生冲突,也可以实时显示信息,具体的代码片段如下(注释参见源码678行处):

@QtCore.pyqtSignature("")
def on_pushButton_BB84_clicked(self):
    self.bb84_1 = threading.Thread(target=self.BB84_clicked, name='')
    self.bb84_1.setDaemon(True)
    self.bb84_1.start()

在上述代码中,首先使用 threading.Thread() 函数创建一个子线程,此时子线程还未执行。当执行 self.bb84_1.start() 后,该子线程得到命令,开始执行。 直到主线程退出或者主动结束子线程前,它会一直运行。这也正好满足我们需要实时下发随机数的要求。

6. 实现下发随机数的暂停和继续操作

实现本操作中,所使用的是子线程的开始以及结束,暂停实则就是结束此处下发的子线程,继续就是重新创建子线程并开始下发。具体的代码片段如下所示(注释参见源码696行处):

@QtCore.pyqtSignature("")
def on_pushButton_BB84_interact_clicked(self):
    a = self.tabWidget.currentIndex()
    def _async_raise(tid1, exctype=SystemExit):
        tid = ctypes.c_long(tid1)
        if not inspect.isclass(exctype):
            exctype = type(exctype)
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
        if res == 0:
            raise ValueError("invalid thread id")
        elif res != 1:
            ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
            raise SystemError("PyThreadState_SetAsyncExc failed")
    _async_raise(self.bb84_1.ident)
    if a == 0:
        self.textBrowser_BB84_rand_show.append(u'\n下发已暂停!\n')
    elif a == 1:
        self.textBrowser_COW_rand_show.append(u'\n下发已暂停!\n')

在代码片段中,当按下暂停按钮,就会执行该装饰器装饰的函数,在该函数中使用了python中的ctype库强制结束了子线程,如果需要继续下发时,则再次执行功能5中所述的操作即可。 本功能巧妙的使用结束子线程的操作来模仿暂停的操作。

7. 自定义诱骗态位置信息文件存储的目录地址

本功能使用了python中的os库,对目录和文件夹进行操作。它可以创建或者删除指定的目录或者文件。代码片段如下(注释参见源码497行处):

@QtCore.pyqtSignature("")
def on_pushButton_address_clicked(self):
    global ADDRESS
    ADDRESS = str(unicode(self.lineEdit_address.text(), 'utf8', 'ignore').encode('gb2312'))
    anster = self.con_serial_zidingyi(ADDRESS,1)

在代码片段中,调用了 self.con_serial_zidingyi(ADDRESS,1) 函数,在该函数中,对目录结构和文件夹进行了处理。对于用户输入不同的文件信息进行不同的处理,具体的处理方式如下:

注解

  1. 用户输入正确的目录文件名,显示框反馈目录设置成功,并成功创建
  2. 用户输入错误的目录文件名,显示框反馈信息提示用户重新输入
  3. 用户不输入目录文件名,显示框会反馈软件自动创建的文件目录地址