smartx-rfid 1.1.6__py3-none-any.whl → 1.5.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- smartx_rfid/clients/rchlo.py +9 -0
- smartx_rfid/db/__init__.py +1 -0
- smartx_rfid/db/_main.py +432 -0
- smartx_rfid/devices/RFID/X714/on_receive.py +0 -3
- smartx_rfid/parser/__init__.py +1 -0
- smartx_rfid/parser/main.py +27 -0
- smartx_rfid/parser/rfid_tag_parser/__init__.py +15 -0
- smartx_rfid/parser/rfid_tag_parser/exceptions.py +15 -0
- smartx_rfid/parser/rfid_tag_parser/tag_tid_parser.py +674 -0
- smartx_rfid/schemas/events.py +2 -1
- smartx_rfid/schemas/tag.py +4 -4
- smartx_rfid/utils/__init__.py +1 -0
- smartx_rfid/utils/path.py +2 -2
- smartx_rfid/utils/regex.py +9 -0
- smartx_rfid/utils/tag_list.py +2 -2
- smartx_rfid/webhook/__init__.py +1 -0
- smartx_rfid/webhook/_main.py +88 -0
- smartx_rfid-1.5.4.dist-info/METADATA +344 -0
- {smartx_rfid-1.1.6.dist-info → smartx_rfid-1.5.4.dist-info}/RECORD +21 -10
- smartx_rfid-1.1.6.dist-info/METADATA +0 -83
- {smartx_rfid-1.1.6.dist-info → smartx_rfid-1.5.4.dist-info}/WHEEL +0 -0
- {smartx_rfid-1.1.6.dist-info → smartx_rfid-1.5.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,674 @@
|
|
|
1
|
+
"""
|
|
2
|
+
RFID Tag TID Parser
|
|
3
|
+
Módulo para parsing e análise de TID (Tag Identifier) de tags RFID.
|
|
4
|
+
|
|
5
|
+
Este módulo fornece funcionalidades para converter TID hexadecimal em informações
|
|
6
|
+
estruturadas sobre tags RFID, incluindo fabricante, modelo, número serial e outras
|
|
7
|
+
características técnicas.
|
|
8
|
+
|
|
9
|
+
Baseado na implementação original em C# TagTidParser.cs
|
|
10
|
+
|
|
11
|
+
Autor: Conversão de C# para Python
|
|
12
|
+
Data: 2025-07-04
|
|
13
|
+
Versão: 1.0.0
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from typing import Dict, Union
|
|
17
|
+
from .exceptions import TagTidParserError, InvalidTidError
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TagTidParser:
|
|
21
|
+
"""
|
|
22
|
+
Parser para TID (Tag Identifier) de tags RFID.
|
|
23
|
+
|
|
24
|
+
Esta classe permite extrair informações detalhadas de tags RFID a partir
|
|
25
|
+
do TID (Tag Identifier) de 96 bits, incluindo:
|
|
26
|
+
|
|
27
|
+
- Número serial de 40 bits (hexadecimal e decimal)
|
|
28
|
+
- Identificação do fabricante
|
|
29
|
+
- Modelo da tag
|
|
30
|
+
- Número do modelo (TMN - Tag Model Number)
|
|
31
|
+
- ID da série Monza (para tags Impinj)
|
|
32
|
+
- Detecção automática do algoritmo de extração
|
|
33
|
+
|
|
34
|
+
Suporte para fabricantes:
|
|
35
|
+
- Impinj (Monza R6, M700, M800, Monza 4/5 Series)
|
|
36
|
+
- NXP (UCODE 7, 8, 9)
|
|
37
|
+
- Fallback universal para fabricantes desconhecidos
|
|
38
|
+
|
|
39
|
+
Exemplo:
|
|
40
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
41
|
+
>>> print(parser.get_vendor_from_tid())
|
|
42
|
+
'Impinj Monza R6'
|
|
43
|
+
>>> print(parser.get_tag_model_name())
|
|
44
|
+
'Impinj M750'
|
|
45
|
+
>>> print(parser.get_40bit_serial_hex())
|
|
46
|
+
'000000000A'
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# Prefixos conhecidos de TID mapeados para fabricantes
|
|
50
|
+
# Baseado nos primeiros 4 bytes (32 bits) do TID
|
|
51
|
+
KNOWN_TID_PREFIXES: Dict[str, str] = {
|
|
52
|
+
# Impinj Monza R6 Series
|
|
53
|
+
"E2801190": "Impinj Monza R6",
|
|
54
|
+
# Impinj M700 Series
|
|
55
|
+
"E2801191": "Impinj M730",
|
|
56
|
+
"E28011A0": "Impinj M770",
|
|
57
|
+
# Impinj M800 Series
|
|
58
|
+
"E28011B0": "Impinj M830/M850",
|
|
59
|
+
# NXP UCODE 9 Series
|
|
60
|
+
"E2806915": "NXP UCODE 9",
|
|
61
|
+
"E2806995": "NXP UCODE 9",
|
|
62
|
+
# Adicione mais conforme necessário
|
|
63
|
+
# Formato: "XXXXXXXX": "Fabricante Modelo"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Mapeamento de TMN (Tag Model Number) para nomes de modelos
|
|
67
|
+
# TMN é extraído dos bits 11-0 dos bytes 2-3 do TID
|
|
68
|
+
TAG_MODEL_MAP: Dict[int, str] = {
|
|
69
|
+
# Impinj M700 Series
|
|
70
|
+
0x190: "Impinj M750", # TMN 0x190 = Impinj M750
|
|
71
|
+
0x191: "Impinj M730", # TMN 0x191 = Impinj M730
|
|
72
|
+
0x1A0: "Impinj M770", # TMN 0x1A0 = Impinj M770
|
|
73
|
+
# Impinj M800 Series
|
|
74
|
+
0x1B0: "Impinj M830/M850", # TMN 0x1B0 = Impinj M830/M850
|
|
75
|
+
# Impinj Monza R6 Family
|
|
76
|
+
0x120: "Impinj Monza R6", # TMN 0x120 = Impinj Monza R6
|
|
77
|
+
0x121: "Impinj Monza R6-A", # TMN 0x121 = Impinj Monza R6-A
|
|
78
|
+
0x122: "Impinj Monza R6-P", # TMN 0x122 = Impinj Monza R6-P
|
|
79
|
+
# Impinj Monza 4 Series
|
|
80
|
+
0x0B2: "Impinj Monza 4D", # TMN 0x0B2 = Impinj Monza 4D
|
|
81
|
+
0x0B3: "Impinj Monza 4E", # TMN 0x0B3 = Impinj Monza 4E
|
|
82
|
+
0x0B4: "Impinj Monza 4U", # TMN 0x0B4 = Impinj Monza 4U
|
|
83
|
+
0x0B5: "Impinj Monza 4QT", # TMN 0x0B5 = Impinj Monza 4QT
|
|
84
|
+
# Impinj Monza 5 Series
|
|
85
|
+
0x0C0: "Impinj Monza 5", # TMN 0x0C0 = Impinj Monza 5
|
|
86
|
+
# NXP UCODE 9 Series
|
|
87
|
+
0x915: "NXP UCODE 9", # TMN 0x915 = NXP UCODE 9 (variante 1)
|
|
88
|
+
0x995: "NXP UCODE 9", # TMN 0x995 = NXP UCODE 9 (variante 2)
|
|
89
|
+
# NXP UCODE 8 Series (prefixos estimados)
|
|
90
|
+
0x910: "NXP UCODE 8", # TMN 0x910 = NXP UCODE 8
|
|
91
|
+
0x990: "NXP UCODE 8", # TMN 0x990 = NXP UCODE 8
|
|
92
|
+
# NXP UCODE 7 Series (valor comum)
|
|
93
|
+
0x970: "NXP UCODE 7", # TMN 0x970 = NXP UCODE 7
|
|
94
|
+
# Adicione mais modelos conforme necessário
|
|
95
|
+
# Formato: 0xXXX: "Fabricante Modelo Específico"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def __init__(self, tid_hex: str):
|
|
99
|
+
"""
|
|
100
|
+
Inicializa o parser com um TID em formato hexadecimal.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
tid_hex (str): TID em formato hexadecimal. Deve ter exatamente 24
|
|
104
|
+
caracteres (96 bits). Espaços e hífens são automaticamente
|
|
105
|
+
removidos. Case insensitive.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
InvalidTidError: Se o TID for None, vazio ou apenas espaços
|
|
109
|
+
ValueError: Se o TID não tiver 24 caracteres hexadecimais ou
|
|
110
|
+
contiver caracteres inválidos
|
|
111
|
+
|
|
112
|
+
Examples:
|
|
113
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
114
|
+
>>> parser = TagTidParser("E2-80-11-90-00-00-00-00-00-00-00-0A")
|
|
115
|
+
>>> parser = TagTidParser("e2 80 11 90 00 00 00 00 00 00 00 0a")
|
|
116
|
+
"""
|
|
117
|
+
if tid_hex is None or not str(tid_hex).strip():
|
|
118
|
+
raise InvalidTidError("TID não pode ser vazio ou nulo")
|
|
119
|
+
|
|
120
|
+
# Normalizar TID: remover espaços, hífens e converter para maiúscula
|
|
121
|
+
tid_hex = str(tid_hex).replace(" ", "").replace("-", "").upper().strip()
|
|
122
|
+
|
|
123
|
+
if len(tid_hex) != 24:
|
|
124
|
+
raise ValueError("TID deve ter 24 caracteres hexadecimais (96 bits)")
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
# Converter para bytes e validar caracteres hexadecimais
|
|
128
|
+
self._tid = bytes.fromhex(tid_hex)
|
|
129
|
+
except ValueError:
|
|
130
|
+
raise ValueError("TID contém caracteres hexadecimais inválidos")
|
|
131
|
+
|
|
132
|
+
# Armazenar TID original normalizado para referência
|
|
133
|
+
self._tid_hex = tid_hex
|
|
134
|
+
# Flag para indicar se o objeto foi "descartado"
|
|
135
|
+
self._disposed = False
|
|
136
|
+
|
|
137
|
+
def get_40bit_serial_hex(self) -> str:
|
|
138
|
+
"""
|
|
139
|
+
Extrai o número serial de 40 bits em formato hexadecimal.
|
|
140
|
+
|
|
141
|
+
O algoritmo de extração varia baseado no fabricante detectado:
|
|
142
|
+
- NXP UCODE 9: Extrai dos bytes 7-11 (posições específicas)
|
|
143
|
+
- Impinj: Extrai dos bytes 6-10 (padrão Impinj)
|
|
144
|
+
- Fallback: Extrai dos últimos 5 bytes (para fabricantes desconhecidos)
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
str: Número serial de 40 bits em formato hexadecimal maiúsculo
|
|
148
|
+
(exatamente 10 caracteres)
|
|
149
|
+
|
|
150
|
+
Examples:
|
|
151
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
152
|
+
>>> parser.get_40bit_serial_hex()
|
|
153
|
+
'000000000A'
|
|
154
|
+
"""
|
|
155
|
+
if self._disposed:
|
|
156
|
+
raise TagTidParserError("Objeto TagTidParser já foi descartado")
|
|
157
|
+
|
|
158
|
+
if self._is_impinj_tid():
|
|
159
|
+
serial = 0
|
|
160
|
+
if self._is_m700_series() or self._is_m800_series():
|
|
161
|
+
serial = (
|
|
162
|
+
((self._tid[6] & 0x3F) << 32)
|
|
163
|
+
| (self._tid[7] << 24)
|
|
164
|
+
| (self._tid[8] << 16)
|
|
165
|
+
| (self._tid[9] << 8)
|
|
166
|
+
| self._tid[10]
|
|
167
|
+
)
|
|
168
|
+
elif self._is_r6_series():
|
|
169
|
+
serial = self._get_r6_series_38bit_serial()
|
|
170
|
+
return f"{serial:010X}"
|
|
171
|
+
|
|
172
|
+
if self._is_nxp_ucode9_tid():
|
|
173
|
+
serial = 0
|
|
174
|
+
for i in range(7, 12):
|
|
175
|
+
serial = (serial << 8) | self._tid[i]
|
|
176
|
+
return f"{serial:010X}"
|
|
177
|
+
|
|
178
|
+
# Algoritmo fallback para fabricantes desconhecidos
|
|
179
|
+
# Extrai dos últimos 5 bytes
|
|
180
|
+
return self._get_fallback_serial_hex()
|
|
181
|
+
|
|
182
|
+
def get_40bit_serial_decimal(self) -> int:
|
|
183
|
+
"""
|
|
184
|
+
Extrai o número serial de 40 bits em formato decimal.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
int: Número serial de 40 bits como inteiro decimal (0 a 1099511627775)
|
|
188
|
+
|
|
189
|
+
Examples:
|
|
190
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
191
|
+
>>> parser.get_40bit_serial_decimal()
|
|
192
|
+
10
|
|
193
|
+
"""
|
|
194
|
+
hex_serial = self.get_40bit_serial_hex()
|
|
195
|
+
return int(hex_serial, 16)
|
|
196
|
+
|
|
197
|
+
def _get_fallback_serial_hex(self) -> str:
|
|
198
|
+
"""
|
|
199
|
+
Método fallback para extrair serial dos últimos 5 bytes.
|
|
200
|
+
|
|
201
|
+
Usado quando o fabricante não é reconhecido ou não tem algoritmo específico.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
str: Serial em formato hexadecimal maiúsculo (10 caracteres)
|
|
205
|
+
"""
|
|
206
|
+
# Extrair últimos 5 bytes (índices 7, 8, 9, 10, 11)
|
|
207
|
+
return self._tid[-5:].hex().upper()
|
|
208
|
+
|
|
209
|
+
def _is_r6_series(self) -> bool:
|
|
210
|
+
"""Verifica se o TMN corresponde a um chip da família Monza R6."""
|
|
211
|
+
tmn = ((self._tid[2] & 0x0F) << 8) | self._tid[3]
|
|
212
|
+
return tmn in {0x120, 0x121, 0x122, 0x170}
|
|
213
|
+
|
|
214
|
+
def _is_m700_series(self) -> bool:
|
|
215
|
+
"""Verifica se o TMN corresponde a um chip da série M700."""
|
|
216
|
+
tmn = ((self._tid[2] & 0x0F) << 8) | self._tid[3]
|
|
217
|
+
return tmn in {0x190, 0x191, 0x1A0, 0x1A2}
|
|
218
|
+
|
|
219
|
+
def _is_m800_series(self) -> bool:
|
|
220
|
+
"""Verifica se o TMN corresponde a um chip da série M800."""
|
|
221
|
+
tmn = ((self._tid[2] & 0x0F) << 8) | self._tid[3]
|
|
222
|
+
return tmn == 0x1B0
|
|
223
|
+
|
|
224
|
+
def _get_r6_series_38bit_serial(self) -> int:
|
|
225
|
+
"""Obtém o serial de 38 bits para tags Monza R6."""
|
|
226
|
+
if not self._is_r6_series():
|
|
227
|
+
raise TagTidParserError("Tag não é da família Monza R6")
|
|
228
|
+
return (
|
|
229
|
+
((self._tid[6] & 0x3F) << 32)
|
|
230
|
+
| (self._tid[7] << 24)
|
|
231
|
+
| (self._tid[8] << 16)
|
|
232
|
+
| (self._tid[9] << 8)
|
|
233
|
+
| self._tid[10]
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def _is_impinj_tid(self) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Verifica se o TID pertence a uma tag Impinj.
|
|
239
|
+
|
|
240
|
+
Critério de identificação Impinj:
|
|
241
|
+
- Byte 0: 0xE2 (ISO/IEC 18000-6C)
|
|
242
|
+
- Byte 1: 0x80 (Allocation Class)
|
|
243
|
+
- Byte 2: 0x1X (bits 7-4 = 0x1, bits 3-0 = qualquer)
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
bool: True se for uma tag Impinj, False caso contrário
|
|
247
|
+
"""
|
|
248
|
+
return self._tid[0] == 0xE2 and self._tid[1] == 0x80 and (self._tid[2] >> 4) == 0x1
|
|
249
|
+
|
|
250
|
+
def _is_nxp_ucode9_tid(self) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Verifica se o TID pertence a uma tag NXP UCODE 9.
|
|
253
|
+
|
|
254
|
+
Critério de identificação NXP UCODE 9:
|
|
255
|
+
- Byte 0: 0xE2 (ISO/IEC 18000-6C)
|
|
256
|
+
- Byte 1: 0x80 (Allocation Class)
|
|
257
|
+
- Byte 2: 0x69 (identificador UCODE 9)
|
|
258
|
+
- Byte 3: 0x15 ou 0x95 (variantes conhecidas)
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
bool: True se for uma tag NXP UCODE 9, False caso contrário
|
|
262
|
+
"""
|
|
263
|
+
return (
|
|
264
|
+
self._tid[0] == 0xE2
|
|
265
|
+
and self._tid[1] == 0x80
|
|
266
|
+
and self._tid[2] == 0x69
|
|
267
|
+
and (self._tid[3] == 0x15 or self._tid[3] == 0x95)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
def get_monza_series_id(self) -> int:
|
|
271
|
+
"""
|
|
272
|
+
Extrai o ID da série Monza (específico para tags Impinj).
|
|
273
|
+
|
|
274
|
+
O ID da série é codificado nos bits 7-6 do byte 10 (índice 10).
|
|
275
|
+
Valores possíveis: 0, 1, 2, 3
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
int: ID da série Monza (0-3)
|
|
279
|
+
|
|
280
|
+
Note:
|
|
281
|
+
Este método só é relevante para tags Impinj. Para outras tags,
|
|
282
|
+
o valor pode não ter significado específico.
|
|
283
|
+
|
|
284
|
+
Examples:
|
|
285
|
+
>>> parser = TagTidParser("E2801190000000000000000040") # série 1
|
|
286
|
+
>>> parser.get_monza_series_id()
|
|
287
|
+
1
|
|
288
|
+
"""
|
|
289
|
+
# Extrair bits 7-6 do byte 10
|
|
290
|
+
return (self._tid[10] >> 6) & 0b11
|
|
291
|
+
|
|
292
|
+
def get_tag_model_number(self) -> str:
|
|
293
|
+
"""
|
|
294
|
+
Extrai o número do modelo da tag (TMN - Tag Model Number).
|
|
295
|
+
|
|
296
|
+
O TMN é codificado nos bits 11-0 dos bytes 2-3:
|
|
297
|
+
- Bits 3-0 do byte 2 (nibble baixo)
|
|
298
|
+
- Bits 7-0 do byte 3 (byte completo)
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
str: Número do modelo em formato hexadecimal maiúsculo (3 caracteres)
|
|
302
|
+
|
|
303
|
+
Examples:
|
|
304
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
305
|
+
>>> parser.get_tag_model_number()
|
|
306
|
+
'190'
|
|
307
|
+
"""
|
|
308
|
+
# Combinar bits 3-0 do byte 2 com byte 3 completo
|
|
309
|
+
# (byte2 & 0x0F) << 8 | byte3
|
|
310
|
+
tmn = ((self._tid[2] & 0x0F) << 8) | self._tid[3]
|
|
311
|
+
return f"{tmn:03X}"
|
|
312
|
+
|
|
313
|
+
def get_tag_model_name(self) -> str:
|
|
314
|
+
"""
|
|
315
|
+
Obtém o nome descritivo do modelo da tag.
|
|
316
|
+
|
|
317
|
+
Consulta o dicionário TAG_MODEL_MAP usando o TMN extraído.
|
|
318
|
+
Se o modelo não for reconhecido, retorna uma string descritiva
|
|
319
|
+
com o TMN em hexadecimal.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
str: Nome do modelo ou "Desconhecido (TMN 0xXXX)" se não encontrado
|
|
323
|
+
|
|
324
|
+
Examples:
|
|
325
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
326
|
+
>>> parser.get_tag_model_name()
|
|
327
|
+
'Impinj M750'
|
|
328
|
+
>>> parser = TagTidParser("E280FF00000000000000000A") # TMN desconhecido
|
|
329
|
+
>>> parser.get_tag_model_name()
|
|
330
|
+
'Desconhecido (TMN 0xF00)'
|
|
331
|
+
"""
|
|
332
|
+
# Extrair TMN
|
|
333
|
+
tmn = ((self._tid[2] & 0x0F) << 8) | self._tid[3]
|
|
334
|
+
|
|
335
|
+
# Buscar no mapeamento ou retornar desconhecido
|
|
336
|
+
return self.TAG_MODEL_MAP.get(tmn, f"Desconhecido (TMN 0x{tmn:03X})")
|
|
337
|
+
|
|
338
|
+
def get_vendor_from_tid(self) -> str:
|
|
339
|
+
"""
|
|
340
|
+
Identifica o fabricante baseado no prefixo do TID.
|
|
341
|
+
|
|
342
|
+
Utiliza os primeiros 4 bytes (32 bits) do TID para identificar
|
|
343
|
+
o fabricante consultando o dicionário KNOWN_TID_PREFIXES.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
str: Nome do fabricante ou "Desconhecido" se não encontrado
|
|
347
|
+
|
|
348
|
+
Examples:
|
|
349
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
350
|
+
>>> parser.get_vendor_from_tid()
|
|
351
|
+
'Impinj Monza R6'
|
|
352
|
+
>>> parser = TagTidParser("FF00AA00000000000000000A") # Prefixo desconhecido
|
|
353
|
+
>>> parser.get_vendor_from_tid()
|
|
354
|
+
'Desconhecido'
|
|
355
|
+
"""
|
|
356
|
+
# Extrair primeiros 4 bytes como string hexadecimal
|
|
357
|
+
prefix = self._tid[:4].hex().upper()
|
|
358
|
+
|
|
359
|
+
# Buscar no dicionário de prefixos conhecidos
|
|
360
|
+
return self.KNOWN_TID_PREFIXES.get(prefix, "Desconhecido")
|
|
361
|
+
|
|
362
|
+
def get_tid_info(self) -> Dict[str, Union[str, int, bool, None]]:
|
|
363
|
+
"""
|
|
364
|
+
Retorna todas as informações extraídas do TID em um dicionário estruturado.
|
|
365
|
+
|
|
366
|
+
Este método combina todos os outros métodos da classe para fornecer
|
|
367
|
+
uma visão completa das informações da tag em um formato conveniente.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
dict: Dicionário com todas as informações da tag contendo:
|
|
371
|
+
- tid (str): TID original em hexadecimal maiúsculo
|
|
372
|
+
- vendor (str): Nome do fabricante
|
|
373
|
+
- model_name (str): Nome do modelo
|
|
374
|
+
- model_number (str): Número do modelo (TMN) em hex
|
|
375
|
+
- serial_hex (str): Serial de 40 bits em hexadecimal
|
|
376
|
+
- serial_decimal (int): Serial de 40 bits em decimal
|
|
377
|
+
- monza_series_id (int|None): ID da série Monza (apenas Impinj)
|
|
378
|
+
- is_impinj (bool): True se for tag Impinj
|
|
379
|
+
- is_nxp_ucode9 (bool): True se for NXP UCODE 9
|
|
380
|
+
|
|
381
|
+
Examples:
|
|
382
|
+
>>> parser = TagTidParser("E2801190000000000000000A")
|
|
383
|
+
>>> info = parser.get_tid_info()
|
|
384
|
+
>>> print(info['vendor'])
|
|
385
|
+
'Impinj Monza R6'
|
|
386
|
+
>>> print(info['serial_decimal'])
|
|
387
|
+
10
|
|
388
|
+
"""
|
|
389
|
+
# Determinar se é Impinj para incluir Monza Series ID
|
|
390
|
+
is_impinj = self._is_impinj_tid()
|
|
391
|
+
monza_series_id = self.get_monza_series_id() if is_impinj else None
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
"tid": self._tid_hex,
|
|
395
|
+
"vendor": self.get_vendor_from_tid(),
|
|
396
|
+
"model_name": self.get_tag_model_name(),
|
|
397
|
+
"model_number": self.get_tag_model_number(),
|
|
398
|
+
"serial_hex": self.get_40bit_serial_hex(),
|
|
399
|
+
"serial_decimal": self.get_40bit_serial_decimal(),
|
|
400
|
+
"monza_series_id": monza_series_id,
|
|
401
|
+
"is_impinj": is_impinj,
|
|
402
|
+
"is_nxp_ucode9": self._is_nxp_ucode9_tid(),
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
def __str__(self) -> str:
|
|
406
|
+
"""
|
|
407
|
+
Representação string do parser.
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
str: String descritiva com TID, fabricante e modelo
|
|
411
|
+
"""
|
|
412
|
+
vendor = self.get_vendor_from_tid()
|
|
413
|
+
model = self.get_tag_model_name()
|
|
414
|
+
return f"TagTidParser(TID={self._tid_hex}, Vendor={vendor}, Model={model})"
|
|
415
|
+
|
|
416
|
+
def __repr__(self) -> str:
|
|
417
|
+
"""
|
|
418
|
+
Representação para debug do parser.
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
str: String de representação para debug
|
|
422
|
+
"""
|
|
423
|
+
return f"TagTidParser('{self._tid_hex}')"
|
|
424
|
+
|
|
425
|
+
def __eq__(self, other) -> bool:
|
|
426
|
+
"""
|
|
427
|
+
Compara dois parsers pela igualdade do TID.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
other: Outro objeto TagTidParser para comparação
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
bool: True se os TIDs forem iguais
|
|
434
|
+
"""
|
|
435
|
+
if not isinstance(other, TagTidParser):
|
|
436
|
+
return False
|
|
437
|
+
return self._tid_hex == other._tid_hex
|
|
438
|
+
|
|
439
|
+
def __hash__(self) -> int:
|
|
440
|
+
"""
|
|
441
|
+
Hash baseado no TID para uso em sets e dicts.
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
int: Hash do TID
|
|
445
|
+
"""
|
|
446
|
+
return hash(self._tid_hex)
|
|
447
|
+
|
|
448
|
+
def dispose(self) -> None:
|
|
449
|
+
"""Descarta o objeto limpando os dados do TID."""
|
|
450
|
+
if not self._disposed:
|
|
451
|
+
self._tid = b""
|
|
452
|
+
self._disposed = True
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
# ============================================================================
|
|
456
|
+
# FUNÇÕES DE CONVENIÊNCIA PARA USO DIRETO
|
|
457
|
+
# ============================================================================
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def parse_tid(tid_hex: str) -> Dict[str, Union[str, int, bool, None]]:
|
|
461
|
+
"""
|
|
462
|
+
Função de conveniência para parsing rápido de TID.
|
|
463
|
+
|
|
464
|
+
Esta função cria um TagTidParser temporário e retorna todas as
|
|
465
|
+
informações extraídas em um dicionário.
|
|
466
|
+
|
|
467
|
+
Args:
|
|
468
|
+
tid_hex (str): TID em formato hexadecimal (24 caracteres)
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
dict: Dicionário com todas as informações da tag
|
|
472
|
+
(mesmo formato que TagTidParser.get_tid_info())
|
|
473
|
+
|
|
474
|
+
Raises:
|
|
475
|
+
InvalidTidError: Se o TID for inválido
|
|
476
|
+
ValueError: Se o TID tiver formato incorreto
|
|
477
|
+
|
|
478
|
+
Examples:
|
|
479
|
+
>>> info = parse_tid("E2801190000000000000000A")
|
|
480
|
+
>>> print(info['model_name'])
|
|
481
|
+
'Impinj M750'
|
|
482
|
+
>>> print(info['serial_hex'])
|
|
483
|
+
'000000000A'
|
|
484
|
+
"""
|
|
485
|
+
parser = TagTidParser(tid_hex)
|
|
486
|
+
return parser.get_tid_info()
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def get_serial_from_tid(tid_hex: str, format_type: str = "hex") -> Union[str, int]:
|
|
490
|
+
"""
|
|
491
|
+
Extrai apenas o número serial do TID no formato especificado.
|
|
492
|
+
|
|
493
|
+
Função de conveniência para quando apenas o serial é necessário,
|
|
494
|
+
evitando criar o parser completo.
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
tid_hex (str): TID em formato hexadecimal (24 caracteres)
|
|
498
|
+
format_type (str): Formato do retorno - "hex" ou "decimal"
|
|
499
|
+
|
|
500
|
+
Returns:
|
|
501
|
+
str: Serial em hexadecimal se format_type="hex"
|
|
502
|
+
int: Serial em decimal se format_type="decimal"
|
|
503
|
+
|
|
504
|
+
Raises:
|
|
505
|
+
InvalidTidError: Se o TID for inválido
|
|
506
|
+
ValueError: Se o TID tiver formato incorreto ou format_type inválido
|
|
507
|
+
|
|
508
|
+
Examples:
|
|
509
|
+
>>> get_serial_from_tid("E2801190000000000000000A", "hex")
|
|
510
|
+
'000000000A'
|
|
511
|
+
>>> get_serial_from_tid("E2801190000000000000000A", "decimal")
|
|
512
|
+
10
|
|
513
|
+
"""
|
|
514
|
+
parser = TagTidParser(tid_hex)
|
|
515
|
+
|
|
516
|
+
if format_type.lower() == "decimal":
|
|
517
|
+
return parser.get_40bit_serial_decimal()
|
|
518
|
+
elif format_type.lower() == "hex":
|
|
519
|
+
return parser.get_40bit_serial_hex()
|
|
520
|
+
else:
|
|
521
|
+
raise ValueError(f"format_type deve ser 'hex' ou 'decimal', recebido: '{format_type}'")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def validate_tid(tid_hex: str) -> bool:
|
|
525
|
+
"""
|
|
526
|
+
Valida se um TID tem formato válido sem fazer o parsing completo.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
tid_hex (str): TID para validar
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
bool: True se o TID for válido, False caso contrário
|
|
533
|
+
|
|
534
|
+
Examples:
|
|
535
|
+
>>> validate_tid("E2801190000000000000000A")
|
|
536
|
+
True
|
|
537
|
+
>>> validate_tid("INVALID")
|
|
538
|
+
False
|
|
539
|
+
"""
|
|
540
|
+
try:
|
|
541
|
+
TagTidParser(tid_hex)
|
|
542
|
+
return True
|
|
543
|
+
except (InvalidTidError, ValueError):
|
|
544
|
+
return False
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
def get_vendor_from_tid(tid_hex: str) -> str:
|
|
548
|
+
"""
|
|
549
|
+
Extrai apenas o fabricante do TID.
|
|
550
|
+
|
|
551
|
+
Função de conveniência para identificação rápida do fabricante.
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
tid_hex (str): TID em formato hexadecimal
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
str: Nome do fabricante ou "Desconhecido"
|
|
558
|
+
|
|
559
|
+
Examples:
|
|
560
|
+
>>> get_vendor_from_tid("E2801190000000000000000A")
|
|
561
|
+
'Impinj Monza R6'
|
|
562
|
+
"""
|
|
563
|
+
parser = TagTidParser(tid_hex)
|
|
564
|
+
return parser.get_vendor_from_tid()
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def get_model_from_tid(tid_hex: str) -> str:
|
|
568
|
+
"""
|
|
569
|
+
Extrai apenas o modelo do TID.
|
|
570
|
+
|
|
571
|
+
Função de conveniência para identificação rápida do modelo.
|
|
572
|
+
|
|
573
|
+
Args:
|
|
574
|
+
tid_hex (str): TID em formato hexadecimal
|
|
575
|
+
|
|
576
|
+
Returns:
|
|
577
|
+
str: Nome do modelo
|
|
578
|
+
|
|
579
|
+
Examples:
|
|
580
|
+
>>> get_model_from_tid("E2801190000000000000000A")
|
|
581
|
+
'Impinj M750'
|
|
582
|
+
"""
|
|
583
|
+
parser = TagTidParser(tid_hex)
|
|
584
|
+
return parser.get_tag_model_name()
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
# ============================================================================
|
|
588
|
+
# CÓDIGO DE EXEMPLO E DEMONSTRAÇÃO
|
|
589
|
+
# ============================================================================
|
|
590
|
+
|
|
591
|
+
if __name__ == "__main__":
|
|
592
|
+
"""
|
|
593
|
+
Código de exemplo que demonstra o uso da biblioteca.
|
|
594
|
+
|
|
595
|
+
Execute este arquivo diretamente para ver exemplos de uso:
|
|
596
|
+
python rfid_tag_parser/tag_tid_parser.py
|
|
597
|
+
"""
|
|
598
|
+
|
|
599
|
+
print("=" * 80)
|
|
600
|
+
print("🏷️ RFID TAG TID PARSER - DEMONSTRAÇÃO")
|
|
601
|
+
print("=" * 80)
|
|
602
|
+
|
|
603
|
+
# TIDs de exemplo para demonstração
|
|
604
|
+
example_tids = [
|
|
605
|
+
"E2801190000000000000000A", # Impinj Monza R6
|
|
606
|
+
"E2801191000000000000000B", # Impinj M730
|
|
607
|
+
"E28011A0000000000000000C", # Impinj M770
|
|
608
|
+
"E2806915000000000000000E", # NXP UCODE 9
|
|
609
|
+
"FF00AA00000000000000002A", # Fabricante desconhecido
|
|
610
|
+
]
|
|
611
|
+
|
|
612
|
+
print("\n📋 Exemplos de Parsing de TID:")
|
|
613
|
+
print("-" * 80)
|
|
614
|
+
|
|
615
|
+
for i, tid in enumerate(example_tids, 1):
|
|
616
|
+
try:
|
|
617
|
+
print(f"\n{i}. TID: {tid}")
|
|
618
|
+
|
|
619
|
+
# Usar a classe TagTidParser
|
|
620
|
+
parser = TagTidParser(tid)
|
|
621
|
+
|
|
622
|
+
print(f" Fabricante: {parser.get_vendor_from_tid()}")
|
|
623
|
+
print(f" Modelo: {parser.get_tag_model_name()}")
|
|
624
|
+
print(f" Número do modelo: {parser.get_tag_model_number()}")
|
|
625
|
+
print(f" Serial (Hex): {parser.get_40bit_serial_hex()}")
|
|
626
|
+
print(f" Serial (Decimal): {parser.get_40bit_serial_decimal()}")
|
|
627
|
+
print(f" É Impinj: {parser._is_impinj_tid()}")
|
|
628
|
+
print(f" É NXP UCODE9: {parser._is_nxp_ucode9_tid()}")
|
|
629
|
+
|
|
630
|
+
if parser._is_impinj_tid():
|
|
631
|
+
print(f" Monza Series ID: {parser.get_monza_series_id()}")
|
|
632
|
+
|
|
633
|
+
except Exception as e:
|
|
634
|
+
print(f" ❌ Erro: {e}")
|
|
635
|
+
|
|
636
|
+
print("\n" + "=" * 80)
|
|
637
|
+
print("🚀 Exemplos de Funções de Conveniência:")
|
|
638
|
+
print("=" * 80)
|
|
639
|
+
|
|
640
|
+
# Demonstrar funções de conveniência
|
|
641
|
+
test_tid = "E2801190123456789ABCDEF0"
|
|
642
|
+
|
|
643
|
+
print(f"\nTID de teste: {test_tid}")
|
|
644
|
+
print("-" * 50)
|
|
645
|
+
|
|
646
|
+
try:
|
|
647
|
+
# Parsing completo
|
|
648
|
+
info = parse_tid(test_tid)
|
|
649
|
+
print("✓ Parsing completo:")
|
|
650
|
+
for key, value in info.items():
|
|
651
|
+
print(f" {key}: {value}")
|
|
652
|
+
|
|
653
|
+
# Extração específica
|
|
654
|
+
print("\n✓ Extração específica:")
|
|
655
|
+
print(f" Serial (hex): {get_serial_from_tid(test_tid, 'hex')}")
|
|
656
|
+
print(f" Serial (decimal): {get_serial_from_tid(test_tid, 'decimal')}")
|
|
657
|
+
print(f" Fabricante: {get_vendor_from_tid(test_tid)}")
|
|
658
|
+
print(f" Modelo: {get_model_from_tid(test_tid)}")
|
|
659
|
+
|
|
660
|
+
# Validação
|
|
661
|
+
print("\n✓ Validação:")
|
|
662
|
+
print(f" TID válido: {validate_tid(test_tid)}")
|
|
663
|
+
print(f" TID inválido: {validate_tid('INVALID')}")
|
|
664
|
+
|
|
665
|
+
except Exception as e:
|
|
666
|
+
print(f"❌ Erro na demonstração: {e}")
|
|
667
|
+
|
|
668
|
+
print("\n" + "=" * 80)
|
|
669
|
+
print("📚 Para mais exemplos, consulte:")
|
|
670
|
+
print(" - examples/basic_usage.py")
|
|
671
|
+
print(" - examples/batch_processing.py")
|
|
672
|
+
print(" - examples/integration_example.py")
|
|
673
|
+
print(" - docs/API.md")
|
|
674
|
+
print("=" * 80)
|
smartx_rfid/schemas/events.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import Any
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class EventSchema(BaseModel):
|
|
5
6
|
event_type: str = Field("event", description="Type of the event")
|
|
6
|
-
event_data = Field(None, description="Associated data with the event")
|
|
7
|
+
event_data: Any = Field(None, description="Associated data with the event")
|