读卡器的状态机, python实现

1. 问题的提出,及状态机简介

ZHUMAO整了个门禁用的读卡器,比以前那种更好,不需要发指令就能读,只要刷卡,读卡器就向串口上写数据。仍然是串口的,还是韦根协议。"刷卡就向上写"避免了轮询读卡器,效率更高,代码也容易了。不过,也造成一个问题。下发命令,然后轮询读的模式下,如果在串口线上只有一个读卡器,不需要对输入的数据特别检验和处理,接收到的数据一定是对的,按协议读入多少个字符,然后按偏移量取有效的部分就行了。"刷卡就向上写"的模式,需要保证对齐,必须从刷卡后产生的第一字符开始读,读到最后一个,要避免从中间读起。如果中间出现了噪音之类的干扰,由于不能下发指令要求对齐
(或者开始读),就再也找不到开始位置,数据全乱。

解决这个问题的方案是状态机。

状态机是个著名的数学模型,在数字电路、编译原理、面向对象系统分析与设计、形式语言与状态机中都有提及。状态机效率很不错,刘典同学曾经用状态机模型写程序参加过CSDN上的比赛,检验IP地址是否合法,获得过第二名。状态机描述问题清晰,邦哥和亮哥曾经用状态机重写安卓程序的界面部分,把原来"朴素"方法可能出现的BUG都去除了。

正确的思考方法是有效的工具,在解决问题中非常重要。人类通常不懈于在猛兽面前炫耀速度和力量,而是使用弩箭和陷阱。所以,工具对于成为人类多么重要。所以,不用状态机,而依靠单纯的智力是多么愚蠢。

2. 问题描述

该型号读卡器上传的数据看起来是这样的,以十六进制表示,"02 XX XX XX XX 0d 0a 03"。其中的4个 XX 表示卡号,是我们感兴趣的部分。其余的部分必须匹配,才能说明读卡器正常工作,卡号有效。

主程序准备写成这样:

1 if __name__ == ‘__main__‘:

2     s = state_machine()

3     t = serial.Serial(‘COM3‘)

4     while True:

5         str = s.go(t)

6         print str.upper()

其中第2行初始化一个 state_machine 类的实例。在第4行开始的循环中,每次迭代都调用 s.go(t),把串口传给状态机。在状态机的go中,读很多次串口,直到遇到一次完整有效的卡号,作为返回值,在第6行中打印出来。这个串口t如果改为在 state_machine 的构造函数中,会更好一些。不过我对python语法不熟,大部分时间都消耗在查语法手册上了,头昏眼花,当时就写成了这样。

当然,真实程序的目标不是打印卡号,而是用卡号作为检索条件,去数据库里查询和更新一些数据。

3. 状态机

开始写状态机,才发现 python 居然没有 switch-case。可见我对语法得多么不熟。

根据 "02 XX XX XX XX 0d 0a 03",状态转换如图所示。状态图中的关键是,我在哪个状态,接收到哪个消息后,会迁移到哪个状态,在迁移的过程中,会做哪些动作。

4. 状态机代码解释

代码如附录A所示。state_machine 是一个类 类型。

成员变量,count用于计数,在0x00状态 (及0x02状态) 一共接收了几个字符,这些字符应该添加到有效卡号 (成员变量ret)的末尾。成员变量state,是当前的状态,其初始状态是 0xff。

成员函数 str2hex 是从zhumao那里抄来的,用于把二进制转为十六进制文本形式。

成员函数 go (self, ser) 是核心部分。每次调用 go,它会从串口读入一个字符,并把这个字符作为发送给状态机的消息;状态机根据自己的 (1) 当前状态 state,然后再根据这个 (2) 消息 c,判定 (3)应该迁移到哪个状态, (4)应在迁移时做哪些动作。

在状态图中,(1) 当前状态标记为椭圆,箭尾所指的那个椭圆, (2)消息,标记为线上的文字,斜线"/"左边的部分, (3)应该迁移到哪个状态,箭头所指的那个椭圆, (4)在迁移时做的动作,标记为线上的文字,斜线右边的部分。

