python+js实现NFC读卡桌面程序

722 阅读4分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。

前言

hi,大家好,项目原因不得不要开发一套python读卡程序(卑微前端瑟瑟发抖),并要求是exe桌面应用并解析读卡后数据和发送指令,之前也试过electron创建桌面程序,后经发现打包完成后读取不到读卡后的数据,最后使用pywebivew打包桌面端,下面跟大家分享我的开发历程:

前期准备

1.安装python环境

python环境建议使用3.0以上版本(2.0和3.0版本还是有很大不同的) 打开python官网,找到下载地址www.python.org/downloads/ 下载需要的版本,目前我用的是3.6版本 www.python.org/downloads/r…

image.png x-86是32位,x86-64是64位。 我下载的是Windows x86-64 web-based installer版本

image.png

双击点开安装,勾选Add Python 3.6 to PATH将python加到widows变量中,选择自定义安装;

点击下一步全部打勾(不要好奇,全打上没有坏处);

点击下一步勾选一下选项;

image.png

安装中...

image.png

最后测试下是否安装成功 打开命令行工具cmd,输入python -v

image.png

2.安装pywebview

命令行内输入 pip install pywebview

读卡解析模块

1.引入smartcard读卡模块 (智能卡操作库)

smartcard是python的插件,调用smartcard里的CardType可以获取智能卡的类型,CardRequest里的CardRequest智能卡的发送信息模块,toHexString转换十六进制的方法。

from smartcard.CardType import AnyCardType
from smartcard.CardRequest import CardRequest
from smartcard.util import toHexString
from smartcard.ATR import ATR

2.需要对读卡模块进行初始化,相当于是启动准备读取的步骤。

通过AnyCardType方法获取智能卡的类型,CardRequest发送信息模块需要配置timeout和智能卡的类型,最后等待反馈...

# 初始化读卡器
def init():
  card_type = AnyCardType()
  card_request = CardRequest(timeout=500, cardType=card_type)
  card_service = card_request.waitforcard()
  card_service.connection.connect()
  atr = ATR(card_service.connection.getATR())
  return card_service

3.发送指令模块设置

发送指令需要等读取成功后发送指令,调用readId需要传入对应的self参数且不能为空 card_service.connection.transmit(command),最终会反馈三个参数,res1、2、3。

def trace_command(apdu):
    logging.info('sending: ' + toHexString(apdu))


def trace_response(response, sw1, sw2):
  if response is None:
    response = []
  print(
    'serial no.: ',
    toHexString(response),
    ' status words: ',
    "%x %x" % (sw1, sw2)
  )
  logging.info('serial no.: ' +
               toHexString(response) +
               ' status words: ' +
               "%x %x" % (sw1, sw2))
def bytes2hexstr(data_bytes):
  data_number = len(data_bytes)
  data_hex_str = ''
  for i in range(0, data_number):
    data_int = int(str(data_bytes[i]))
    data_hex_str += "{:02X} ".format(data_int)
  return data_hex_str

#发送指令
def sendCommand(card_service,command):
    trace_command(command)
    res, s1, s2 = card_service.connection.transmit(command)
    trace_response(res, s1, s2)
    return res,s1,s2


def readId(card_service):
  rf_command_bytes = [0xFF,0xCA,0x00,0x00,0x00]
  res,s1,s2 = sendCommand(card_service,rf_command_bytes)
  return bytes2hexstr(res)

4.读取并发送指令

只要是读取成功便发送指令(我这边发送的是清空指令),使用sendCommand方法发送数据方法内可传入两个参数

rf_command_bytes数据的格式

def readId(card_service):
  rf_command_bytes = [0xFF,0xCA,0x00,0x00,0x00]
  res,s1,s2 = sendCommand(card_service,rf_command_bytes)
  return bytes2hexstr(res)

设置界面信息并用js调用

1.引入webview

import webview

2.设置桌面信息

桌面信息可以自定义设定,并开发api类方便js调用,webview.create_window是webview创建窗口的方法,方法内传入名称,url,窗口大小width,height,api地址

def read_uid(self):
    uid = readId(init())
    return uid
if __name__ == '__main__':
    window = webview.create_window('test',url='./client/index.html',width=1500,height=900, js_api=api)
    webview.start(debug=True)

js调用python方法

1. html文件加载完成后 监听pywebviewready

查看是否调用成功可以通过js方法 window.addEventListener,传入pywebviewready,查看状态,这一步至关重要。

window.addEventListener('pywebviewready', function () {
  alert('程序连接成功')
},true)

2.程序连接成功后,使用pywebview调用python的方法

前边咱们创建窗口的时候js_api的名字为api,js内可直接通过pywebview.api.xx调用

pywebview.api.readId().then(result=>{
    alert('调用成功')
}).catch(err=>{
    alert('调用失败')
}) 

打包

推荐大家使用 auto-py-to-exe 这是有可视化界面打包过程更加流程

1.安装 auto-py-to-exejiaoban

cmd打开终端 输入 pip install auto-py-to-exe

pip install auto-py-to-exe

2.打包

脚本位置python文件地址,推荐大家使用单目录,单文件打包集成不是很好,图标可以自行配置,附加文件是整个开发文件的目录,路径要设置准确,完成后点击开始转换

image.png

总结

从0到1,从不会到会小有成就感,网上的例子也蛮多,也参考不少,但是python+js结合感觉很少,特此跟大家分享,谢谢!