具体功能:
读取一个带有SN号信息的.elsx表,可以根据CCID通讯的终端设备,匹配特定SN号段的相关信息,后根据与终端自定的协议发送.elsx表中设备SN号段中所有配置信息,方便多个设备终端录入配置信息
总结:
俩个点:
1. TK界面 列表框 和 文本标签,可以设置一个tk.StringVar()类,然后 可以在另一个线程中调用类的set()方法,即可改变tk标签上的显示的内容
varlistb = tk.StringVar()
listb = tk.Listbox(root, listvariable=varlistb, height=20, width=50, yscrollcommand=sb.set) # 创建两个列表组件
var = tk.StringVar() # 将label标签的内容设置为字符类型,用var来接收hit_me函数的传出内容用以显示在标签上
l = tk.Label(root, textvariable=var, font=('Arial', 12), width=40, height=5)
2. 打包成.exe文件时, 使用 pyinstaller -F 时,运行显示 win系统找不到PySmartCard文件,所以打包的时候需要自己拼装相应的文件
#命令语法:pyinstaller -F 文件名(带后缀py)
#常用参数说明:
#–icon=图标路径
#-F 打包成一个exe文件
#-w 使用窗口,无控制台
#-c 使用控制台,无窗口
#-D 创建一个目录,里面包含exe以及其他一些依赖性文件
使用 pyinstaller -F -D xxxx.py,运行后再运行.exe,缺失什么文件,可以直接拷贝至.exe文件夹中即可
缺陷:
线程之前切换时,没有使用互斥锁,后期可以优化一下
源代码:
import xlrd
import tkinter as tk
import time
from PySmartCard.CpuCard import PcscReader
import threading
import os
#根据函数生成索引表
def Get_QR_ListData(table, i):
return table.row_values(i)
#得到各行数据
def Creat_QR_Data(suoyingList, Hang_data):
i = 0
Message = ''
for StrMessage in suoyingList:
if str(Hang_data[i]) != '':
Message += StrMessage + '=' + str(Hang_data[i]) + '&'
i = i + 1
return (Message.encode('GBK'))
#根据第几行生成对应的二维码
def Create_Photo(table,Meessage):
global packDATA
#print(Creat_QR_Data(Get_QR_ListData(table, 0), Get_QR_ListData(table, i)))
num = table.nrows
recv_list = []
for i in range(1, num):
if Meessage == 'SN':
continue
if Meessage in Get_QR_ListData(table, i):
adu = Creat_QR_Data(Get_QR_ListData(table, 0), Get_QR_ListData(table, i))
packDATA = adu
#指令数据处理
def CCID_Data(adu):
global ucMode
if len(adu) > 1000:
print("数据长度不能大于1000字节")
exit()
#指令头
AT_Hear = 'F0 2F 51 '
#数据地址
AT_Hear_Adder = 16
Pack_div_len = 252
#包数据
Pack_list = ['a1', 'a2', 'a3', 'a4']
Pack_list.append(hex(int(len(adu) / Pack_div_len)).replace('0x', ''))
Pack_list.append(hex(len(adu) % Pack_div_len).replace('0x', ''))
#有效数据
for i in adu:
Pack_list.append(hex(i).replace('0x', ''))
for i in range(0, int(len(Pack_list)/Pack_div_len) + 1):
MessPackData = ''
if i != int(len(Pack_list)/Pack_div_len):
for MessBuf in Pack_list[i*Pack_div_len:(i+1)*Pack_div_len]:
if len(MessBuf) == 1:
MessPackData += '0'
MessPackData += str(MessBuf)
else:
for MessBuf in Pack_list[i*Pack_div_len:]:
if len(MessBuf) == 1:
MessPackData += '0'
MessPackData += str(MessBuf)
Message = AT_Hear + str(hex(AT_Hear_Adder + i).replace('0x', '')) \
+ hex(int(len(MessPackData)/2)).replace('0x', '') + MessPackData
recv_list = []
sendApdu(pcsc, Message, recv_list, 2)
if recv_list[1] != '9000':
var.set('CCID下载失败') # 为label设置值
var.set('参数下载成功')
time.sleep(2)
ucMode = 0
def g():
global packDATA
CCID_Data(packDATA)
def showLog(data, issend):
'''打印日志函数'''
current = time.strftime('%Y_%m_%d %H:%M:%S', time.localtime())
if issend: # 发送到卡片的数据
send = '-->'
else: # 卡片返回的数据
send = '<--'
print("{} {} {}".format(current, send, data))
def sendApdu(reader, apdu, recv_list, readerType=None):
'''给卡片发送指令的函数'''
# 清空
recv_list[:] = []
# 去掉多余的空格
apdu = apdu.replace(' ', '')
#showLog(apdu, True)
respone = reader.send_apdu(apdu, readerType)
#showLog(respone, False)
# recv_list第1个元素是返回数据
recv_list.append(respone[:-4])
# recv_list第2个元素是sw
recv_list.append(respone[-4:])
def SendCCidMeessage():
'''获取所有的读卡器列表'''
readerName = pcsc.get_pcsc_readerlist()
##print(readerName)#字符串类型的读卡器名称,按;隔开
# 按;切割字符串,得到所有的读卡器名称
#print('当前连接的读卡器有:')
readerNameList = readerName.split(';')
for i in range(len(readerNameList) - 1):
#print("{} {} :{}".format('reader', i, readerNameList[i]))
pass
'''连接指定的读卡器'''
#print('*' * 40)
# 要连接的读卡器
useReaderName = "TianYu ANYPAY 0"
# 得到的是字符串类型的复位信息
ATR = pcsc.connect_device(useReaderName)
if ATR:
#print("ConnectDevice Success...")
#print("ATR: ", ATR)
return 1
else:
#print("ConnectDevice Failed!")
#print("设备连接失败,请检查设备")
return 0
#线程操作
def autoCaozuo():
global pcsc, SN, root, ucMode, table
while 1:
if ucMode == 0:
# '''实例化PCSC读卡器'''
pcsc = PcscReader()
#print(pcsc)
if SendCCidMeessage() != 1:
#print('设备请先进入维护模式')
varlistb.set('')
var.set('请先连接终端') # 为label设置值
ucMode = 7
else:
ucMode = 1
elif ucMode == 1:
pcsc = PcscReader()
SendCCidMeessage()
# 通过索引顺序获取
if '商户信息配置表.xlsx' not in os.listdir(os.getcwd()):
var.set('缺少商户信息配置表.xlsx')
time.sleep(3)
ucMode = 0
else:
data = xlrd.open_workbook('商户信息配置表.xlsx')
table = data.sheet_by_index(0)
# '''读SN号'''#
recv_list = []
SN = ''
sendApdu(pcsc, 'FE 01 01 06 00', recv_list, 2)
if recv_list[1] == '9000':
LenSn = int(recv_list[0][2:4]) * 2
SN = str(recv_list[0][4:4 + LenSn])
# 找SN列的
i = 0
# 遍历0行 找SN列数的
for Mesage in table.row_values(0):
if 'SN' == Mesage:
break
i += 1
# SN列中对应的SN号
a = 0
for Message in table.col_values(i):
a += 1
if SN == Message:
#print('SN号段匹配成功!')
var.set('')
Message_list = []
j = 0
# print('设备SN号: ' + SN)
for title in table.row_values(0):
Message_list.append(title + ':' + table.row_values(a-1)[j])
j += 1
varlistb.set(Message_list)
Create_Photo(table, SN)
ucMode = 2
break
else:
if a == table.nrows:
ucMode = 0
var.set('未找到该设备对应的信息') # 为label设置值
break
elif ucMode == 7:
ucMode = 0
else:
time.sleep(1)
pass
def auto_destroy():
global ucMode
while 1:
time.sleep(1)
if ucMode == 2:
g()
elif ucMode == 7:
ucMode = 0
else:
time.sleep(1)
pass
def CheckActive():
while 1:
if True != a.isAlive() or True != b.isAlive():
a.join(1)
b.join(1)
var.set('工具异常,请检查运行环境')
time.sleep(5)
root.destroy()
exit(0)
if __name__ == '__main__':
pcsc = []
SN = ''
ucMode = 0
xixi = 1
root = []
packDATA = ''
root = tk.Tk() # 创建窗口对象的背景色
sb = tk.Scrollbar(root) # 垂直滚动条组件
sb.pack(side=tk.RIGHT, fill=tk.Y) # 设置垂直滚动条显示的位置
varlistb = tk.StringVar()
listb = tk.Listbox(root, listvariable=varlistb, height=20, width=50, yscrollcommand=sb.set) # 创建两个列表组件
listb.pack() # 将小部件放置到主窗口中
var = tk.StringVar() # 将label标签的内容设置为字符类型,用var来接收hit_me函数的传出内容用以显示在标签上
l = tk.Label(root, textvariable=var, font=('Arial', 12), width=40, height=5)
l.pack()
threadLock = threading.Lock()
a = threading.Thread(target=autoCaozuo)
b = threading.Thread(target=auto_destroy)
c = threading.Thread(target=CheckActive)
a.start()
b.start()
c.start()
root.mainloop()