成员函数 go,一旦读到的字符可以拼成一个有效的卡号 (包括0x03也已读入),就给出卡号作为返回值,退出 go 函数,控制权转交回主函数。如果尚未形成有效的卡号,就继续在 go 里面转。

如果你想测试,还没有找到读卡器。那么把第12行改成从一个二进制文进中读入。

附录A 状态机代码

1 class state_machine:

2     count = 0

3     state = 0xff # 02 XX XX XX XX 0d 0a 03

4     ret = ""

5     def str2hex(self, c):

6         hvol = ord(c)

7         hhex = ‘%02x‘%hvol

8         return hhex

9

10     def go(self, ser):

11         while True:

12             c = ser.read(1)

13             c = self.str2hex(c)

14             # print self.state

15             # print c

16             # print self.ret

17             # print

18             if self.state == 0xff:

19                 if c == ‘02‘:

20                     self.state = 0x02

21                     self.ret = ""

22                     continue

23             if self.state == 0x02:

24                 self.state = 0x00

25                 self.count = 0

26                 self.count=self.count+1

27                 self.ret = self.ret + c

28                 continue

29             if self.state == 0x00:

30                if self.count<4:

31                     self.count=self.count+1

32                     self.ret = self.ret + c

33                     self.state = 0x00

34                     continue

35                else:

36                     if c == ‘0d‘:

37                         self.state = 0x0d

38                         continue

39                     else:

40                         self.state = 0xff

41                         self.ret = ""

42                         continue

43             if self.state == 0x0d:

44                     if c == ‘0a‘:

45                         self.state = 0x0a

46                         continue

47                     else:

48                         self.state = 0xff

49                         continue

50             if self.state == 0x0a:

51                     if c == ‘03‘:

52                         self.state = 0x03

53                         self.state = 0xff

54                         return self.ret

55                     else:

56                         self.state = 0xff

57                         continue

58             else:

59                 continue

附录 B 状态机图示的源代码 in graphviz

digraph state

{

graph [ nodesep=1.2]

start -> "0xff" ;

"0x00" [color=red];

"0xff" -> "0x02" [label="0x02"];

"0xff" -> "0xff" [label="不是0x02"];

"0x02" -> "0x00" [label="任意字符 / (count=1,该字符填入卡号末尾)", color=red, fontcolor=red];

"0x00"-> "0x00" [label="任意字符 & count<4 / (count+=1,该字符填入卡号末尾)", color=red, fontcolor=red];

"0x00" -> "0x0d" [label="0x0d & count>=4"];

"0x00" -> "0xff" [label="不是0x0d & count>=4"];

"0x0d" -> "0x0a" [label="0x0a"];

"0x0d" -> "0xff" [label="不是0x0a"];

"0x0a" -> "0x03" [label="0x03"];

"0x0a" -> "0xff" [label="不是0x03"];

"0x03" -> "0xff" [label="无条件"];

}

--------------------

博客会手工同步到以下地址:

