目录
(1)communication.py
(2)Reader.py
(3)Tag.py
实验1—— EPC C1G2标准下的标签状态转换仿真
实验说明: 利用Python或Matlab模拟C1G2标签的状态转换模型; 程序应能显示标签当前的状态,并能通过键入的不同指令完成状态的转换。
文件结构如下:
(1)communication.py
import sys
from Tag import Tag
from Reader import Reader
def validACK(tagIndex):
print('有效的的ACK RN16,tag返回EPC,标签变为Acknowledged状态')
tagArray[tagIndex].setState('Acknowledged')
return tagArray[tagIndex].returnEPC()
def invalidACK():
print('无效的ACK RN16,no reply')
def returnHandle(tagIndex):
tagArray[tagIndex].setState('Open')
handle = tagArray[tagIndex].returnHandle()
print('tag返回handle:{},进入{}状态,交出钥匙,接受指令'.format(handle, tagArray[tagIndex].state))
return handle
def access(command):
if command == 'read':
cmd = reader.read(tagArray[tagIndex])
elif command == 'write':
cmd = reader.write(tagArray[tagIndex])
elif command == 'lock':
cmd = reader.lock(tagArray[tagIndex])
elif command == 'kill':
cmd = reader.kill(tagArray[tagIndex])
return cmd
# tagNum是标签的数目,tagArray用以存储所有标签
tagNum = int(input('please10 input the number of Tag: '))
Q = int(input('please input the Q to decide the maxNum of slot: '))
tagArray = []
tagIndex = 0
for i in range(tagNum):
tag = Tag()
tag.endPoint = (2 ** (Q - 1))
tagArray.append(tag)
reader = Reader()
print('*****************开始通信*****************')
print('\n*****************进入选择阶段*****************')
selectChoice = int(input('输入0选择全部标签,输入1选择奇数号标签,输入2选择偶数号标签: '))
reader.select(tagArray, selectChoice)
print('\n*****************进入盘存阶段*****************')
ans, tagIndex = reader.Query(tagArray)
while ans is None: # ans非空时代表slot值为0 tag返回一个RN16 此时ans=RN16
ans, tagIndex = reader.QueryRep(tagArray)
else:
ACKRN16 = reader.ACK(ans)
print('ACK返回RN16,判断是否有效进入下一步')
if ACKRN16 == ans:
EPC = validACK(tagIndex)
print('\n*****************进入访问阶段*****************')
NewRN16 = reader.Req_RN(EPC, ACKRN16)
if NewRN16 == ACKRN16:
print('收到有效的RN16,tag将返回handle(新的16位随机数)')
handle = returnHandle(tagIndex)
command, returnhandle = reader.Command(handle)
if returnhandle == handle:
cmd = access(command)
while cmd:
command, returnhandle = reader.Command(handle)
cmd = access(command)
else:
sys.exit(0)
else:
print('收到无效的RN16,no reply')
else:
invalidACK()
(2)Reader.py
import random
from Tag import Tag
class Reader:
def __init__(self):
self.readerID = self.setReaderID()
self.crush = 0
self.noRespond = 0
self.Q = 2
# 给每一个阅读器设置一个唯一的16位ID
def setReaderID(self):
str = ''
ID = str.join(random.choice('0123456789') for i in range(16))
return ID
# 定义阅读器的标签选择规则 tag=0 选择所有标签 tag=1 选择奇数标签 =2选择偶数标签
def select(self, tagArray, tag=0):
if tag == 0:
for i in range(len(tagArray)):
tagArray[i].selected = True
print('select阶段, 所有标签全部被选中')
elif tag == 1:
for i in range(1, len(tagArray), 2):
tagArray[i].selected = True
print('select阶段, 奇数号标签全部被选中')
else:
for i in range(0, len(tagArray), 2):
tagArray[i].selected = True
print('select阶段, 偶数号标签全部被选中')
def QueryTagState(self, tagArray):
cnt, index = 0, 0 # cnt为此刻slot值为0的标签数目,index为可得到回应的标签
for i in range(len(tagArray)):
if tagArray[i].slot == 0:
cnt += 1
index = i
if cnt > 1:
print('多个tag回应, 发生碰撞, 无法识别, 为Arbitrate状态')
self.crush += 1
# 碰撞次数大于随即数右端点的一半
if self.crush > Tag.endPoint / 2:
self.QueryAdjust()
return None, index
elif cnt == 1:
tagArray[index].setState('Reply')
print('{}号tag,其ID为{},slot值为0, 回应reader的随机数为{}, tag进入{}状态'.format(index, tagArray[index].id,
tagArray[index].returnRN16(),
tagArray[index].state))
return tagArray[index].returnRN16(), index
else:
print('无tag回应, 均为Arbitrate状态')
self.noRespond += 1
# 无响应次数大于随即数右端点的一半
if self.noRespond > Tag.endPoint / 2:
self.QueryAdjust()
return None, index
# 阅读器发出查询Query命令,被选择的标签开始置随机数slot
def Query(self, tagArray):
print('********发出第一次Query********')
for i in range(len(tagArray)):
if tagArray[i].selected == True:
tagArray[i].setSlot()
tagArray[i].setState('Arbitrate')
print('{}号tag,状态为{}'.format(i, tagArray[i].state))
tag, index = self.QueryTagState(tagArray)
return tag, index
def QueryRep(self, tagArray):
print('********发出QueryRep, 各标签slot值减1, 或恰好有标签响应********')
for i in range(len(tagArray)):
if tagArray[i].selected == True:
tagArray[i].reduceSlot()
tag, index = self.QueryTagState(tagArray)
return tag, index
def QueryAdjust(self, tagArray):
print('********发出QueryAdjust, 根据碰撞次数和未响应次数调整slot阈值********')
tagNum = len(tagArray)
if self.noRespond > tagNum * 2:
print('等待响应时间过长, {}轮未得到响应, 下调slot阈值'.format(self.noRespond))
for i in range(tagNum):
tagArray[i].adjustSlot(tag=False)
self.noRespond = 0
elif self.crush > tagNum * 2:
print('碰撞{}轮, 过于频繁, 上调slot阈值'.format(self.crush))
for i in range(tagNum):
tagArray[i].adjustSlot(tag=True)
self.crush = 0
else:
print('无需调整slot阈值, 已碰撞{}次, 等待响应{}轮'.format(self.crush, self.noRespond))
# 收到标签的RN16 1.返回ACK指令夹带原RN16 或 2.返回ACK指令,但夹带的RN16错误
def ACK(self, RN16):
print('我收到了你的RN16{},ACK'.format(RN16))
return RN16
# 得到EPC码后 再次请求随机数 RN16为标签第一次返回的随机数
def Req_RN(self, EPC, RN16):
print('我收到了你的EPC{}, Req_RN, 请求返回一个新的随机数'.format(EPC))
return RN16
# 得到handle后 随机返回一次命令已经确认的handle值
def Command(self, handle):
commandSet = ['read', 'write', 'lock', 'kill']
command = random.choice(commandSet)
print('发出新命令{}'.format(command))
return command, handle
def read(self, tag):
print('read tag data:')
print('tagID:{}\n tagEPC:{}\n tagData:{}'.format(tag.id, tag.EPC, tag.data))
return 1
def write(self, tag):
data = input('待写入数据:')
tag.data = data
print('写入完成')
return 1
def lock(self, tag):
print('tagID:{}已lock'.format(tag.id))
return 1
def kill(self, tag):
print('tagID:{} killed,访问结束'.format(tag.id))
return 0
(3)Tag.py
import random
class Tag:
# 类属性
endPoint = 4
def __init__(self):
self.id = self.setID()
self.slot = None
self.state = 'Ready'
self.EPC = self.setEPC()
self.selected = False
self.len = None
self.sendTime = None
self.readerNum = None
self.data = self.initData()
# 给每一个标签一个唯一ID 生成16位随机数
def setID(self):
str = ''
ID = str.join(random.choice('0123456789') for i in range(16))
print('初始化标签ID,tagID={}'.format(ID))
return ID
# 给每个标签赋值一个给定区间[0,10]的随机数
def setSlot(self):
self.slot = random.randint(0, self.endPoint)
print('随机给定slot值,tagID={}的slot值为{}, tag的右端点是{}'.format(self.id, self.slot, self.endPoint))
# 收到QueryAdjust就调整区间右端点 根据tag的值来更改 调整槽计数器的范围 True上调 False下调
def adjustSlot(self, tag=True):
if tag == True:
print()
Tag.endPoint *= 2
else:
Tag.endPoint /= 2
self.setSlot()
def setLength(self, length):
"""
function: 设置数据包长度len和对应所需要的花费时间
:return: NULL
"""
self.len = length
self.sendTime = length * 0.5 + 0.1 #数据包发送时间
# 自减Slot
def reduceSlot(self):
self.slot = (self.slot-1+self.endPoint)%self.endPoint
# 给每个标签赋值一个96位的随机数 即它的EPC码 只初始化一次
def setEPC(self):
str = ''
RN96 = str.join(random.choice('0123456789') for i in range(96))
return RN96
# 返回EPC码
def returnEPC(self):
return self.EPC
# 返回16位的随机数 RN16
def returnRN16(self):
str = ''
RN16 = str.join(random.choice('0123456789') for i in range(16))
return RN16
# 返回一个新的随机数 即Handle
def returnHandle(self):
return self.returnRN16()
# 改变标签状态
def setState(self, newState):
self.state = newState
def initData(self):
foodSet = ['奥利奥', '趣多多', '好丽友', '喜之郎', '娃哈哈']
priceSet = ['10', '15', '21', '8', '29']
food = random.choice(foodSet)
price = random.choice(priceSet)
return '我的存储区里搁了一个{},{}元一个'.format(food, price)
(4)概要介绍
在EPC C1G2协议标准中,标签的行为可以用有限状态机来描述,七个状态。
就绪态(ready state)
就绪态是标签在通电前所处的状态。标签处于就绪态,不参与询问过程。
一个询问过程由阅读器发出的查询命令引起,由下一个查询命令终止。阅读器通过查询命令获得标签的EPC码。
当标签接收到一个查询命令,离开就绪态。标签从阅读器发送的查询命令中,选择一个参数生成随机数用于计算发送的时隙。如果随机数为0,则标签进入恢复状态。否则标签进入仲裁态。
仲裁态(arbitrate state)
当标签处于仲裁态时,它将参加这一轮的查询。但由于此时的标签的时隙数不为0,所以标签等待时隙数变为0.当标签的时隙数变为0时,标签进入回复状态。
回复态(reply state)
当标签需要回复EPC码时,首先进入的是回复态。这时,标签的时隙数为0,标签返回一个16比特的随机数RN16给阅读器。发送RN16是标签发送EPC码给阅读器的第一步。如果标签正确地接收到RN16,它将会发送一个确认命令ACK给标签。在接收RN16的时候,阅读器可能无法正确接收,主要原因有:a.两个或多个标签同时发生RN16给阅读器b.其它射频信号干扰了RN16的传输c.阅读器错过接收RN16的时间。
如果标签成功接收到来自阅读器的确认命令ACK,标签将会发送它的EPC码,同时发送PC码(描述标签的物理信息)和CRC码(用于错误检测)。
接收到确认命令ACK引起标签从回复态进入确认态。标签处于回复态的时间有限,只有标签发送RN16后,没有接收到阅读器的任何命令的有效时间才处于该状态。此后,标签会自动进入仲裁态。
确认态(acknowledged state)
当标签发送它的EPC码给阅读器后 ,标签进入确认态。确认态是标签进入访问命令的必经状态。在确认态中,标签不会被杀死。确认态就像回复态一样,有一个计时器,每当接收到一个命令式,计时器将会置位0。如果标签在指定的时间内没有收到来自阅读器的命令,标签将会自动返回仲裁态。
开放态
安全态
杀死态
杀死操作不能被解除,它将会永远地毁坏标签。 对于一个标签而言,它是在接收到阅读器的正确的密码和杀死命令之后,才会进入杀死态。此后,标签会一直处于杀死态而不是就绪态。当标签进入杀死态,不会再响应阅读器的任何命令。
参考链接:
(24条消息) 标签的状态机_Caramel_biscuit的博客-CSDN博客_在确认态,标签会回复给阅读器pc