03_软件实现功能详解¶
本部分将详细介绍,在软件代码的编写工程中所实现的各种功能,并给出实现功能的代码片段
1. 与硬件的通信¶
该功能是使用python中的第三方库ZeroMQ。
注解
- ZeroMQ(简称ZMQ)是一个基于消息队列的多线程网络库,其对套接字类型、连接处理、帧、甚至路由的底层细节进行抽象,提供跨越多种传输协议的套接字。
- ZMQ是网络通信中新的一层,介于应用层和传输层之间(按照TCP/IP划分),其是一个可伸缩层,可并行运行,分散在分布式系统间。
- 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. 诱骗态占比随机调节¶
实现诱骗态占比的随机调节是根据用户输入的数据进行不同的处理,相应处理方式是:
注解
- 输入的为0~100内的数值,那么会根据输入的数据对随机数范围进行标定
- 输入的为小于0或大于100的数值,那么显示框会反馈让用户重新输入数据
- 输入的是非数字字符,那么显示框会反馈让用户重新输入
- 不输入任何数值,那么会默认诱骗态占比为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) 函数,在该函数中,对目录结构和文件夹进行了处理。对于用户输入不同的文件信息进行不同的处理,具体的处理方式如下:
注解
- 用户输入正确的目录文件名,显示框反馈目录设置成功,并成功创建
- 用户输入错误的目录文件名,显示框反馈信息提示用户重新输入
- 用户不输入目录文件名,显示框会反馈软件自动创建的文件目录地址