[http://giftdotyoung.blogspot.com]

[http://blog.csdn.net/younggift]

=======================

读卡器的状态机, python实现,布布扣,bubuko.com

时间: 2024-10-12 03:29:51

读卡器的状态机, python实现的相关文章

Python高级特性:迭代器和生成器 -转

在Python中,很多对象都是可以通过for语句来直接遍历的,例如list.string.dict等等,这些对象都可以被称为可迭代对象.至于说哪些对象是可以被迭代访问的,就要了解一下迭代器相关的知识了. 迭代器 迭代器对象要求支持迭代器协议的对象,在Python中,支持迭代器协议就是实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身:next()方法返回容器的下一个元素,在结尾时引发StopIteration异常. __iter__()和next()

Python多线程入门指南

一直懒得写Python相关的文章,恰好有天需要简单的给童鞋们讲点课,仓促之余就诞生了此文. 今天本来准备全面的聊聊有关高性能并发这个话题来着,但是周末马上要来了啊.所以我就取了其中的一点来介绍,关于其他的方面,有兴趣的小伙伴可以和我交流.谈高效并发,往往脱离不了以下三种方案: 进程:每个逻辑控制流都是一个进程,由内核来调度和维护.因为进程有独立的虚拟地址空间,想要和其他控制流通信必须依靠显示的进程间通信,即我们所说的IPC机制 线程:线程应该是我们最为熟知的.它本质是运行在一个单一进程上下文中的

200行Python代码实现2048

200行Python代码实现2048 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序: LX终端(LXTerminal): Linux命令行终端,打开后会进入Bash环境,可以使用Linux命令 GVim:非常好用的编辑器,最简单的用法可以参考课程Vim编辑器 3. 环境使用 使用GVim编辑器输入实验所需的代码及文件,使用LX终端(LXTerminal)运行所需命令进行操

Python的迭代器和生成器

先说迭代器,对于string.list.dict.tuple等这类容器对象,使用for循环遍历是很方便的就,在后台for语句对容器对象对象调用iteration()函数,这是python的内置函数,iter()会返回一个定义next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数.在没有后续元素是,调用next()会抛出一个StopIteration异常 上面说的都是python自带的容器对象,它们都实现了相应的迭代器方法,自定义类的遍历怎么实现,方法是

练习PYTHON之EVENTLET

以下是重点,要会运用: eventlet是一个用来处理和网络相关的python库函数,而且可以通过协程来实现并发,在eventlet里,把“协程”叫做 greenthread(绿色线程).所谓并发,就是开启了多个greenthread,并且对这些greenthread进行管理,以实现非阻塞式的 I/O.比如说用eventlet可以很方便的写一个性能很好的web服务器,或者是一个效率很高的网页爬虫,这都归功于eventlet的“绿色线程”,以及对“绿色线程”的管理机制.更让人不可思议的是,even

Python 中的进程、线程、协程、同步、异步、回调

进程和线程究竟是什么东西?传统网络服务模型是如何工作的?协程和线程的关系和区别有哪些?IO过程在什么时间发生? 在刚刚结束的 PyCon2014 上海站,来自七牛云存储的 Python 高级工程师许智翔带来了关于 Python 的分享<Python中的进程.线程.协程.同步.异步.回调>. 一.上下文切换技术 简述 在进一步之前,让我们先回顾一下各种上下文切换技术. 不过首先说明一点术语.当我们说"上下文"的时候,指的是程序在执行中的一个状态.通常我们会用调用栈来表示这个状

树莓派2代B model 上手初体验,不用显示器,Python GPIO 点亮一颗LED

开题:[好东西,值得研究!] 标题:树莓派2代B model 上手初体验,不用显示器,Python GPIO 点亮一颗LED [知识普及] 1,树莓派各版本对比: 2,树莓派2代BModel 主板,图样 树莓派2 代B GPIO 图 [所需硬件] 一张TF卡,8G或者8G以上,我的是 [三星TF卡16g class10 EVO] 一根网线,让树莓派与路由器连接 一个5V 500MA 的普通USB电源,为树莓派供电 ,我试过了,5V 500ma没问题 一个树莓派2代B 一个普通路由器[如果你连路由

[python]--迭代器,生成器补充

在python中,list,string,dict都是可迭代对象,可以通过for语句遍历. 迭代器 迭代器对象要求支持迭代器协议的对象,在python中,支持迭代器协议就算实现对象的__iter__()和next()方法.其中__iter__()方法返回迭代器对象本身; next()方法返回容器的下一个元素,在结尾时引发StopIteration异常 __iter__()和next()方法 这两个方法是迭代器最基本的方法,一个用来获得迭代器对象,一个用来获取容器中的下一个元素. 对于可迭代对象,

【转载】如何系统地自学 Python?

原文:如何系统地自学 Python? 作者:彭猫 本文由 知乎 彭猫 授权发布,版权所有归作者,转载请联系作者! 是否非常想学好 Python,一方面被琐事纠缠,一直没能动手,另一方面,担心学习成本太高,心里默默敲着退堂鼓? 幸运的是,Python 是一门初学者友好的编程语言,想要完全掌握它,你不必花上太多的时间和精力. Python 的设计哲学之一就是简单易学,体现在两个方面: 语法简洁明了:相对 Ruby 和 Perl,它的语法特性不多不少,大多数都很简单直接,不玩儿玄学. 切入点很多:Py