smartx-rfid 0.4.0__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/__init__.py +9 -0
- smartx_rfid/devices/RFID/R700_IOT/_main.py +167 -0
- smartx_rfid/devices/RFID/R700_IOT/on_event.py +17 -0
- smartx_rfid/devices/RFID/R700_IOT/reader_config_example.py +69 -0
- smartx_rfid/devices/RFID/R700_IOT/reader_helpers.py +136 -0
- smartx_rfid/devices/RFID/R700_IOT/write_commands.py +60 -0
- smartx_rfid/devices/RFID/X714/_main.py +146 -0
- smartx_rfid/devices/RFID/X714/ble_protocol.py +159 -0
- smartx_rfid/devices/RFID/X714/on_receive.py +54 -0
- smartx_rfid/devices/RFID/X714/rfid.py +73 -0
- smartx_rfid/devices/RFID/X714/serial_protocol.py +89 -0
- smartx_rfid/devices/RFID/X714/tcp_protocol.py +127 -0
- smartx_rfid/devices/RFID/X714/write_commands.py +25 -0
- smartx_rfid/devices/__init__.py +8 -0
- smartx_rfid/devices/generic/SERIAL/_main.py +237 -0
- smartx_rfid/devices/generic/TCP/_main.py +72 -0
- smartx_rfid/devices/generic/TCP/helpers.py +41 -0
- smartx_rfid/schemas/tag.py +55 -0
- smartx_rfid/utils/__init__.py +0 -0
- smartx_rfid/utils/event.py +15 -0
- smartx_rfid-0.4.0.dist-info/METADATA +84 -0
- smartx_rfid-0.4.0.dist-info/RECORD +24 -0
- smartx_rfid-0.4.0.dist-info/WHEEL +4 -0
- smartx_rfid-0.4.0.dist-info/licenses/LICENSE +21 -0
smartx_rfid/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""publish_lib_ghp - A simple Python library demonstrating how to publish packages to PyPI."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = version("publish-lib-example")
|
|
7
|
+
except Exception:
|
|
8
|
+
# Fallback for development environments
|
|
9
|
+
__version__ = "0.0.0-dev"
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from smartx_rfid.schemas.tag import WriteTagValidator
|
|
7
|
+
from smartx_rfid.utils.event import on_event
|
|
8
|
+
|
|
9
|
+
from .on_event import OnEvent
|
|
10
|
+
from .reader_helpers import ReaderHelpers
|
|
11
|
+
from .write_commands import WriteCommands
|
|
12
|
+
from .reader_config_example import R700_IOT_config_example
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class R700_IOT(OnEvent, ReaderHelpers, WriteCommands):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
# READER CONFIG
|
|
19
|
+
reading_config: dict,
|
|
20
|
+
name: str = "R700",
|
|
21
|
+
# CONNECTION
|
|
22
|
+
ip: str = "192.168.1.101", # Example hotsname: impinj-14-46-36
|
|
23
|
+
username: str = "root",
|
|
24
|
+
password: str = "impinj",
|
|
25
|
+
start_reading: bool = False,
|
|
26
|
+
# Firmware Version
|
|
27
|
+
firmware_version: str = "8.4.1",
|
|
28
|
+
):
|
|
29
|
+
self.name = name
|
|
30
|
+
self.device_type = "rfid"
|
|
31
|
+
|
|
32
|
+
self.ip = ip
|
|
33
|
+
self.username = username
|
|
34
|
+
self.password = password
|
|
35
|
+
|
|
36
|
+
self.start_reading = start_reading
|
|
37
|
+
|
|
38
|
+
# URL AND ENDPOINTS
|
|
39
|
+
self.urlBase = f"https://{self.ip}/api/v1"
|
|
40
|
+
self.endpoint_interface = f"{self.urlBase}/system/rfid/interface"
|
|
41
|
+
self.check_version_endpoint = f"{self.urlBase}/system/image"
|
|
42
|
+
self.endpoint_start = f"{self.urlBase}/profiles/inventory/start"
|
|
43
|
+
self.endpoint_stop = f"{self.urlBase}/profiles/stop"
|
|
44
|
+
self.endpointDataStream = f"{self.urlBase}/data/stream"
|
|
45
|
+
self.endpoint_gpo = f"{self.urlBase}/device/gpos"
|
|
46
|
+
self.endpoint_write = f"{self.urlBase}/profiles/inventory/tag-access"
|
|
47
|
+
|
|
48
|
+
self.interface_config = {"rfidInterface": "rest"}
|
|
49
|
+
self.auth = httpx.BasicAuth(self.username, self.password)
|
|
50
|
+
|
|
51
|
+
self.tags_to_write = {}
|
|
52
|
+
|
|
53
|
+
self.is_connected = False
|
|
54
|
+
self.is_reading = False
|
|
55
|
+
self._stop_connection = False
|
|
56
|
+
|
|
57
|
+
self.firmware_version = firmware_version
|
|
58
|
+
|
|
59
|
+
self.reading_config = reading_config
|
|
60
|
+
self.config_example = R700_IOT_config_example
|
|
61
|
+
|
|
62
|
+
self.on_event = on_event
|
|
63
|
+
|
|
64
|
+
async def disconnect(self):
|
|
65
|
+
"""Desconecta o reader de forma segura."""
|
|
66
|
+
logging.info(f"{self.name} 🔌 Disconnecting reader")
|
|
67
|
+
self._stop_connection = True
|
|
68
|
+
|
|
69
|
+
if self.is_reading:
|
|
70
|
+
try:
|
|
71
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=5.0) as session:
|
|
72
|
+
await self.stop_inventory(session)
|
|
73
|
+
except Exception as e:
|
|
74
|
+
logging.warning(f"{self.name} ⚠️ Error stopping inventory during disconnect: {e}")
|
|
75
|
+
|
|
76
|
+
self.is_connected = False
|
|
77
|
+
self.is_reading = False
|
|
78
|
+
self.on_event(self.name, "reading", False)
|
|
79
|
+
self.on_event(self.name, "connection", False)
|
|
80
|
+
|
|
81
|
+
async def connect(self):
|
|
82
|
+
self._stop_connection = False
|
|
83
|
+
while not self._stop_connection:
|
|
84
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=10.0) as session:
|
|
85
|
+
if self.is_connected:
|
|
86
|
+
self.on_event(self.name, "connection", False)
|
|
87
|
+
|
|
88
|
+
self.is_connected = False
|
|
89
|
+
self.is_reading = False
|
|
90
|
+
|
|
91
|
+
# Configure interface
|
|
92
|
+
success = await self.configure_interface(session)
|
|
93
|
+
if not success:
|
|
94
|
+
logging.warning(f"{self.name} - Failed to configure interface")
|
|
95
|
+
await asyncio.sleep(1)
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
# Check firmware version
|
|
99
|
+
success = await self.check_firmware_version(session)
|
|
100
|
+
if not success:
|
|
101
|
+
logging.warning(
|
|
102
|
+
f"{self.name} - Incompatible firmware version. Update R700 firmware to {self.firmware_version}."
|
|
103
|
+
)
|
|
104
|
+
await asyncio.sleep(5)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# Stop any ongoing profiles
|
|
108
|
+
success = await self.stop_inventory(session)
|
|
109
|
+
if not success:
|
|
110
|
+
logging.warning(f"{self.name} - Failed to stop profiles")
|
|
111
|
+
await asyncio.sleep(1)
|
|
112
|
+
continue
|
|
113
|
+
|
|
114
|
+
if self.start_reading or self.reading_config.get("startTriggers"):
|
|
115
|
+
success = await self.start_inventory(session)
|
|
116
|
+
if not success:
|
|
117
|
+
logging.warning(f"{self.name} - Failed to start inventory")
|
|
118
|
+
await asyncio.sleep(1)
|
|
119
|
+
continue
|
|
120
|
+
if self.start_reading:
|
|
121
|
+
self.is_reading = True
|
|
122
|
+
self.on_event(self.name, "reading", True)
|
|
123
|
+
|
|
124
|
+
# Clear GPO states
|
|
125
|
+
for i in range(1, 4):
|
|
126
|
+
asyncio.create_task(self.write_gpo(pin=i, state=False))
|
|
127
|
+
|
|
128
|
+
self.is_connected = True
|
|
129
|
+
self.on_event(self.name, "connection", True)
|
|
130
|
+
await self.get_tag_list(session)
|
|
131
|
+
|
|
132
|
+
async def clear_tags(self):
|
|
133
|
+
self.tags = {}
|
|
134
|
+
|
|
135
|
+
async def write_gpo(
|
|
136
|
+
self,
|
|
137
|
+
pin: int = 1,
|
|
138
|
+
state: bool | str = True,
|
|
139
|
+
control: str = "static",
|
|
140
|
+
time: int = 1000,
|
|
141
|
+
*args,
|
|
142
|
+
**kwargs,
|
|
143
|
+
):
|
|
144
|
+
gpo_command = await self.get_gpo_command(pin=pin, state=state, control=control, time=time)
|
|
145
|
+
try:
|
|
146
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=10.0) as session:
|
|
147
|
+
await self.post_to_reader(session, self.endpoint_gpo, payload=gpo_command, method="put")
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logging.warning(f"{self.name} - Failed to set GPO: {e}")
|
|
150
|
+
|
|
151
|
+
def write_epc(self, target_identifier: str | None, target_value: str | None, new_epc: str, password: str):
|
|
152
|
+
"""
|
|
153
|
+
Writes a new EPC (Electronic Product Code) to RFID tags.
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
validated_tag = WriteTagValidator(
|
|
157
|
+
target_identifier=target_identifier,
|
|
158
|
+
target_value=target_value,
|
|
159
|
+
new_epc=new_epc,
|
|
160
|
+
password=password,
|
|
161
|
+
)
|
|
162
|
+
logging.info(
|
|
163
|
+
f"{self.name} - Writing EPC: {validated_tag.new_epc} (Current: {validated_tag.target_identifier}={validated_tag.target_value})"
|
|
164
|
+
)
|
|
165
|
+
asyncio.create_task(self.send_write_command(validated_tag.model_dump()))
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logging.warning(f"{self.name} - Write validation error: {e}")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class OnEvent:
|
|
2
|
+
async def on_start(self):
|
|
3
|
+
self.is_reading = True
|
|
4
|
+
self.on_event(self.name, "reading", True)
|
|
5
|
+
|
|
6
|
+
async def on_stop(self):
|
|
7
|
+
self.is_reading = False
|
|
8
|
+
self.on_event(self.name, "reading", False)
|
|
9
|
+
|
|
10
|
+
async def on_tag(self, tag):
|
|
11
|
+
current_tag = {
|
|
12
|
+
"epc": tag.get("epcHex"),
|
|
13
|
+
"tid": tag.get("tidHex"),
|
|
14
|
+
"ant": tag.get("antennaPort"),
|
|
15
|
+
"rssi": int(tag.get("peakRssiCdbm", 0) / 100),
|
|
16
|
+
}
|
|
17
|
+
self.on_event(self.name, "tag", current_tag)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
R700_IOT_config_example = {
|
|
2
|
+
"antennaConfigs": [
|
|
3
|
+
{
|
|
4
|
+
"antennaPort": 1,
|
|
5
|
+
"estimatedTagPopulation": 16,
|
|
6
|
+
"fastId": "enabled",
|
|
7
|
+
"inventorySearchMode": "dual-target",
|
|
8
|
+
"inventorySession": 1,
|
|
9
|
+
"receiveSensitivityDbm": -80,
|
|
10
|
+
"rfMode": 4,
|
|
11
|
+
"transmitPowerCdbm": 3300,
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"antennaPort": 2,
|
|
15
|
+
"estimatedTagPopulation": 16,
|
|
16
|
+
"fastId": "enabled",
|
|
17
|
+
"inventorySearchMode": "dual-target",
|
|
18
|
+
"inventorySession": 1,
|
|
19
|
+
"receiveSensitivityDbm": -80,
|
|
20
|
+
"rfMode": 4,
|
|
21
|
+
"transmitPowerCdbm": 3300,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"antennaPort": 3,
|
|
25
|
+
"estimatedTagPopulation": 16,
|
|
26
|
+
"fastId": "enabled",
|
|
27
|
+
"inventorySearchMode": "dual-target",
|
|
28
|
+
"inventorySession": 1,
|
|
29
|
+
"receiveSensitivityDbm": -80,
|
|
30
|
+
"rfMode": 4,
|
|
31
|
+
"transmitPowerCdbm": 3300,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"antennaPort": 4,
|
|
35
|
+
"estimatedTagPopulation": 16,
|
|
36
|
+
"fastId": "enabled",
|
|
37
|
+
"inventorySearchMode": "dual-target",
|
|
38
|
+
"inventorySession": 1,
|
|
39
|
+
"receiveSensitivityDbm": -80,
|
|
40
|
+
"rfMode": 4,
|
|
41
|
+
"transmitPowerCdbm": 3300,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
"startTriggers": [{"gpiTransitionEvent": {"gpi": 1, "transition": "high-to-low"}}],
|
|
45
|
+
"stopTriggers": [{"gpiTransitionEvent": {"gpi": 1, "transition": "low-to-high"}}],
|
|
46
|
+
"eventConfig": {
|
|
47
|
+
"common": {"hostname": "disabled"},
|
|
48
|
+
"tagInventory": {
|
|
49
|
+
"epc": "disabled",
|
|
50
|
+
"epcHex": "enabled",
|
|
51
|
+
"xpcHex": "disabled",
|
|
52
|
+
"tid": "disabled",
|
|
53
|
+
"tidHex": "enabled",
|
|
54
|
+
"antennaPort": "enabled",
|
|
55
|
+
"transmitPowerCdbm": "disabled",
|
|
56
|
+
"peakRssiCdbm": "enabled",
|
|
57
|
+
"frequency": "disabled",
|
|
58
|
+
"pc": "disabled",
|
|
59
|
+
"lastSeenTime": "disabled",
|
|
60
|
+
"phaseAngle": "disabled",
|
|
61
|
+
"tagReporting": {
|
|
62
|
+
"reportingIntervalSeconds": 1,
|
|
63
|
+
"tagCacheSize": 2048,
|
|
64
|
+
"antennaIdentifier": "antennaPort",
|
|
65
|
+
"tagIdentifier": "epc",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ReaderHelpers:
|
|
9
|
+
async def check_firmware_version(self, session: httpx.AsyncClient | None = None):
|
|
10
|
+
endpoint = self.check_version_endpoint
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
if session is None:
|
|
14
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=5.0) as client:
|
|
15
|
+
response = await client.get(endpoint)
|
|
16
|
+
else:
|
|
17
|
+
response = await session.get(endpoint)
|
|
18
|
+
|
|
19
|
+
if response.status_code != 200:
|
|
20
|
+
logging.warning(f"{self.name} - Failed to get firmware version: {response.status_code}")
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
version_info = response.json()
|
|
24
|
+
firmware_version = version_info.get("primaryFirmware", "Unknown")
|
|
25
|
+
return firmware_version.startswith(self.firmware_version)
|
|
26
|
+
|
|
27
|
+
except Exception as e:
|
|
28
|
+
logging.warning(f"{self.name} - Error GET {endpoint}: {e}")
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
async def configure_interface(self, session):
|
|
32
|
+
return await self.post_to_reader(
|
|
33
|
+
session,
|
|
34
|
+
self.endpoint_interface,
|
|
35
|
+
payload=self.interface_config,
|
|
36
|
+
method="put",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
async def stop_inventory(self, session=None):
|
|
40
|
+
return await self.post_to_reader(session, self.endpoint_stop)
|
|
41
|
+
|
|
42
|
+
async def start_inventory(self, session=None):
|
|
43
|
+
return await self.post_to_reader(session, self.endpoint_start, payload=self.reading_config)
|
|
44
|
+
|
|
45
|
+
async def post_to_reader(self, session, endpoint, payload=None, method="post", timeout=3):
|
|
46
|
+
try:
|
|
47
|
+
if session is None:
|
|
48
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=timeout) as client:
|
|
49
|
+
return await self.post_to_reader(client, endpoint, payload, method, timeout)
|
|
50
|
+
|
|
51
|
+
if method == "post":
|
|
52
|
+
response = await session.post(endpoint, json=payload, timeout=timeout)
|
|
53
|
+
if response.status_code != 204:
|
|
54
|
+
logging.warning(f"{self.name} - POST {endpoint} failed: {response.status_code}")
|
|
55
|
+
|
|
56
|
+
elif method == "put":
|
|
57
|
+
response = await session.put(endpoint, json=payload, timeout=timeout)
|
|
58
|
+
if response.status_code != 204:
|
|
59
|
+
logging.warning(f"{self.name} - PUT {endpoint} failed: {response.status_code}")
|
|
60
|
+
|
|
61
|
+
return response.status_code == 204
|
|
62
|
+
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logging.warning(f"{self.name} - Error posting to {endpoint}: {e}")
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
async def get_tag_list(self, session):
|
|
68
|
+
try:
|
|
69
|
+
async with session.stream("GET", self.endpointDataStream, timeout=None) as response:
|
|
70
|
+
if response.status_code != 200:
|
|
71
|
+
logging.warning(f"{self.name} - Failed to connect to data stream: {response.status_code}")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
logging.info(f"{self.name} - Connected to data stream.")
|
|
75
|
+
|
|
76
|
+
async for line in response.aiter_lines():
|
|
77
|
+
try:
|
|
78
|
+
string = line.strip()
|
|
79
|
+
if not string:
|
|
80
|
+
continue
|
|
81
|
+
jsonEvent = json.loads(string)
|
|
82
|
+
|
|
83
|
+
if "inventoryStatusEvent" in jsonEvent:
|
|
84
|
+
status = jsonEvent["inventoryStatusEvent"]["inventoryStatus"]
|
|
85
|
+
if status == "running":
|
|
86
|
+
asyncio.create_task(self.on_start())
|
|
87
|
+
else:
|
|
88
|
+
asyncio.create_task(self.on_stop())
|
|
89
|
+
elif "tagInventoryEvent" in jsonEvent:
|
|
90
|
+
tagEvent = jsonEvent["tagInventoryEvent"]
|
|
91
|
+
asyncio.create_task(self.on_tag(tagEvent))
|
|
92
|
+
|
|
93
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as parse_error:
|
|
94
|
+
logging.warning(f"{self.name} - Failed to parse event: {parse_error}")
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logging.warning(f"{self.name} - Unexpected error: {e}")
|
|
97
|
+
|
|
98
|
+
async def get_gpo_command(
|
|
99
|
+
self, pin: int = 1, state: bool | str = True, control: str = "static", time: int = 1000
|
|
100
|
+
) -> dict:
|
|
101
|
+
"""
|
|
102
|
+
Gera o payload de configuração de GPO para o leitor RFID.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
pin (int): Número do pino GPO a ser configurado. Default é 1.
|
|
106
|
+
state (bool | str): Estado do pino. Pode ser:
|
|
107
|
+
- True ou "high" → alto
|
|
108
|
+
- False ou "low" → baixo
|
|
109
|
+
control ("static" | "pulsed"): Tipo de controle do pino.
|
|
110
|
+
- "static": mantém o estado
|
|
111
|
+
- "pulsed": envia pulso por tempo definido
|
|
112
|
+
time (int): Duração do pulso em milissegundos. Apenas usado se control="pulsed". Default 1000ms.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
dict: Payload compatível com a API do leitor RFID para configurar GPO.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
gpo_cmd = await self.get_gpo_command(pin=2, state=True, control="pulsed", time=500)
|
|
119
|
+
"""
|
|
120
|
+
# Normaliza o estado
|
|
121
|
+
state = "high" if state is True else "low" if state is False else str(state)
|
|
122
|
+
|
|
123
|
+
if control == "static":
|
|
124
|
+
gpo_command = {"gpoConfigurations": [{"gpo": pin, "state": state, "control": control}]}
|
|
125
|
+
elif control == "pulsed":
|
|
126
|
+
gpo_command = {
|
|
127
|
+
"gpoConfigurations": [
|
|
128
|
+
{
|
|
129
|
+
"gpo": pin,
|
|
130
|
+
"state": state,
|
|
131
|
+
"pulseDurationMilliseconds": time,
|
|
132
|
+
"control": control,
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
return gpo_command
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WriteCommands:
|
|
7
|
+
async def get_write_cmd(self, tag):
|
|
8
|
+
identifier = tag.target_identifier
|
|
9
|
+
target = tag.target_value
|
|
10
|
+
epc = tag.new_epc
|
|
11
|
+
password = tag.password
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
"accessCommands": [
|
|
15
|
+
{
|
|
16
|
+
"identifier": "1",
|
|
17
|
+
"blockWrite": {
|
|
18
|
+
"memoryBank": "epc",
|
|
19
|
+
"wordOffset": 2,
|
|
20
|
+
"dataHex": epc[0:8],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"identifier": "2",
|
|
25
|
+
"blockWrite": {
|
|
26
|
+
"memoryBank": "epc",
|
|
27
|
+
"wordOffset": 4,
|
|
28
|
+
"dataHex": epc[8:16],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"identifier": "3",
|
|
33
|
+
"blockWrite": {
|
|
34
|
+
"memoryBank": "epc",
|
|
35
|
+
"wordOffset": 6,
|
|
36
|
+
"dataHex": epc[16:24],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
"tagAccessPasswordHex": password,
|
|
41
|
+
"tagSelectors": [
|
|
42
|
+
{
|
|
43
|
+
"action": "include",
|
|
44
|
+
"tagMemoryBank": identifier if identifier else "epc",
|
|
45
|
+
"bitOffset": 0 if identifier == "tid" else 32,
|
|
46
|
+
"mask": target if target else "0",
|
|
47
|
+
"maskLength": 1 if identifier is None else 96,
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async def send_write_command(self, write_command):
|
|
53
|
+
if not isinstance(write_command, list):
|
|
54
|
+
write_command = [write_command]
|
|
55
|
+
payload = {"accessConfigurations": write_command}
|
|
56
|
+
try:
|
|
57
|
+
async with httpx.AsyncClient(auth=self.auth, verify=False, timeout=10.0) as session:
|
|
58
|
+
await self.post_to_reader(session, self.endpoint_write, payload=payload)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
logging.warning(f"{self.name} - Failed to Write: {e}")
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from .ble_protocol import BLEProtocol
|
|
7
|
+
from .on_receive import OnReceive
|
|
8
|
+
from .rfid import RfidCommands
|
|
9
|
+
from .serial_protocol import SerialProtocol
|
|
10
|
+
from .tcp_protocol import TCPProtocol
|
|
11
|
+
from .write_commands import WriteCommands
|
|
12
|
+
|
|
13
|
+
from smartx_rfid.utils.event import on_event
|
|
14
|
+
|
|
15
|
+
ant_default_config = {
|
|
16
|
+
"1": {"active": True, "power": 22, "rssi": -120},
|
|
17
|
+
"2": {"active": False, "power": 22, "rssi": -120},
|
|
18
|
+
"3": {"active": False, "power": 22, "rssi": -120},
|
|
19
|
+
"4": {"active": False, "power": 22, "rssi": -120},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class X714(SerialProtocol, OnReceive, RfidCommands, BLEProtocol, WriteCommands, TCPProtocol):
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
name: str = "X714",
|
|
27
|
+
connection_type: str = "SERIAL", # SERIAL, BLE or TCP
|
|
28
|
+
# SERIAL
|
|
29
|
+
port: str = "AUTO", # AUTO or COMx / /dev/ttyX
|
|
30
|
+
baudrate: int = 115200,
|
|
31
|
+
vid: int = 1,
|
|
32
|
+
pid: int = 1,
|
|
33
|
+
# TCP
|
|
34
|
+
ip: str | None = "192.168.1.100",
|
|
35
|
+
tcp_port: int = 23,
|
|
36
|
+
# BLE
|
|
37
|
+
ble_name: str = "SMTX",
|
|
38
|
+
# GENERIC
|
|
39
|
+
buzzer: bool = False,
|
|
40
|
+
session: int = 1, # 0, 1, 2, 3
|
|
41
|
+
start_reading: bool = False,
|
|
42
|
+
gpi_start: bool = False,
|
|
43
|
+
ignore_read: bool = False,
|
|
44
|
+
always_send: bool = True,
|
|
45
|
+
simple_send: bool = False,
|
|
46
|
+
keyboard: bool = False,
|
|
47
|
+
decode_gtin: bool = False,
|
|
48
|
+
hotspot: bool = True,
|
|
49
|
+
reconnection_time: int = 3,
|
|
50
|
+
prefix: str = "",
|
|
51
|
+
protected_inventory_password: str | None = None,
|
|
52
|
+
# Antenna config
|
|
53
|
+
# If ant_dict is provided use it else use the other vars
|
|
54
|
+
ant_dict: dict | None = None,
|
|
55
|
+
active_ant: list[int] | None = [1],
|
|
56
|
+
read_power: int = 22,
|
|
57
|
+
read_rssi: int = -120,
|
|
58
|
+
):
|
|
59
|
+
# Name
|
|
60
|
+
self.name = name
|
|
61
|
+
self.device_type = "rfid"
|
|
62
|
+
|
|
63
|
+
# CONNECTION TYPE
|
|
64
|
+
if connection_type in ["SERIAL", "BLE", "TCP"]:
|
|
65
|
+
self.connection_type = connection_type
|
|
66
|
+
else:
|
|
67
|
+
self.connection_type = "SERIAL"
|
|
68
|
+
logging.warning(f"[{self.name}] Invalid connection_type '{connection_type}' set to 'SERIAL'")
|
|
69
|
+
|
|
70
|
+
# SERIAL CONFIG
|
|
71
|
+
self.port = port
|
|
72
|
+
self.baudrate = baudrate
|
|
73
|
+
self.vid = vid
|
|
74
|
+
self.pid = pid
|
|
75
|
+
self.is_auto = self.port == "AUTO"
|
|
76
|
+
|
|
77
|
+
# TCP CONFIG
|
|
78
|
+
self.ip = ip
|
|
79
|
+
self.tcp_port = tcp_port
|
|
80
|
+
|
|
81
|
+
# BLE CONFIG
|
|
82
|
+
self.ble_name = ble_name
|
|
83
|
+
self.init_ble_vars()
|
|
84
|
+
|
|
85
|
+
# GENERIC CONFIG
|
|
86
|
+
self.buzzer = buzzer
|
|
87
|
+
if session not in [0, 1, 2, 3]:
|
|
88
|
+
session = 1
|
|
89
|
+
logging.warning(f"[{self.name}] Invalid session '{session}' set to '1'")
|
|
90
|
+
self.session = session
|
|
91
|
+
self.start_reading = start_reading
|
|
92
|
+
self.gpi_start = gpi_start
|
|
93
|
+
self.ignore_read = ignore_read
|
|
94
|
+
self.always_send = always_send
|
|
95
|
+
self.simple_send = simple_send
|
|
96
|
+
self.keyboard = keyboard
|
|
97
|
+
self.decode_gtin = decode_gtin
|
|
98
|
+
self.hotspot = hotspot
|
|
99
|
+
self.reconnection_time = reconnection_time
|
|
100
|
+
self.prefix = prefix
|
|
101
|
+
self.protected_inventory_password = protected_inventory_password
|
|
102
|
+
|
|
103
|
+
# ANTENNA CONFIG
|
|
104
|
+
if ant_dict is not None:
|
|
105
|
+
self.ant_dict = ant_dict
|
|
106
|
+
else:
|
|
107
|
+
self.ant_dict = ant_default_config
|
|
108
|
+
for ant in self.ant_dict.keys():
|
|
109
|
+
ant_num = int(ant)
|
|
110
|
+
if active_ant and ant_num in active_ant:
|
|
111
|
+
self.ant_dict[ant]["active"] = True
|
|
112
|
+
else:
|
|
113
|
+
self.ant_dict[ant]["active"] = False
|
|
114
|
+
self.ant_dict[ant]["power"] = read_power
|
|
115
|
+
self.ant_dict[ant]["rssi"] = read_rssi
|
|
116
|
+
|
|
117
|
+
self.transport = None
|
|
118
|
+
self.on_con_lost = None
|
|
119
|
+
self.rx_buffer = bytearray()
|
|
120
|
+
self.last_byte_time = None
|
|
121
|
+
|
|
122
|
+
self.is_connected = False
|
|
123
|
+
self.is_reading = False
|
|
124
|
+
|
|
125
|
+
self.on_event: Callable = on_event
|
|
126
|
+
|
|
127
|
+
def write(self, to_send, verbose=True):
|
|
128
|
+
if self.connection_type == "SERIAL":
|
|
129
|
+
self.write_serial(to_send, verbose)
|
|
130
|
+
elif self.connection_type == "BLE":
|
|
131
|
+
asyncio.create_task(self.write_ble(to_send.encode(), verbose))
|
|
132
|
+
else:
|
|
133
|
+
asyncio.create_task(self.write_tcp(to_send, verbose))
|
|
134
|
+
|
|
135
|
+
async def connect(self):
|
|
136
|
+
if self.connection_type == "SERIAL":
|
|
137
|
+
await self.connect_serial()
|
|
138
|
+
elif self.connection_type == "BLE":
|
|
139
|
+
await self.connect_ble()
|
|
140
|
+
else:
|
|
141
|
+
await self.connect_tcp(self.ip, self.tcp_port)
|
|
142
|
+
|
|
143
|
+
def on_connected(self):
|
|
144
|
+
"""Callback chamado quando a conexão é estabelecida."""
|
|
145
|
+
self.config_reader()
|
|
146
|
+
self.on_event(self.name, "connected", True)
|